Bootstrap

resttemplate默认超时时间_深入理解Kafka客户端之超时批次的处理

一、场景分析

     前面提到,消息封装成批次对象ProducerBatch后,会放到RecordAccumulator对应的Deque队列,等待Sender线程去发送。但是封装好的批次会有一个时间限制,如果超过这个时间限制还未发送成功,那么就会将该批次标记为超时批次,从而执行相应的处理。那么客户端如何处理这种超时的批次呢?为什么超时批次会导致数据重复?这篇进行详细的分析。 二、图示说明

    首先更正一下《深入理解Kafka客户端之服务端响应及超时请求的处理》中的流程图,图中少画了一个数据结构:InFlightBatches,这个结构保存的是正在发送的批次。当Sender线程将缓存中的批次拿出来封装成请求的同时,会将这个批次放到InFlightBatches结构中,即标记这些批次正在发送,当发送成功,会将这个批次从InFlightBatches中移除,同样,如果批次超时,也会将该批次移除。

    超时批次的处理主要在右下角Sender线程这块:

51378978651675ff598721ee9e84a67a.png

三、过程源码分析

    还是从Sender的runOnce方法看起,主要看下面的代码:

long pollTimeout = sendProducerData(currentTimeMs);
private long sendProducerData(long now) {
        ...    //TODO 步骤六:对超时批次对处理(由于没有建立网络连接,第一次这里的代码也不执行)这里的超时指生成的批次超过120s未发送或者发送了未返回响应    //获取inflightBatches集合中已经超时的批次,    // inflightBatches记录的是从缓存中取出来的批次,上面进行合并时,将批次从Deque中取出来    List expiredInflightBatches = getExpiredInflightBatches(now);    //获取缓存中已经过期的批次,这里指还未发送就已经超时的批次    List expiredBatches = this.accumulator.expiredBatches(now);    //获取总的过期的批次    expiredBatches.addAll(expiredInflightBatches);    if (!expiredBatches.isEmpty())        log.trace("Expired {} batches in accumulator", expiredBatches.size());    //遍历处理过期的批次    for (ProducerBatch expiredBatch : expiredBatches) {
            String errorMessage = "Expiring " + expiredBatch.recordCount + " record(s) for " + expiredBatch.topicPartition            + ":" + (now - expiredBatch.createdMs) + " ms has passed since batch creation";        //TODO 处理超时的批次        failBatch(expiredBatch, -1, NO_TIMESTAMP, new TimeoutException(errorMessage), false);        if (transactionManager != null && expiredBatch.inRetry()) {
                // This ensures that no new batches are drained until the current in flight batches are fully resolved.            transactionManager.markSequenceUnresolved(expiredBatch.topicPartition);        }    }    ...}
1. 在sendProducerData方法中,首先获取inFlightBatches中超时的批次:
List<ProducerBatch> expiredInflightBatches = getExpiredInflightBatches(now);
private ListgetExpiredInflightBatches(long now) {
        List expiredBatches = new ArrayList<>();    for (Iterator>> batchIt = inFlightBatches.entrySet().iterator(); batchIt.hasNext();) {
            Map.Entry> entry = batchIt.next();        //获取每个分区对应的List集合        List partitionInFlightBatches = entry.getValue();        //说明指定的分
;