Я пишу компонент планирования задач, так что задача таймера должна выполняться только один раз в кластере с дублированным планировщиком (т. Е. На сервере приложений), и в настоящее время для надежности используется распределенная блокировка.
Распределенная блокировка реализована с помощью записи Redis.Когда задача отправляется в планировщик A, планировщик B будет использовать SETNX
(атомарный) и записывать запись задачи на сервере Redis.Если другой планировщик отправляет точно такую же задачу (например, по электронной почте пользователю), SETNX
не будет выполнен, и тогда планирование будет отменено в планировщике B.
Моя проблема возникает в особом случаеэто описано следующим образом:
scheduler A Redis scheduler B scheduler C
| lock | |
|--------------->| lock |
| success |<--------------|
|<---------------| fail |
task alpha o |-------------->|
submit | | x schedule
success | | canceled 0 a new scheduler
| | | is scaled in cluster
| | fetch existing tasks |
| |<----------------------------------|
| | report submitted tasks |
| |---------------------------------->| now scheduler C
| | | knows task alpha.
| | |
task alpha o lock | lock o task alpha
starts |--------------->|<----------------------------------| starts
execute | fail | success | execute
|<---------------|---------------------------------->|
schedule x | | keep executing
canceled | |
| |
| |
x Redis fail |
|
unlock v execution success
<----------------------------------| delete task record
|
Redis 0 |
recovered | |
| 0 scheduler B
the record | fetch | recovered
of task alpha |<-------------|
still exists | report |
|------------->| task alpha needs
| lock o execution because
|<-------------| time has arrived!
| success |
|------------->|
| |
| unlock v execution success (inconsistent execution)
|<-------------|
|
|
На этом рисунке планировщик C выполнил task alpha
. Однако из-за сбоя разблокировки запись задачи не удаляется из Redis.После этого, когда планировщик B будет восстановлен, задание будет выполнено снова, что вызывает несоответствие .
Как правило, не предполагается, что основной сервис Redis не будет работать, что делает эту проблемуредкая проблема.Redis всегда можно принять за дублированный кластер, который синхронизирует записи друг с другом. Сбой Redis означает, что все дублированные узлы Redis мертвы.
Длинное описание, но короткие вопросы:
- Как можно решить проблему несоответствия?
- Есть ли какой-либо другой лучший планировщик для сценария, описанного выше?