Spring предоставляет удобный namespace task, который содержит все необходимое для создания Executor/Scheduler бинов. В моей конфигурации максимальное кол-во потоков, проводящих опрос базы данных - пять. <task:annotation-driven> элемент позволяет использовать аннотировнные компоненты для запуска задач, а также указывает executor/scheduler которые будут использоваться по умолчанию для запуска аннотированных методов.
Как видно из примера выше, для создания задачи достаточно аннотировать необходимый метод аннотацией @Scheduled, которой необходимо указать один из трех параметров:
1. fixedDelay - период между завершением предыдущего запуска и началом следущего запуска задачи в миллисекундах
2. fixedRate - период между каждым запуском задачи в миллисекундах (считается от старта предыдущей задачи)
3. cron - указывает cron подобное выражение, в котором можно более точно указать периоды запуска задачи
Хочу обратить внимание, что fixedDelay следует использовать в тех случаях, когда время выполнения задачи может превышать период между выполнениями задач. Для остальных случаев вполне подойдет fixedRate.
После старта контекста Spring автоматически настроит необходимые Executor бины и будет выполнять метод pollDatabase раз в секунду (ну или чуть реже, учитывая время на выполнение самого метода).н
Правда, во время эксплуатации этого кода, возникла одна неприятная проблема, которая в общем-то не влияла на работоспособность системы, но оставляла мусор в логах. Дело в том, что выключая контейнер (Tomcat в нашем случае), Spring закрывает контекст и разрушает все бины, находящиеся в нем. При этом выполняемые запланированные задачи иногда разрушались после того как уже были разрушены сервисные бины; задачи пытались использовать разрушенные сервисные бины, вызывая различные странные исключения.
Правда, во время эксплуатации этого кода, возникла одна неприятная проблема, которая в общем-то не влияла на работоспособность системы, но оставляла мусор в логах. Дело в том, что выключая контейнер (Tomcat в нашем случае), Spring закрывает контекст и разрушает все бины, находящиеся в нем. При этом выполняемые запланированные задачи иногда разрушались после того как уже были разрушены сервисные бины; задачи пытались использовать разрушенные сервисные бины, вызывая различные странные исключения.
Таким образом, чтобы навести порядок в логах и очистить совесть, решили исправить ситуацию. Для того, чтобы корректно разрушить нашу компоненту с задачами, нам необходимо вклиниться в процесс разрушения контекста и закрыть выполняемые задачи до того, как разрушатся остальные бины. Для решения этой проблемы Spring предоставляет специальный интерфейс ApplicationListener, позволяющий реагировать на различные сообщения, связанные с контекстом.
Мы включили в нашу компоненту две новых зависимости (databasePollExecutor, databasePollScheduler), которые дали нам доступ к executor/scheduler, обьявленных в XML контексте. Также мы добавили в нее реализацию интерфейса, которая уничтожает executor/scheduler и связанные с ними задачи. Метод onApplicationEvent будет вызван Spring-ом до начала разрушения остальных бинов и контекста в целом, а это именно то что нам нужно.
Не подскажите, есть ли в спринге возможность, не используя других средств, создать скедьюлер наподобие кварцовского clustered скедьюлера? Чтобы одинковое приложение, задеплоенное на разные ноды не выполняло одну и туже работу по несколько раз.
ReplyDeleteНасколько я понимаю Вам нужен SchedulerFactoryBean, он может работать с Quartz кластером. Судя по тому что пишут сам Quartz поддерживает для кластера только хранение jobs в некоем JDBC-хранилище из которого мы потом собственно их и забираем нашей фактори. В инете много примеров. Может я не понял вопрос?
ReplyDeleteСпасибо за ответ, Алексей.
ReplyDeleteЕсть некое приложение, задеплоенное на несколько инстансов apache tomcat, находящихся за load balancer. Приложение умеет выполнять некие джобы по скедьюлеру. Допустим они тянут откуда-то некие данные и кладут их в хранилище. Задача состояла в том, чтобы при выполнении задачи на одном инстансе, запретить ее выполнение на других. Quartz умеет решать данную проблему, синхронизируя выполнение задач через DB.
Мы сначала сделали все на спринге, как и у вас, но когда наступило время переезжать с одного инстанса на неколько нам пришлось все переделать на quartz.
Так вот, вопрос был в том, умеет ли такое spring без quartz. Но, насколько я понял, перед вами такая задача пока что не стояла.
В любом случае, спасибо.
Не умеет, Вы все правильно сделали. Spring предлагает готовые решения для работы с Quartz Jobs, и если нужна кластеризация, то используйте SchedulerFactoryBean и встроенные средства самого Quartz
ReplyDeleteВы правы не сталкивался. По поводу Spring - не встречал описание такого его поведения. Можно написать разработчикам :)
ReplyDeleteИ все такие не понял вопроса сначала :)
ReplyDeleteЖеня и Алексей, спасибо за ответы.
ReplyDeleteНадеюсь это будет следующим этапом развития спринга. Так как, если откинуть кластеризацию, простой скедьюлер на спринге создать намного проще, чем с помощью кварца. Хотелось бы такой простоты во всем :)