Для внедрения системы планирования задач достаточно прочесть это

задняя часть
Для внедрения системы планирования задач достаточно прочесть это

Сообщение пользователя сети при чтении статьи «Выбор временной схемы задач»Электричествомне:

Я видел очень много так называемых туториалов, большинство из них учат "как пользоваться инструментами", немногие учат "как делать инструменты", а тех, кто может научить "имитировать инструменты", очень мало. действительно могут "делать инструменты", и нет недостатка в программистах, которые "используют инструменты"! ......» Последнее, что нужно этой отрасли, это «инженеры, которые могут использовать XX инструменты», но «творческие инженеры-программисты»! Все рабочие места в отрасли по существу обеспечиваются «креативными инженерами-программистами».

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

1 Quartz

Quartz — это среда планирования задач с открытым исходным кодом для Java, а также отправная точка для многих инженеров Java, чтобы связаться с планированием задач.

На следующем рисунке показан общий процесс планирования задач:

Ядро Quartz состоит из трех компонентов.

  • Задача: задание используется для представления запланированной задачи;
  • Триггер: Триггер определяет элемент планирования времени, то есть в соответствии с каким правилом времени выполнять задачу. Задание может быть связано с несколькими триггерами, но триггер может быть связан только с одним заданием;
  • Планировщик: фабричный класс создает планировщик для планирования задач в соответствии с временными правилами, определенными триггерами.

JobStore of Quartz в приведенном выше коде:RAMJobStore, Триггер и Задание сохраняются в памяти.

Основным классом, выполняющим планирование задач, являетсяQuartzSchedulerThread.

  1. Поток планирования получает список запускаемых триггеров из JobStore и изменяет состояние триггеров;
  2. FireТриггер, измените информацию триггера (при следующем выполнении триггера и состоянии триггера) и сохраните ее.
  3. Наконец, создайте конкретный объект задачи выполнения и выполните задачу через пул рабочих потоков.

Далее поговорим о решении Quartz для развертывания кластера.

Решение для развертывания кластера Quartz требует создания таблиц Quartz в экземплярах базы данных для разных типов баз данных (MySQL, ORACLE).JobStoreSupport.

Эта программа распределена, не несет ответственности за централизованное управление узлами, но использовать блокирующий способ блокировки строки базы данных для достижения контроля параллелизма в кластерной среде.

Экземпляр планировщика сначала получает блокировку строки в таблице {0}LOCKS в кластерном режиме, а Mysql получает оператор блокировки строки:

{0} будет заменено конфигурацией файла конфигурации по умолчанию.QRTZ_. sched_name — это имя экземпляра кластера приложений, а lock_name — это имя блокировки на уровне строки. Quartz в основном имеет две блокировки доступа к блокировке на уровне строки (TRIGGER_ACCESS) и государственные блокировки доступа (STATE_ACCESS).

Эта архитектура решает проблему распределенного планирования задач.Только один узел может выполнять одну и ту же задачу, а другие узлы не будут выполнять задачу.При столкновении с большим количеством коротких задач каждый узел часто конкурирует за блокировки базы данных.Чем больше узлов, чем лучше производительность, тем хуже.

2 Режим распределенной блокировки

Кластерный режим Quartz можно масштабировать по горизонтали и планировать распределенным образом, но для этого требуется, чтобы бизнес-стороны добавляли соответствующие таблицы в базу данных, что очень навязчиво.

Чтобы избежать этой навязчивости, многие студенты R&D также исследовалиРежим распределенной блокировки.

Бизнес-сценарий: для проекта электронной коммерции, если пользователь не платит в течение определенного периода времени после размещения заказа, система закроет заказ по истечении времени ожидания.

Обычно мы будем выполнять запланированное задание каждые две минуты, чтобы проверять заказы в течение первых получаса, запрашивать список заказов, которые не были оплачены, а затем восстанавливать запасы товаров в заказе, а затем устанавливать порядок быть недействительным.

Мы используем метод Spring Schedule для выполнения запланированной задачи.

@Scheduled(cron = "0 */2 * * * ? ")
public void doTask() {
   log.info("定时任务启动");
   //执行关闭订单的操作
   orderService.closeExpireUnpayOrders();
   log.info("定时任务结束");
 }

