Расшифровка принципа кварца

Java

Author: Dorae
Дата: 17 июля 2018 г., 15:55:02
Пожалуйста, укажите источник

1. Обзор кварца

Quartz — это инфраструктура планирования задач с открытым исходным кодом, реализованная в java, которую можно использовать для создания простого или сложного планирования задач и которая может предоставлять множество функций уровня предприятия, таких как JTA, кластеризация и т. д. Сегодня это популярная инфраструктура планирования задач JAVA. .

1. Для чего можно использовать

Quartz — это система планирования задач при возникновении следующих проблем:

  • Я хочу автоматически погашать 25 числа каждого месяца;
  • Я хочу отправлять анонимные поздравительные открытки богине, в которую я был влюблен, 1 апреля каждого года;
  • Я хочу каждый час резервную копию ваших вещей.

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

2. Особенности

  • Мощные функции планирования, такие как различные методы планирования, могут удовлетворить различные обычные и специальные потребности;
  • Гибкие методы применения, такие как поддержка планирования задач и различных комбинаций задач, а также поддержка различных хранилищ данных (БД, ОЗУ и т. д.);
  • Распределенный кластер поддержки, после приобретения Terracotta, он был дополнительно преобразован на исходной основе.

Во-вторых, основной принцип кварца

Core элемент

Основные элементы Quartz имеют Scheduler, Trigger, Job, JobDetail, в которых задание триггера, метаданные jobDetail и фактический планировщик выполняет диспетчер планирования.

  • Trigger

Триггер используется для определения правил времени для планирования задач.В Quartz существует четыре основных типа триггеров: SimpleTrigger, CronTrigger, DataIntervalTrigger и NthIncludedTrigger.

  • Job&Jodetail

Quartz делит задачу на две части: Job и JobDetail.Job используется для определения логики выполнения задачи, а JobDetail используется для описания определения задания (например, класса реализации интерфейса задания и другой связанной статической информации). ). Для Quartz существует в основном два типа заданий: StateLessJob, StateFulJob.

  • Scheduler

Контроллер, который фактически выполняет логику планирования, Quartz предоставляет фабричные классы, такие как DirectSchedulerFactory и StdSchedulerFactory, для поддержки создания объектов, связанных с планировщиком.

2. Отношения между основными элементами

Изображение 1-1

3. Основная нить

В Quartz существует два типа потоков: потоки выполнения и потоки планирования.Потоки, выполняющие задачи, обычно поддерживаются пулом потоков. Отношения между потоками показаны на рис. 1-2.

Рисунок 1-2

Рисунок 1-2

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

4. Хранение данных

Триггеры и задания в Quartz необходимо сохранить, прежде чем их можно будет использовать. В Quartz есть два метода хранения: RAMJobStore и JobStoreSupport, где RAMJobStore хранит триггеры и задания в памяти, а JobStoreSupport хранит триггеры и задания в базе данных на основе jdbc. Скорость доступа к RAMJobStore очень высока, но поскольку все данные будут потеряны после остановки системы, в кластерных приложениях необходимо использовать JobStoreSupport. Структура таблицы показана в таблице 1-1.

Таблица 1-1
Table name Description
QRTZ_CALENDARS Сохранение информации о кварцевом календаре
QRTZ_CRON_TRIGGERS Хранит CronTrigger, включая выражения Cron и информацию о часовом поясе.
QRTZ_FIRED_TRIGGERS Хранить информацию о состоянии, связанную с инициированным ТРИГГЕРОМ, а также выполнением задания.
QRTZ_PAUSED_TRIGGER_GRPS Магазины информации о приостановленной группе триггера
QRTZ_SCHEDULER_STATE Храните небольшой объем информации о состоянии планировщика и других экземпляров планировщика.
QRTZ_LOCKS Информация о пессимистической блокировке хранимой процедуры
QRTZ_JOB_DETAILS Хранит подробную информацию о каждом настроенном задании.
QRTZ_SIMPLE_TRIGGERS Храните простые триггеры, включая повторения, интервалы и количество касаний
QRTZ_BLOG_TRIGGERS Триггер хранится как тип Blob
QRTZ_TRIGGERS Хранит информацию о настроенном триггере
QRTZ_SIMPROP_TRIGGERS

Три кварцевые кластерные принцип

Каждый узел в кластере Quartz является независимым приложением Quartz, которое управляет другими узлами. Это означает, что вы должны запускать или останавливать каждый узел отдельно. В кластере Quartz независимый узел Quartz не взаимодействует с другим узлом или узлом управления, но воспринимает другое приложение Quartz через ту же таблицу базы данных, как показано на рис. 1-3.

Рисунок 1-3

В-четвертых, основной процесс кварца

1. Запустите процесс

