Kafka produce flush 引起的性能分析

调用kafka producer发送数据时,发现延迟级别在10-200ms不等,与正常的kafka写入速度不匹配,于是开始找问题~

 

一.场景:

一批数据,需要遍历每个数据并发送数据细节的信息到kafka,下面是我原始代码,每个人发送后执行一次flush操作。

val results = Array[DataObject](...)
results.foreach(data => {
    val info = new ProducerRecord[String, String](topic, message)
    producer.send(info)
})
kafka.flush()

服务器执行延迟在10-200ms不等

Kafka produce flush 引起的性能分析_第1张图片

 

二.可能原因分析:

1.send 函数造成阻塞

    public Future send(ProducerRecord record) {
        return this.send(record, (Callback)null);
    }

    public Future send(ProducerRecord record, Callback callback) {
        ProducerRecord interceptedRecord = this.interceptors == null ? record : this.interceptors.onSend(record);
        return this.doSend(interceptedRecord, callback);
    }

查看源码的send逻辑,一种有回调函数,一种没有回调函数,所以这里send是异步执行,不会造成堵塞,排除

2.flush 函数造成阻塞

    public void flush() {
        log.trace("Flushing accumulated records in producer.");
        this.accumulator.beginFlush();
        this.sender.wakeup();

        try {
            this.accumulator.awaitFlushCompletion();
        } catch (InterruptedException var2) {
            throw new InterruptException("Flush interrupted.", var2);
        }
    }

flush 这里accumulator会调用await相关方法,查看官方API的解释是:

flush()
Invoking this method makes all buffered records immediately available to send (even if linger.ms is greater than 0) and blocks on the completion of the requests associated with these records.

调用此方法可使所有缓冲记录立即可用于发送(即使linger.ms大于0)并在与这些记录关联的请求完成时发生阻塞。 ok,找到问题

 

三.Flush 原理

基于flush引起的延迟,首先看一下kafka生产的过程

Step1:异步调用send发送日志,根据Properties的配置对kv进行序列化

Step2::根据k hash 得到分区信息,追加到对应topic下的partition,这里先会写入到本地缓存区

Step3: 本地缓存写入后,有独立的线程传送向producer发送ACK

1.分析:

flush 是将第二步写到缓存区的数据强制推送发送,正常情况下清空缓存区操作通过参数配置实现:

batch.size 离线缓存达到该size时执行一次flush

linger.ms 达到该时间间隔时,执行一次flush

调用flush时,会清空缓存区内存,调用 awaitFlushCompletion 时需要等待缓存区清空,这里会造成线程的堵塞

    public void awaitFlushCompletion() throws InterruptedException {
        try {
            Iterator i$ = this.incomplete.all().iterator();

            while(i$.hasNext()) {
                RecordBatch batch = (RecordBatch)i$.next();
                batch.produceFuture.await();
            }
        } finally {
            this.flushesInProgress.decrementAndGet();
        }

    }

awaitFlushCompletion 将当前缓存区数据构造迭代器循环发送,并在finally阶段调整offset。

这里我设置发送延迟时间为1000ms

我的实际发送时间在1000ms以内,所以每次发送调用 flush 都会造成延迟,相当于手动调用频繁的刷新缓存区,增加的IO等待的时间,违背了批处理减少IO的规则,所以造成kafka写入时长增加,这里取消flush,通过参数控制 producer 生产解决问题。

Kafka produce flush 引起的性能分析_第2张图片

第一次时间长是因为初始化kafka服务端,和最一开始添加 flush 相比,时间消耗基本可以忽略。

你可能感兴趣的:(kafka,kafka,producer,flush)