Когда один сервер работает нормально, учитывая высокую доступность и резкое увеличение объема бизнеса, архитектура будет развиваться в кластерном режиме.тот же моментСуществует несколько служб, выполняющих запланированную задачу, что может привести к перебоям в работе.

Решение заключается в использовании распределенных блокировок Redis для решения таких проблем при выполнении задач.

@Scheduled(cron = "0 */2 * * * ? ")
public void doTask() {
    log.info("定时任务启动");
    String lockName = "closeExpireUnpayOrdersLock";
    RedisLock redisLock = redisClient.getLock(lockName);
    //尝试加锁,最多等待3秒,上锁以后5分钟自动解锁
    boolean locked = redisLock.tryLock(3, 300, TimeUnit.SECONDS);
    if(!locked){
        log.info("没有获得分布式锁:{}" , lockName);
        return;
    }
    try{
       //执行关闭订单的操作
       orderService.closeExpireUnpayOrders();
    } finally {
       redisLock.unlock();
    }
    log.info("定时任务结束");
}

Redis обладает отличной производительностью чтения и записи, а распределенные блокировки более легкие, чем блокировки уровня строки базы данных Quartz. Конечно, блокировки Redis также можно заменить блокировками Zookeeper, что является тем же механизмом.

В небольших проектах хорошие результаты дает использование временной структуры задач (Quartz/Spring Schedule) и распределенных блокировок (redis/zookeeper).

но? Мы можем найти две проблемы с этой комбинацией:

  1. Запланированные задачи могут свободно выполняться в распределенных сценариях, и задачи не могут быть фрагментированы;
  2. Чтобы запустить задачу вручную, необходимо добавить дополнительный код для ее завершения.

3 Платформа ElasticJob-Lite

ElasticJob-Lite позиционируется как легкое децентрализованное решение, предоставляющее услуги координации распределенных задач в виде jar-файлов.官网架构图

Приложение определяет класс задачи, реализует интерфейс SimpleJob и записывает фактический бизнес-процесс своей задачи.

public class MyElasticJob implements SimpleJob {
    @Override
    public void execute(ShardingContext context) {
        switch (context.getShardingItem()) {
            case 0:
                // do something by sharding item 0
                break;
            case 1:
                // do something by sharding item 1
                break;
            case 2:
                // do something by sharding item 2
                break;
            // case n: ...
        }
    }
}

Пример: Приложение A имеет пять задач, которые необходимо выполнить, а именно A, B, C, D и E. Задачу E нужно разделить на четыре подзадачи, а приложение развернуть на двух машинах.

После запуска приложения A пять задач распределяются на две машины после координации Zookeeper, а разные задачи выполняются отдельно через Quartz Scheduler.

По сути, базовое планирование задач ElasticJob по-прежнему осуществляется через Quartz.По сравнению с распределенной блокировкой Redis или распределенным развертыванием Quartz, его преимущество заключается в том, что он может полагаться на Zookeeper, крупного убийцу, для назначения задач планировщику Quartz в приложении через алгоритм балансировки нагрузки контейнер.

С точки зрения пользователя, он очень прост в использовании. Однако с архитектурной точки зрения планировщик и исполнитель по-прежнему находятся в одной и той же JVM приложения, и после запуска контейнера по-прежнему требуется балансировка нагрузки. Если приложение часто перезапускается, постоянно выбирает мастер и выполняет балансировку нагрузки на осколки, это относительные сравнения.Тяжелыйоперация.

Кроме того, консоль ElasticJob довольно грубая: она отображает статус задания, читая данные реестра, и обновляет данные реестра, чтобы изменить глобальную конфигурацию задачи.

4 централизованных жанра

Принцип централизации заключается в разделении планирования и выполнения задач на две части: центр планирования и исполнитель. Модуль центра планирования должен отвечать только за атрибуты планирования задач и запускать команды планирования. Исполнитель получает команды планирования для выполнения определенной бизнес-логики, и оба могут выполнять распределенное расширение.

4.1 Режим MQ

Давайте поговорим о первой централизованной архитектуре, с которой я столкнулся в команде продвижения eLong.