Если кварц настроен весной, при запуске сервера будут загружены соответствующие бины. SchedulerFactoryBean реализует интерфейс InitializingBean, поэтому при инициализации bean-компонента будет выполнен метод afterPropertiesSet, который вызовет SchedulerFactory (DirectSchedulerFactory или StdSchedulerFactory, обычно StdSchedulerFactory) для создания планировщика. В процессе создания кварцаScheduler SchedulerFactory будет считывать параметры конфигурации и инициализировать каждый компонент.Ключевыми компонентами являются следующие:

  1. ThreadPoolВообще, SimpleThreadPool используется. SimpleThreadPool создает определенное количество экземпляров рабочей силы, чтобы включить работу для обработки в потоках. Workthread - это внутренний класс, определенный в классе SimpleThreadPool, который по существу является нитью. Есть три списка в SimpleThreadpool: рабочие - хранить все нить ссылки в бассейне, доступники - хранить все холостые нити, BusyWorkers - хранить все рабочие темы; Параметры конфигурации пула резьбы следующие:

    org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=3 org.quartz.threadPool.threadPriority=5

  2. JobStore: Разделенный и сохраненный в памяти RAMJobStore JobStoreSupport хранится в базе данных (включая две реализации JobStoreTX и JobStoreCMT, JobStoreCMT зависит от контейнера для управления транзакциями и управления своими делами JobStoreTX), чтобы использовать кластер для использования JobStoreSupport Way ;

  3. QuartzSchedulerThread: Поток, используемый для планирования задач, paused=true, halted=false во время инициализации, хотя поток начинает выполняться, но paused=true, поток будет ждать, пока метод запуска не установит paused в false;

Далее, SchedulerFactoryBean также реализовал интерфейс SmartLifeCycle, после завершения инициализации запустится выполнение () метода, который выполняет основные следующие несколько действий:

  1. Создайте поток ClusterManager и запустите поток: этот поток используется для обнаружения и обработки сбоев кластера, которые будут подробно рассмотрены ниже;
  2. Создайте поток MisfireHandler и запустите поток: Этот поток используется для обработки задач осечки, которые будут подробно обсуждаться ниже;
  3. Установите QuartzSchedulerThread paused=false, поток планирования действительно начнет планирование;

Весь процесс запуска Quartz показан на рис. 1-4.

Рисунок 1-4

2. Поток QuartzSchedulerThread

Поток QuartzSchedulerThread — это поток, который фактически выполняет планирование задач, и основной код выглядит следующим образом.

