Java 卡夫卡消费者配置/性能问题

Java 卡夫卡消费者配置/性能问题,java,performance,apache-kafka,kafka-consumer-api,Java,Performance,Apache Kafka,Kafka Consumer Api,我正在尝试卡夫卡作为AWS SQS的替代品。其动机主要是为了提高性能,卡夫卡将消除每次提取10条消息的限制,每次提取的消息上限为256kb。下面是我的用例的高级场景。我有一堆爬虫,它们正在发送文档进行索引。有效负载的大小平均约为1MB。爬虫程序调用SOAP端点,该端点依次运行生产者代码以将消息提交到kafka队列。消费者应用程序拾取消息并进行处理。对于我的测试盒,我已经配置了30个分区和2个复制的主题。这两个kafka实例使用1个zookeeper实例运行。卡夫卡的版本是0.10.0 为了进行测

我正在尝试卡夫卡作为AWS SQS的替代品。其动机主要是为了提高性能,卡夫卡将消除每次提取10条消息的限制,每次提取的消息上限为256kb。下面是我的用例的高级场景。我有一堆爬虫,它们正在发送文档进行索引。有效负载的大小平均约为1MB。爬虫程序调用SOAP端点,该端点依次运行生产者代码以将消息提交到kafka队列。消费者应用程序拾取消息并进行处理。对于我的测试盒,我已经配置了30个分区和2个复制的主题。这两个kafka实例使用1个zookeeper实例运行。卡夫卡的版本是0.10.0

为了进行测试,我在队列中发布了700万条消息。我创建了一个消费者组,其中有30个消费者线程,每个分区一个。我最初的印象是,与通过SQS获得的处理能力相比,这将大大加快处理能力。不幸的是,情况并非如此。在我的例子中,数据处理是复杂的,平均需要1-2分钟才能完成。这导致了一系列的分区重新平衡,因为线程无法按时进行心跳。我可以在日志中看到一堆消息

组已满的自动偏移提交失败\u组:无法执行提交 已完成,因为集团已重新平衡并分配 分区到另一个成员。这意味着 对poll()的后续调用长于配置的 session.timeout.ms,这通常意味着轮询循环 花太多时间处理消息。你也可以解决这个问题 通过增加会话超时或减少 在poll()中返回的具有max.poll.records的批

这会导致同一消息被多次处理。我尝试使用会话超时、max.poll.records和poll time来避免这种情况,但这降低了总体处理时间。下面是一些配置参数。

metadata.max.age.ms=300000
max.partition.fetch.bytes=1048576
bootstrap.servers=[kafkahost1:9092,kafkahost2:9092]
enable.auto.commit=true
最大轮询记录数=10000
request.timeout.ms=310000
heartbeat.interval.ms=100000
auto.commit.interval.ms=1000
receive.buffer.bytes=65536
fetch.min.bytes=1
send.buffer.bytes=131072
value.deserializer=class com.autodesk.preprocessor.consumer.serializer.KryoObjectSerializer
group.id=完整组
retry.backoff.ms=100
fetch.max.wait.ms=500
connections.max.idle.ms=540000
session.timeout.ms=300000
key.deserializer=类org.apache.kafka.common.serialization.StringDeserializer
metrics.sample.window.ms=30000
auto.offset.reset=最新版本
我将消费者调查时间缩短到100毫秒。这减少了再平衡问题,消除了重复处理,但显著减慢了整个过程。最终需要35小时才能完成处理所有600万条消息,而使用基于SQS的解决方案需要25小时。每个消费者线程平均每次轮询检索50-60条消息,尽管其中一些线程有时轮询0条记录。当分区中有大量可用消息时,我不确定这种行为。同一线程能够在随后的迭代中拾取消息。这可能是因为再平衡吗

这是我的消费代码

while(true){
试一试{
ConsumerRecords记录=consumer.poll(100);
对于(消费者记录:记录){
if(record.value()!=null){
TextAnalysisRequest textAnalysisObj=record.value();
if(textAnalysisObj!=null){
//过程记录
submitPostProcessRequest(textAnalysisObj);
}
}
}
}捕获(例外情况除外){
LOGGER.error(“完整用户组worker中的错误”,例如);
}
我知道记录处理部分是我的一个瓶颈。但我相信这里的一些人也有类似的处理大量处理时间的用例。我想过通过在专用线程中旋转每个处理器来进行异步处理,或者使用一个大容量的线程池,但不确定这是否会在系统中产生很大的负载同时,我也看到过一些例子,人们使用暂停和恢复API来执行处理,以避免重新平衡问题


在这种情况下,我真的在寻找一些建议/最佳实践。特别是关于hearbeat、请求超时、最大轮询记录、自动提交间隔、轮询间隔等的建议配置设置。如果kafka不是适合我的用例的工具,请也让我知道。

您可以从异步处理消息开始通常,在一个独立的线程中,而不是在从Kafka读取的线程中。这种方式自动提交将非常快,Kafka不会中断您的会话。类似于以下内容:

    private final BlockingQueue<TextAnalysisRequest> requests = 
new LinkedBlockingQueue();
在处理线程中:

while (true) {
    try{
        ConsumerRecords records = consumer.poll(100);
        for (ConsumerRecord record : records) {
            if(record.value()!=null){
                TextAnalysisRequest textAnalysisObj = record.value();
                if(textAnalysisObj!=null){
                    // Process record
                    requests.offer(textAnalysisObj);
                }
            }
     }    
}
catch(Exception ex){
    LOGGER.error("Error in Full Consumer group worker", ex);
}
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    TextAnalysisRequest textAnalysisObj = requests.take();
                    PreProcessorUtil.submitPostProcessRequest(textAnalysisObj);
                } catch (InterruptedException e) {
                    LOGGER.info("Process thread interrupted", e);
                    Thread.currentThread().interrupt();
                } catch (Throwable t) {
                    LOGGER.warn("Unexpected throwable while processing.", t);
                }
            }
另请参阅本文档,了解通过卡夫卡发送大型消息的策略:

简言之,Kafka在10K左右的小邮件上表现最好,如果您需要发送较大的邮件,最好将其放在网络存储中,通过Kafka发送,或者将其拆分

while (true) {
    try{
        ConsumerRecords records = consumer.poll(100);
        for (ConsumerRecord record : records) {
            if(record.value()!=null){
                TextAnalysisRequest textAnalysisObj = record.value();
                if(textAnalysisObj!=null){
                    // Process record
                    requests.offer(textAnalysisObj);
                }
            }
     }    
}
catch(Exception ex){
    LOGGER.error("Error in Full Consumer group worker", ex);
}
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    TextAnalysisRequest textAnalysisObj = requests.take();
                    PreProcessorUtil.submitPostProcessRequest(textAnalysisObj);
                } catch (InterruptedException e) {
                    LOGGER.info("Process thread interrupted", e);
                    Thread.currentThread().interrupt();
                } catch (Throwable t) {
                    LOGGER.warn("Unexpected throwable while processing.", t);
                }
            }