Кафка KStreams - обработка таймаутов

Я пытаюсь использовать <KStream>.process() С TimeWindows.of("name", 30000) чтобы собрать некоторые KTable значения и отправить их на. Кажется, что 30 секунд превышает интервал тайм-аута потребителя, после которого Кафка считает указанного потребителя несуществующим и освобождает раздел.

Я попытался увеличить частоту опрос и фиксация интервала чтобы избежать этого:

config.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, "5000");
config.put(StreamsConfig.POLL_MS_CONFIG, "5000");

к сожалению, эти ошибки до сих пор происходящее:

(много таких)

ERROR  o.a.k.s.p.internals.RecordCollector - Error sending record to topic kafka_test1-write_aggregate2-changelog 
org.apache.kafka.common.errors.TimeoutException: Batch containing 1 record(s) expired due to timeout while requesting metadata from brokers for kafka_test1-write_aggregate2-changelog-0

а потом:

INFO   o.a.k.c.c.i.AbstractCoordinator - Marking the coordinator 12.34.56.7:9092 (id: 2147483547 rack: null) dead for group kafka_test1
WARN   o.a.k.s.p.internals.StreamThread - Failed to commit StreamTask #0_0 in thread [StreamThread-1]: 
  org.apache.kafka.clients.consumer.CommitFailedException: Commit cannot be completed since the group has already rebalanced and assigned the partitions to another member. This means that the time between subsequent calls to poll() was longer than the configured session.timeout.ms, which typically implies that the poll loop is spending too much time message processing. You can address this either by increasing the session timeout or by reducing the maximum size of batches returned in poll() with max.poll.records.
at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator$OffsetCommitResponseHandler.handle(ConsumerCoordinator.java:578)

ясно, что мне нужно чаще отправлять сердцебиения обратно на сервер. Как?

мой топология:

KStreamBuilder kStreamBuilder = new KStreamBuilder();
KStream<String, String> lines = kStreamBuilder.stream(TOPIC);
KTable<Windowed<String>, String>  kt = lines.aggregateByKey(
            new DBAggregateInit(),
            new DBAggregate(),
            TimeWindows.of("write_aggregate2", 30000));

DBProcessorSupplier dbProcessorSupplier = new DBProcessorSupplier();

kt.toStream().process(dbProcessorSupplier);
KafkaStreams kafkaStreams = new KafkaStreams(kStreamBuilder, streamsConfig);

kafkaStreams.start();

на KTable группирует значения по ключу каждые 30 секунд. В .init () я называю context.schedule(30000).

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

это довольно простая топология, но ясно, что я где-то пропустил шаг.


Edit:

Я понимаю, что могу настроить это на стороне сервера, но я надеюсь, что есть решение на стороне клиента. Мне нравится понятие разделов, доступных довольно быстро, когда клиент выходит / умирает.


Edit:

в попытке упростить задачу я удалил шаг агрегации из графика. Теперь это просто потребитель- > процессор (). (Если я отправлю потребителя непосредственно в .print() он работает v быстро, поэтому я знаю, что все в порядке). (Аналогично, если я выведу агрегацию (KTable) через .print() кажется, тоже нормально).

я обнаружил, что .process(), которые должны называть .punctuate() каждые 30 секунды фактически блокируются для переменных длин времени и выводятся несколько случайным образом (если вообще).

далее:

Я установил уровень отладки в "debug" и reran. Я вижу много сообщений:

DEBUG  o.a.k.s.p.internals.StreamTask - Start processing one record [ConsumerRecord <info>

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

1 ответов


несколько уточнений:

  • StreamsConfig.COMMIT_INTERVAL_MS_CONFIG является нижней границей интервала фиксации, т. е. после фиксации следующая фиксация происходит не раньше этого времени. В принципе, поток Кафки пытается совершить как можно скорее после этого времени, но нет никакой гарантии, сколько времени на самом деле потребуется, чтобы сделать следующий коммит.
  • StreamsConfig.POLL_MS_CONFIG используется для внутренней KafkaConsumer#poll() вызов, чтобы указать максимальное время блокировки poll() вызов.

таким образом, оба значения не полезно чаще сердцебиение.

потоки Кафки следуют стратегии "глубина-первый" при обработке записи. Это означает, что после poll() для каждой записи выполняются все операторы топологии. Предположим, у вас есть три последовательных карты, чем все три карты будут вызваны для первой записи, прежде чем следующая/вторая запись будет обработана.

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

вы можете использовать параметры конфигурации KafkaConsumer что вы можете задать через StreamsConfig чтобы сделать это (см. https://kafka.apache.org/documentation.html#consumerconfigs):

streamConfig.put (ConsumerConfig.XXX, VALUE);

  • max.poll.records: если вы уменьшите это значение, будет опрошено меньше записей
  • session.timeout.ms: если вы увеличиваете это значение, есть больше времени для обработки данных (добавление этого для полноты, потому что это на самом деле настройка клиента, а не конфигурация на стороне сервера/брокера-даже если вы знаете об этом решении и не нравится :))

редактировать

как Кафка 0.10.1 можно (и рекомендуется) префикс consumer и procuder configs в пределах streams config. Это позволяет избежать конфликтов параметров, поскольку некоторые имена параметров используются для потребителя и производителя и не могут быть различимы иначе (и будут применяться к потребителю и производитель в то же время). Для префикса параметра можно использовать StreamsConfig#consumerPrefix() или StreamsConfig#producerPrefix(), соответственно. Например: streamsConfig.put(StreamsConfig.consumerPrefix(ConsumerConfig.PARAMETER), VALUE);

еще одна вещь, чтобы добавить: сценарий, описанный в этом вопросе, является известной проблемой, и уже КИП-62 это вводит фоновый поток для KafkaConsumer которые посылают сердцебиения, таким образом отделяя сердцебиения от poll() звонки. Kafka Streams будет использовать эту новую функцию в предстоящих выпусках.