Случайная задержка аннотации Spring @Scheduled

Я использую аннотацию @Scheduled из среды Spring для вызова метода. Но у меня есть несколько узлов в моей установке, и я не хочу, чтобы все они работали в одно и то же время. Поэтому я хотел бы установить случайное значение начальной задержки, чтобы сместить их друг от друга.

import org.springframework.scheduling.annotation.Scheduled;

@Scheduled(fixedRate = 600000, initialDelay = <random number between 0 and 10 minutes> )

К сожалению, здесь мне разрешено использовать только константное выражение. Есть ли другой способ обойти это? Я думал об использовании языка выражений Spring.


person masstroy    schedule 09.01.2015    source источник


Ответы (7)


Вы можете настроить initialDelay через Spring Expression Language:

@Scheduled(fixedRate = 600000, initialDelayString = "#{ T(java.util.concurrent.ThreadLocalRandom).current().nextInt(10*60*1000) }" )

У меня сейчас нет IDE для тестирования этого кода, так что вам, возможно, придется его немного адаптировать.

person sk_    schedule 10.01.2015
comment
Разве команды SpEl не должны быть строками? Я видел их только в строковых значениях. Это, к сожалению, ожидает длинное значение. Я попробовал это без кавычек и получил нерешенные проблемы с компиляцией. Я попробовал это в виде строки, и планировщик никогда не вызывался, но не было сообщений об ошибках. - person masstroy; 10.01.2015
comment
Вы заметили использование initialDelayString вместо initialDelay? Последний принимает тип long, первый — строку, которая может быть выражением. - person M. Deinum; 10.01.2015
comment
Пример не совсем рабочий. Во-первых, он должен быть заключен в кавычки, иначе компилятор будет жаловаться. Но даже тогда все, что я получаю, равно нулю. Есть ли какая-то конфигурация, которую мне нужно сделать? - person girgen; 08.06.2015
comment
Подтверждено несколькими версиями Spring, что это не работает. - person anupash; 02.08.2018

Чтобы сделать начальную задержку случайным образом где-то между 0 и fixedRate, попробуйте следующее:

@Scheduled(fixedDelayString = "${some.delay}", initialDelayString = "${random.int(${some.delay})}")

Где вы определяете some.delay (но выбираете более подходящее имя) как 10 минут как свойство, подобное этому, в вашем application.properties или эквивалентном.

some.delay = 600000

Конечно, если вы хотите быть ленивым и жестко программировать, вы всегда можете просто использовать ${random.int(600000)}

person mekazu    schedule 12.07.2016
comment
Просто хотел добавить этот альтернативный ответ, так как «правильный» ответ не очень полезен, и все же эта страница высоко в результатах поиска. - person mekazu; 12.07.2016
comment
Подтверждено несколькими версиями Spring, что это не работает. - person anupash; 02.08.2018
comment
Я использовал именно это решение в нескольких проектах, и оно отлично сработало. Не пробовал с самыми последними версиями Spring. - person Samuel Lindblom; 28.07.2021

В этом рабочем примере случайная задержка будет составлять от 5 до 10 секунд.

@Scheduled(fixedDelayString = "#{new Double((T(java.lang.Math).random() + 1) * 5000).intValue()}")
person yglodt    schedule 23.01.2017

Имейте в виду, что initialDelayString оценивается только один раз при запуске, и затем эти же значения используются всякий раз, когда задание запланировано.

См. org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor#processScheduled

person Stefan Isele - prefabware.com    schedule 25.07.2016
comment
Знаете ли вы способ, чтобы планировщик имел не только постоянное случайное значение, оцениваемое при запуске, но и случайное значение между каждым запуском? - person yglodt; 24.01.2017

Это должно работать

@Scheduled(fixedRate = 600000, initialDelay = "#{new java.util.Random().nextInt(700)}")

Проверил это с помощью этого простого теста:

    @Test
    public void testSpEL() {
       ExpressionParser parser = new SpelExpressionParser();
       Expression exp = parser.parseExpression("new java.util.Random().nextInt(500)");
       Integer value =(Integer) exp.getValue();
       Assertions.assertThat(value).isNotNull();
}
person slorinc    schedule 06.11.2020

В котлине это работает:

@Component
class MyJob {
   companion object {
      const val INTERVAL = 24*3600*1000L // once a day
   }

   @Scheduled(fixedRate = INTERVAL, initialDelayString = "\${random.long($INTERVAL)}")
   fun doDaily() {
      ...
   }
}
person Milo van der Zee    schedule 28.08.2019

Или вы можете просто добавить Thread.sleep(...) в конце вашей функции.

@Scheduled(fixedDelay = 10000)
public void replicateData() {

    ... do stuff ...

    try {
        Thread.sleep(RandomUtils.nextLong(1000, 10000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
person Vincnetas    schedule 19.05.2017
comment
Это должна быть случайная задержка в начале функции. И должен быть код, чтобы сделать эту задержку только в первый раз. Это может работать, но, вероятно, немного неуклюже - person masstroy; 19.05.2017