Kafka-реализация отложенной очереди с использованием потребителя высокого уровня
хотите реализовать отложенного потребителя с помощью API потребителя высокого уровня
основные идеи:
- создавать сообщения по ключу (каждый msg содержит метку времени создания) это гарантирует, что каждый раздел упорядочил сообщения по времени создания.
- "авто".совершать.enable=false (будет явно фиксироваться после каждого процесса сообщения)
- употребляет
- проверьте метку времени сообщения и проверьте, достаточно ли времени прошло
- сообщение процесса (эта операция никогда не будет терпеть неудачу)
-
зафиксировать 1 смещение
while (it.hasNext()) { val msg = it.next().message() //checks timestamp in msg to see delay period exceeded while (!delayedPeriodPassed(msg)) { waitSomeTime() //Thread.sleep or something.... } //certain that the msg was delayed and can now be handled Try { process(msg) } //the msg process will never fail the consumer consumer.commitOffsets //commit each msg }
некоторые проблемы с этой реализацией:
- фиксация каждого смещения может замедлить ZK
- может потребителя.commitOffsets создает исключение? если да, я буду использовать одно и то же сообщение дважды (может решить с идемпотентными сообщениями)
- проблема долгое время ожидания без фиксации смещения, например период задержки составляет 24 часа, будет получать следующий от итератора, спать в течение 24 часов, обрабатывать и фиксировать (тайм-аут сеанса ZK ?)
- как сеанс ZK может поддерживать жизнь без фиксации новых смещений ? (установка зоопарка улья.сеанс.тайм-аут.ms может разрешать в Мертвом потребителе, не признавая его)
- любые другие проблемы im отсутствует?
спасибо!
4 ответов
один из способов сделать это - использовать другую тему, где вы нажимаете все сообщения, которые должны быть задержаны. Если все отложенные сообщения должны быть обработаны после той же задержки времени, это будет довольно прямо вперед:
while(it.hasNext()) {
val message = it.next().message()
if(shouldBeDelayed(message)) {
val delay = 24 hours
val delayTo = getCurrentTime() + delay
putMessageOnDelayedQueue(message, delay, delayTo)
}
else {
process(message)
}
consumer.commitOffset()
}
все регулярные сообщения теперь будут обработаны как можно скорее, в то время как те, которые нуждаются в задержке, помещаются на другую тему.
хорошо то, что мы знаем, что сообщение во главе отложенной темы-это то, что должен быть обработан первым, так как его значение delayTo будет наименьшим. Поэтому мы можем настроить другого потребителя, который читает сообщение head, проверяет, находится ли отметка времени в прошлом, и если да, обрабатывает сообщение и фиксирует смещение. Если нет, он не фиксирует смещение и вместо этого просто спит до этого времени:
while(it.hasNext()) {
val delayedMessage = it.peek().message()
if(delayedMessage.delayTo < getCurrentTime()) {
val readMessage = it.next().message
process(readMessage.originalMessage)
consumer.commitOffset()
} else {
delayProcessingUntil(delayedMessage.delayTo)
}
}
если есть разные времена задержки, вы можете разделить тему на задержку (например, 24 часа, 12 часов, 6 часов). Если время задержки более динамично это становится немного сложнее. Вы могли бы решить его, введя две темы задержки. Прочитайте все сообщения от темы задержки A
и обрабатывать все сообщения, чьи delayTo
значение в прошлом. Среди других вы просто найдете тот, который с ближайшим delayTo
и затем поставить их на тему B
. Спите, пока ближайший не будет обработан, и делайте все наоборот, т. е. обрабатывайте сообщения из темы B
и поместите один раз, который еще не должен быть обработан по теме A
.
чтобы ответить на ваши конкретные вопросы (некоторые из них были рассмотрены в комментарии на ваш вопрос)
- фиксация каждого смещения может замедлить ZK
вы можете рассмотреть возможность переключения на сохранение смещения в Кафке (функция доступна с 0.8.2, проверьте offsets.storage
свойство в конфигурации потребителя)
- может потребителя.commitOffsets создает исключение? если да я буду потреблять одно и то же сообщение дважды (может решить с идемпотентными сообщениями)
Я считаю, что это возможно, если он не может связаться с хранилищем смещения, например. Использование идемпотентных сообщений решает эту проблему, как вы говорите.
- проблема долгое время ожидания без фиксации смещения, например, период задержки составляет 24 часа, будет получать следующий от итератора, спать в течение 24 часов, обрабатывать и фиксировать (тайм-аут сеанса ZK ?)
это не будет проблемой с вышеуказанным изложенным решением, если обработка самого сообщения не займет больше времени ожидания сеанса.
- как сеанс ZK может поддерживать жизнь без фиксации новых смещений ? (установка зоопарка улья.сеанс.тайм-аут.ms может разрешать в Мертвом потребителе, не признавая его)
опять же с вышеизложенным вам не нужно устанавливать длительный тайм-аут сеанса.
- любые другие проблемы im отсутствует?
там всегда есть ;)
Я бы предложил другой путь в ваши дела.
нет смысла рассматривать время ожидания в основном потоке потребителя. Это будет анти-шаблон в том, как используются очереди. Концептуально, вам нужно обработать сообщения как можно быстрее и сохранить очередь с низким коэффициентом загрузки.
вместо этого я бы использовал планировщик, который будет планировать задания для каждого сообщения, которое вам нужно отложить. Таким образом, вы можете обработать очередь и создать асинхронные задания, которые будут запускаться в заранее определенные моменты времени.
падение использования этого метода заключается в том, что он чувствителен к состоянию JVM, который держит запланированные задания в памяти. Если эта JVM терпит неудачу, вы теряете запланированные задания, и вы не знаете, была ли задача выполнена или не была выполнена.
есть реализации планировщика, хотя это может быть настроено для запуска в кластерной среде, тем самым сохраняя вас в безопасности от сбоев JVM.
взгляните на эту структуру планирования java:http://www.quartz-scheduler.org/
используйте Tibco EMS или другие очереди JMS. У них есть задержка повтора . Кафка не может быть правильным выбором дизайна для того, что вы делаете