Центр планирования использует режим кластера Quartz для отправки сообщений в RabbitMQ при планировании задач. После того как бизнес-приложение получает сообщение о задаче, оно использует информацию о задаче.

В этой модели в полной мере используются характеристики разделения MQ: диспетчерский центр отправляет задачи, а сторона приложения выступает в качестве исполнителя для получения и выполнения задач.

Однако эта схема сильно зависит от очередей сообщений, масштабируемости и функциональности, а загрузка системы сильно связана с очередями сообщений. Этот архитектурный проект требует, чтобы архитектор был хорошо знаком с очередями сообщений.

4.2 XXL-JOB

XXL-JOB — это распределенная платформа планирования задач, основными целями которой являются быстрая разработка, простота обучения, легкий вес и легкое расширение. Теперь с открытым исходным кодом и подключен к линейкам онлайн-продуктов многих компаний, из коробки.

xxl-job 2.3.0架构图

Сосредоточимся на анализе схемы архитектуры:

▍ Модель сетевой связи сервер-рабочий

Связь между диспетчерским центром и исполнителем осуществляется в режиме server-worker. Сам диспетчерский центр является проектом SpringBoot, и при запуске он будет прослушивать порт 8080.

После запуска исполнителя встроенная служба ( EmbedServer ) будет запущена для прослушивания порта 9994. Таким образом, обе стороны могут отправлять команды друг другу.

Как диспетчерский центр узнает адресную информацию привода? На приведенном выше рисунке исполнитель будет периодически отправлять команду регистрации, чтобы диспетчерский центр мог получить список онлайн-исполнителей.

В списке исполнителей можно выбрать узел для выполнения задачи в соответствии с политикой маршрутизации, настроенной задачей. Существует три общих стратегии маршрутизации:

  • Произвольное выполнение узла: выберите доступный узел выполнения в кластере для выполнения запланированной задачи. Применимые сценарии: Расчет офлайн-заказа.

  • Широковещательное выполнение: Распространяйте и выполняйте запланированные задачи на всех исполнительных узлах в кластере. Применимые сценарии: Пакетное обновление локального кеша приложения.

  • Выполнение сегментирования: разделение в соответствии с определяемой пользователем логикой сегментирования и распределение по разным узлам в кластере для параллельного выполнения для повышения эффективности использования ресурсов. Применимые сценарии: массовая регистрация статистики.

▍ Планировщик

Планировщик является очень важным компонентом в системе планирования задач. Более ранние версии XXL-JOB зависели от Quartz.

Однако в версии v2.1.0 зависимость от Quartz полностью удалена, а исходная таблица Quartz, которую необходимо создать, также заменена самостоятельно разработанной таблицей.

Основные классы планирования:JobTriggerPoolHelper. После вызова метода start запускаются два потока: scheduleThread и ringThread.

Во-первых, scheduleThread будет периодически загружать запланированные задачи из базы данных, что по существу основано на блокировках строк базы данных, чтобы гарантировать, что только один узел центра планирования запускает планирование задач одновременно.

Connection conn = XxlJobAdminConfig.getAdminConfig()
                  .getDataSource().getConnection();
connAutoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
preparedStatement = conn.prepareStatement(
"select * from xxl_job_lock where lock_name = 'schedule_lock' for update");
preparedStatement.execute();
# 触发任务调度 (伪代码)
for (XxlJobInfo jobInfo: scheduleList) {
  // 省略代码
}
# 事务提交
conn.commit();

Поток планирования будет выполнять различные действия в зависимости от «следующего времени запуска» задачи:

Задачи с истекшим сроком действия, которые необходимо выполнить немедленно, помещаются непосредственно в пул потоков для запуска выполнения, а задачи, которые необходимо выполнить в течение пяти секунд, помещаются в объект ringData.

После запуска ringThread периодически получает список задач, подлежащих выполнению, из объекта ringData и помещает его в пул потоков для запуска выполнения.

5 Самоисследование на плечах гигантов

В 2018 году у меня был опыт самостоятельной разработки системы планирования задач.

Предыстория: он совместим с инфраструктурой RPC, разработанной технической группой. Технической группе не нужно изменять код. Метод аннотации RPC может быть размещен в системе планирования задач и выполняться непосредственно как задача.

