Kafka KStreams - 处理超时

时间:2016-08-30 16:11:55

标签: java apache-kafka apache-kafka-streams

我正在尝试使用<KStream>.process()TimeWindows.of("name", 30000)批量处理某些 KTable 值并将其发送。似乎30秒超过了消费者超时间隔,之后Kafka认为消费者已经解散并释放分区。

我尝试提高民意调查提交时间间隔的频率以避免这种情况:

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秒按键对值进行分组。在 Processor.init()中,我致电context.schedule(30000)

DBProcessorSupplier 提供 DBProcessor 的实例。这是 AbstractProcessor 的实现,其中提供了所有覆盖。他们只做LOG,所以我知道每个人都被击中了。

这是一个非常简单的拓扑结构,但很清楚我在某个地方错过了一个步骤。

编辑:

我知道我可以在服务器端对此进行调整,但我希望有一个客户端解决方案。我喜欢在客户端退出/死亡时很快就可以使用分区的概念。

编辑:

为了简化问题,我从图中删除了聚合步骤。它现在只是消费者 - &gt;处理器()。 (如果我将消费者直接发送到.print(),它会很快发挥作用,所以我知道它没问题。 (类似地,如果我通过.print()输出聚合(KTable),它似乎也可以。)

我发现.process() - 应该每隔30秒调用.punctuate()实际上阻塞了不同的时间长度并且随机输出(如果有的话) )。

此外:

我将调试级别设置为&#39; debug&#39;并重申。我看到很多消息:

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

.punctuate()函数中的断点未被击中。所以它做了很多工作,但没给我机会使用它。

1 个答案:

答案 0 :(得分:9)

一些澄清:

  • StreamsConfig.COMMIT_INTERVAL_MS_CONFIG是提交间隔的下限,即在提交之后,下一次提交不会在此时间之前发生。基本上,Kafka Stream试图在这段时间过后尽快提交,但无法保证在下一次提交时需要多长时间。
  • StreamsConfig.POLL_MS_CONFIG用于内部KafkaConsumer#poll()来电,以指定poll()来电的最长阻止时间。

因此,这两个值对心跳更有帮助。

Kafka Streams遵循&#34;深度优先&#34;处理记录时的策略。这意味着,在每个记录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:如果增加此值,则有更多时间处理数据(为了完整性而添加此项,因为它实际上是客户端设置而不是服务器/代理端配置 - 即使您知道这个解决方案并不喜欢它:))
  

修改

     

从Kafka 0.10.1开始,可以(并推荐)在流配置中为消费者和procuder配置添加前缀。这避免了参数冲突,因为一些参数名称用于消费者和生产者,否则无法区分(并且将同时应用于消费者生产者)。   要为参数添加前缀,您可以分别使用StreamsConfig#consumerPrefix()StreamsConfig#producerPrefix()。例如:       streamsConfig.put(StreamsConfig.consumerPrefix(ConsumerConfig.PARAMETER), VALUE);

要添加的另一件事:此问题中描述的场景是一个已知问题,并且已经KIP-62KafkaConsumer引入了发送心跳的后台线程,从而将心跳与{{1}解耦调用。 Kafka Streams将在即将发布的版本中利用这一新功能。