while (!halted.get()) {
	int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
	triggers = qsRsrcs.getJobStore().acquireNextTriggers(now + idleWaitTime,
			Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
	long triggerTime = triggers.get(0).getNextFireTime().getTime();
	long timeUntilTrigger = triggerTime - now;
	while (timeUntilTrigger > 2) {
		now = System.currentTimeMillis();
		timeUntilTrigger = triggerTime - now;
	}
	List<TriggerFiredResult> bndle = qsRsrcs.getJobStore().triggersFired(triggers);
	for (int i = 0; i < res.size(); i++) {
		JobRunShell shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
		shell.initialize(qs);
		qsRsrcs.getThreadPool().runInThread(shell);
	}
}
  1. Сначала получите количество доступных потоков в пуле потоков (если доступного потока нет, он будет заблокирован до тех пор, пока не появится доступный поток);
  2. Получите триггеры, которые будут выполняться в пределах 30 м (т.е. приобретитеNextTriggers): Получите блокировку триггера, выбрав ... для обновления; получите триггеры для выполнения в пределах 30 м (настраивается) (время узлов кластера должно быть согласовано), если @ConcurrentExectionDisallowed и триггер существует в списке, пропустите его, иначе обновить триггер Статус ACQUIRED (WAITING в начале); вставлена ​​таблица firedTrigger, и статус ACQUIRED; (Примечание: в RAMJobStore есть timeTriggers, и метод сортировки устроен по времени триггера nextFireTime ; когда JobStoreSupport извлекает триггеры из базы данных, они сортируются в соответствии с nextFireTime);
  3. Подождите, пока триггер, который первым выполняется в полученном триггере, не будет в пределах 2 мс;
  4. триггерыFired:
    1. Обновите статус = ВЫПОЛНЕНИЕ триггера firedTrigger;
    2. Обновить время следующего триггера триггера;
    3. Обновить состояние триггера: триггер без состояния->ОЖИДАНИЕ, триггер с состоянием->БЛОКИРОВАН, если nextFireTime==null ->COMPLETE;
    4. зафиксировать соединение, снять блокировку;
  5. Для каждого запускаемого триггера создайте JobRunShell и поместите его в пул потоков для выполнения:
    1. выполнить: выполнить задание
    2. Получить блокировку TRIGGER_ACCESS
    3. Если задание с отслеживанием состояния: обновить статус триггера: BLOCKED-> WAITING, PAUSED_BLOCKED-> BLOCKED
    4. Если @PersistJobDataAfterExecution, то updateJobData
    5. удалить firedTrigger
    6. зафиксировать соединение, снять блокировку

Поток выполнения потока планирования показан на рисунке 1-5.

Рисунок 1-5

На рис. 1-6 показаны изменения состояния триггера в процессе планирования.

Рисунок 1-6.

3. Поток MisfireHandler

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

  1. Система была перезагружена по какой-то причине. В период между выключением системы и перезапуском некоторые задачи могут не запускаться;
  2. В период, когда триггер приостановлен, некоторые задачи могут не запускаться;
  3. Все потоки в пуле потоков заняты, поэтому задача не может быть запущена и выполнена, что приводит к осечке;
  4. Когда задача с отслеживанием состояния поступает в следующее время триггера, последнее выполнение еще не закончилось; для обработки заданий с ошибкой Quartz определяет стратегии обработки для триггеров, которые в основном включают следующие две:
    • MISFIRE_INSTRUCTION_FIRE_ONCE_NOW: выполнить один раз для задания с ошибкой;
    • MISFIRE_INSTRUCTION_DO_NOTHING: Игнорировать ошибочное задание и ждать следующего триггера; по умолчанию MISFIRE_INSTRUCTION_SMART_POLICY, политика в CronTrigger =MISFIRE_INSTRUCTION_FIRE_ONCE_NOW Поток выполняется раз в 1 минуту по умолчанию; в транзакции восстановление по умолчанию до 20 за раз;

Процесс реализации:

  1. Если сконфигурировано (значение по умолчанию — true, настраивается) для проверки наличия триггера, требующего восстановления, перед получением блокировки, сначала необходимо получить misfireCount;
  2. Получить блокировку TRIGGER_ACCESS;
  3. hasMisfiredTriggersInState: Получить триггеры с пропуском срабатывания. По умолчанию в транзакции может быть не более 20 триггеров с пропуском срабатывания (настраивается).
  4. notifyTriggerListenersMisfired
  5. updateAfterMisfire: получить политику пропусков зажигания (по умолчанию MISFIRE_INSTRUCTION_SMART_POLICY, которая находится в CronTrigger =MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) и обновить nextFireTime в соответствии с политикой;
  6. Обновление NEXTFIRETIME ETC для триггера таблицы;
  7. Зафиксировать соединение, снять блокировку. 8. Если осечек больше, перейти в спящий режим на короткое время (для балансировки нагрузки кластера), в противном случае — перейти в спящий режим на заданное время, а затем продолжить опрос;

Поток выполнения потока misfireHandler показан на рисунке 1-7:

Рисунок 1-7

4. Поток управления кластером ClusterManager

инициализация:

failedInstance=failed+self+firedTrigger Таблица schedulerName не найдена в таблице scheduler_state (сирота)

Выполнение потока:

Каждый сервер будет регулярно обновлять LAST_CHECKIN_TIME таблицы SCHEDULER_STATE (org.quartz.jobStore.clusterCheckinInterval), если это поле намного превышает время обновления, экземпляр сервера считается зависшим;

Примечание. Каждый экземпляр сервера имеет уникальный идентификатор, если он настроен как AUTO, это имя хоста + текущее_время.

Конкретный процесс выполнения потока:

  1. Проверьте, есть ли экземпляры, у которых истекло время ожидания failedInstances;
  2. Обновите LAST_CHECKIN_TIME этого экземпляра сервера; Если есть экземпляр тайм-аута:
  3. Получить блокировку STATE_ACCESS;
  4. Получить экземпляр тайм-аута failedInstances;
  5. Получить блокировку TRIGGER_ACCESS;
  6. clusterRecover:
    • Для каждого failedInstances получите firedTriggers каждого экземпляра по instanceId;
    • Для каждого запущенного триггера:
      • Обновить статус триггера:
        • BLOCKED->WAITING
        • PAUSED_BLOCKED->PAUSED
        • ACQUIRED->WAITING
      • Если FireDtrigger не находится в приобретенном состоянии (в состоянии выполнения), и Jobrequestrecestrecvovery = True: Создайте Simpletretrigger и храните его в таблице триггера, состояния = ждать, misfire_instr = misfire_instruction_ignore_misfire_policy.
      • удалить firedTrigger

Диаграмма последовательности выполнения потока clusterManager показана на рисунке 1-8:

Рисунок 1-8

Обратите внимание на проблему

  1. проблема с синхронизацией времени

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

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

  1. Узел проблемы конкуренции за работу

Поскольку Quartz использует алгоритм случайной балансировки нагрузки, задания выполняются разными экземплярами случайным образом. На официальном сайте Quartz упоминается, что в настоящее время нет возможности назначить (закрепить) задание на конкретный узел в кластере.

  1. Получение списка заданий из кластера

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

6. Ссылки

  1. блог woo woo woo.cn на.com/drift-ice/afraid…
  2. ООО, ООО на .cn blog .com / chen yu yao di ...
  3. blog.csdn.net/u014427391/...
  4. blog.CSDN.net/moon_yang_no…
  5. № OSCHINA.net/Songhong Xu/…
  6. Система Ван Тянь Талант/2016/01/03/…
  7. woohoo.quartz-scheduler.org/document ATI…