В процессе самоисследования я изучил исходный код XXL-JOB, а заодно почерпнул много полезного из распределенного планировщика задач Alibaba Cloud SchedulerX.

SchedulerX 1.0 架构图

  • Schedulerx-console — это консоль планирования задач, используемая для создания и управления запланированными задачами. Отвечает за создание, изменение и запрос данных. Взаимодействие с schedulerx-сервером внутри продукта.
  • Schedulerx-server — это сервер планирования задач и основной компонент планировщика. Отвечает за планирование и запуск клиентских задач, а также за мониторинг состояния выполнения задач.
  • Schedulerx-client — это клиент для планирования задач. Каждый процесс приложения, который обращается к клиенту, является рабочим. Worker отвечает за установление связи с schedulerx-server, что позволяет schedulerx-server обнаружить клиентскую машину. И зарегистрируйте группу текущего приложения с помощью schedulerx-server, чтобы schedulerx-server мог периодически запускать задачи для клиента.

Мы сымитировали модуль SchedulerX, и архитектурный проект выглядит следующим образом:

Я выбрал коммуникационный модуль удаленного взаимодействия исходного кода RocketMQ в качестве коммуникационной структуры саморазрабатываемой системы планирования. На основании следующих двух моментов:

  1. Я не знаком со знаменитым в отрасли Dubbo, и я уже сделал несколько колес для удаленного доступа, и я верю, что справлюсь;

  2. При чтении исходного кода клиента SchedulerX 1.0 было обнаружено, что коммуникационная структура SchedulerX во многих местах похожа на RocketMQ Remoting. В его исходниках есть готовые реализации проектов, что является настоящим сокровищем.

Я удалил код службы имен из модуля удаленного взаимодействия RocketMQ и сделал определенную настройку.

При удаленном взаимодействии RocketMQ сервер принимает режим процессора.

Диспетчерскому центру необходимо зарегистрировать два обработчика: обработчик результатов обратного вызова CallBackProcessor и обработчик пульса HeartBeatProcessor. Исполнителю необходимо зарегистрировать триггерный обработчик задач TriggerTaskProcessor.

public void registerProcessor(
             int requestCode,
             NettyRequestProcessor processor,
             ExecutorService executor);

Интерфейс процессора:

public interface NettyRequestProcessor {
 RemotingCommand processRequest(
                 ChannelHandlerContext ctx,
                 RemotingCommand request) throws Exception;
 boolean rejectRequest();
}

Для коммуникационной структуры мне не нужно обращать внимание на детали связи, просто реализовать интерфейс процессора.

Пример: TriggertaskProcessor: запуск обработчика задач:

После получения сети связи, как разработать планировщик? В конце концов я выбрал Кварцевый кластерный режим. В основном по следующим причинам:

  1. В случае небольшого объема планирования режим кластера Quartz достаточно стабилен и совместим с исходными задачами XXL-JOB;
  2. Если вы пользуетесь колесом времени, у вас недостаточно практического опыта, и вас беспокоят проблемы. Кроме того, для запуска задач через различные службы планирования (сервер расписания) требуется координатор. Так что я подумал о Zookeeper. Но в этом случае вводится новый компонент.
  3. Цикл исследований и разработок не может быть слишком длинным, и мы хотим получить результаты как можно скорее.

Самостоятельно разработанному сервису планирования потребовалось полтора месяца, чтобы выйти в онлайн. Система работает очень стабильно, а доступ команды R&D также очень плавный. Объем отправки невелик, общий объем отправки составляет от 40 до 50 миллионов за четыре месяца.

Откровенно говоря, узкое место самодельной версии часто возникает у меня в голове. С большим объемом данных я могу работать с подбазой данных и подтаблицей, но кластер Quartz основан на режиме блокировки на уровне строк, и верхний предел не должен быть слишком высоким.

Чтобы облегчить путаницу в моем сердце, я написал демо-версию колеса, чтобы проверить, работает ли оно:

  1. Удалить внешний центр регистрации, служба расписания (SCHEDULE-Server) управления сессией;
  2. Представить zookeeper для координации услуг планирования через zk. Но механизм HA очень груб, эквивалентен одной работающей службе планирования задач, другой резервной службе;
  3. Кварц заменен колесом времени (см. исходный код колеса времени в Dubbo).

Эта демо-версия может работать в среде разработки, но есть много деталей, которые нужно оптимизировать, это всего лишь игрушка, и у нее нет шансов запуститься в производственной среде.

Недавно я прочитал статью Alibaba Cloud «Как реализовать миллионы предупреждений о правилах с помощью планирования задач». Архитектура высокой доступности SchedulerX2.0 показана на следующем рисунке:

В статье упоминаются:

Каждое приложение будет делать три бэкапа, захватывать блокировки через zk, один мастер и два бэкапа.Если сервер зависнет, он переключится, а другие серверы возьмут на себя задачи по планированию.

Самостоятельно разработанная система планирования задач несложна с точки зрения архитектуры, реализует основные функции XXL-JOB и совместима с RPC-фреймворком технической группы, но не реализует workflow и mapreduce sharding.

SchedulerX основан на новой архитектуре Akka после обновления до версии 2.0.так называемыйВнедрите высокопроизводительный механизм рабочего процесса, реализуйте взаимодействие между процессами и сократите код сетевого взаимодействия.

В системе планирования задач с открытым исходным кодом, которую я исследовал,PowerJobОн также основан на архитектуре Akka, а также реализует режимы выполнения workflow и MapReduce.

я правPowerJobМне очень интересно, и я также буду выводить соответствующие статьи после обучения и практики, так что следите за обновлениями.

6 Технический отбор

Во-первых, мы объединяем продукт с открытым исходным кодом для планирования задач и коммерческий продукт SchedulerX, чтобы создать сравнительную таблицу:

Quartz и ElasticJob по сути являются фреймворками.

Централизованные продукты более ясны с точки зрения архитектуры, более гибки в планировании и могут поддерживать более сложное планирование (динамическое сегментирование mapreduce, рабочий процесс).

XXL-JOB изначально был минималистичным на уровне продукта, а режим планирования может удовлетворить потребности большинства команд, занимающихся исследованиями и разработками. Прост в использовании + играбелен, поэтому он очень популярен у всех.

На самом деле у каждой технической команды разные технические резервы и разные сценарии, поэтому технический отбор нельзя обобщать.

Независимо от того, какие технологии используются, вам все еще нужно обратить внимание на две точки при написании задач бизнес-кода:

  • Идемпотент. Когда задача выполняется повторно или когда распределенная блокировка не работает, программа все еще может выводить правильный результат;
  • Миссия не запущена, не паникуйте. Просмотрите журнал планирования, используйте команду Jstack для просмотра стека на уровне JVM и добавьте тайм-аут для сетевого взаимодействия, что обычно может решить большинство проблем.

7 до конца

2015 год был действительно очень интересным. Два разных жанра проектов планирования задач, ElasticJob и XXL-JOB, имеют открытый исходный код.

В исходном коде XXL-JOB все еще есть динамический снимок экрана г-на Сюй Сюэли в открытом исходном коде Китая:

Только что написанная структура планирования задач, задачи динамического веб-управления вступают в силу в режиме реального времени и вызывают энтузиазм. Без происшествий запушите его на git.osc завтра в полдень. Ха-ха, иди вниз и перемешай, чтобы добавить яйцо лотоса, чтобы отпраздновать.

Глядя на этот снимок экрана, глубоко в моем сердце возникает какое-то сочувствие, и уголки моего рта не могут не подняться.

Я снова вспомнил: в 2016 году Чжан Лян, автор ElasticJob, открыл исходный код sharding-jdbc. Я создал частный проект на github, обратился к исходному коду sharding-jdbc и самостоятельно реализовал функцию подбиблиотеки и подтаблицы. Первый класс называется ShardingDataSource, а время установлено на 2016/3/29.

Я не знаю, как определить «креативного инженера-программиста», но я считаю: инженер, который любопытен, трудолюбив, готов делиться и помогать другим, определенно не является плохой приметой.


Если вы считаете, что это было полезно для вас, пожалуйста, ставьте автору "Нравится" и "Избранное", увидимся в следующем выпуске.