KafkaプロデューサーRecordAccumulatorの予備調査

ここに画像の説明を挿入します
ステップ1:まず、パーティションに応じてどのキューに挿入する必要があるかを見つけます。
既存のキューがある場合は、既存のキューを使用します。キューが存在しない場合は、新しいキューを作成して
、両端キューをロックします。
ステップ2:キュー内のバッチにデータを追加して、tryAppendを呼び出し、次の手順を実行します。 Deque A RecordBatchは、Record
同期ブロックを最後に
追加し、自動的にロックを解除します。追加が成功すると、ProduceRequestResultをカプセル化するRecordAppendResultが返されます。
追加が失敗した場合は、から新しいByteBufferを申請してみてください。バッファプール
ステップ3:計算バッチのサイズ
ステップ4:移動バッチのサイズに応じてメモリを割り当てて
。両端キューをロックする
ステップ5:バッチにデータを書き込もう。(ステップ2を試してください)
追加が成功した場合は戻ります。追加が失敗した場合は、ByteBufferを使用してRecordBatchを作成します。
ステップ6:メモリサイズに従ってバッチをカプセル化します。
ステップ7:バッチをキューの最後に配置します(新しく作成されたRecordBatchにレコードを追加します)、そして新しく作成されたRecordBatchを対応するDequeの最後に追加します)
同期されたブロックが終了すると、自動的にロックが解除されます

            /**
             * 步骤七:
             *  把消息放入accumulator(32M的一个内存)
             *  然后有accumulator把消息封装成为一个批次一个批次的去发送。
             */
            RecordAccumulator.RecordAppendResult result = accumulator.append(tp, timestamp, serializedKey, serializedValue, interceptCallback, remainingWaitMs);
    public  RecordAppendResult append(TopicPartition tp,
                                     long timestamp,
                                     byte[] key,
                                     byte[] value,
                                     Callback callback,
                                     long maxTimeToBlock) throws InterruptedException {
    
    
        // We keep track of the number of appending thread to make sure we do not miss batches in
        // abortIncompleteBatches().
        appendsInProgress.incrementAndGet();
        try {
    
    
            // check if we have an in-progress batch
            /**
             * 步骤一:先根据分区找到应该插入到哪个队列里面。
             * 如果有已经存在的队列,那么我们就使用存在队列
             * 如果队列不存在,那么我们新创建一个队列
             *
             * 我们肯定是有了存储批次的队列,但是大家一定要知道一个事
             * 我们代码第一次执行到这儿,获取其实就是一个空的队列。
             *
             * 现在代码第二次执行进来。
             * 假设 分区还是之前的那个分区。
             *
             * 这个方法里面我们之前分析,里面就是针对batchs进行的操作
             * 里面kafka自己封装了一个数据结构:CopyOnWriteMap (这个数据结构本来就是线程安全的)
             *
             *
             * 根据当前消息的信息,获取一个队列
             *
             *
             * 线程一,线程二,线程三
             */
            Deque<RecordBatch> dq = getOrCreateDeque(tp);
            /**
             * 假设我们现在有线程一,线程二,线程三
             *
             */
            synchronized (dq) {
    
    
                //线程一进来了
                //线程二进来了
                if (closed)
                    throw new IllegalStateException("Cannot send after the producer is closed.");
                /**
                 * 步骤二:
                 *      尝试往队列里面的批次里添加数据
                 *
                 *      一开始添加数据肯定是失败的,我们目前只是以后了队列
                 *      数据是需要存储在批次对象里面(这个批次对象是需要分配内存的)
                 *      我们目前还没有分配内存,所以如果按场景驱动的方式,
                 *      代码第一次运行到这儿其实是不成功的。
                 */
                RecordAppendResult appendResult = tryAppend(timestamp, key, value, callback, dq);
                //线程一 进来的时候,
                //第一次进来的时候appendResult的值就为null
                if (appendResult != null)
                    return appendResult;
            }//释放锁

            // we don't have an in-progress record batch try to allocate a new batch
            /**
             * 步骤三:计算一个批次的大小
             *
             * 在消息的大小和批次的大小之间取一个最大值,用这个值作为当前这个批次的大小。
             * 有可能我们的一个消息的大小比一个设定好的批次的大小还要大。
             * 默认一个批次的大小是16K。
             * 所以我们看到这段代码以后,应该给我们一个启示。
             * 如果我们生产者发送数的时候,如果我们的消息的大小都是超过16K,
             * 说明其实就是一条消息就是一个批次,那也就是说消息是一条一条被发送出去的。
             * 那如果是这样的话,批次这个概念的设计就没有意义了
             * 所以大家一定要根据自定公司的数据大小的情况去设置批次的大小。
             *
             */
            int size = Math.max(this.batchSize, Records.LOG_OVERHEAD + Record.recordSize(key, value));
            log.trace("Allocating a new {} byte message buffer for topic {} partition {}", size, tp.topic(), tp.partition());
            /**
             * 步骤四:
             *  根据批次的大小去分配内存
             *
             *
             *  线程一,线程二,线程三,执行到这儿都会申请内存
             *  假设每个线程 都申请了 16k的内存。
             *
             *  线程1 16k
             *  线程2 16k
             *  线程3 16k
             *
             */
            ByteBuffer buffer = free.allocate(size, maxTimeToBlock);

            synchronized (dq) {
    
    
                //假设线程一 进来了。
                //线程二进来了
                // Need to check if producer is closed again after grabbing the dequeue lock.
                if (closed)
                    throw new IllegalStateException("Cannot send after the producer is closed.");
                /**
                 * 步骤五:
                 *      尝试把数据写入到批次里面。
                 *      代码第一次执行到这儿的时候 依然还是失败的(appendResult==null)
                 *      目前虽然已经分配了内存
                 *      但是还没有创建批次,那我们向往批次里面写数据
                 *      还是不能写的。
                 *
                 *   线程二进来执行这段代码的时候,是成功的。
                 */
                RecordAppendResult appendResult = tryAppend(timestamp, key, value, callback, dq);
                //失败的意思就是appendResult 还是会等于null
                if (appendResult != null) {
    
    
                    //释放内存

                    //线程二到这儿,其实他自己已经把数据写到批次了。所以
                    //他的内存就没有什么用了,就把内存个释放了(还给内存池了。)
                    free.deallocate(buffer);
                    return appendResult;
                }
                /**
                 * 步骤六:
                 *  根据内存大小封装批次
                 *
                 *
                 *  线程一到这儿 会根据内存封装出来一个批次。
                 */
                MemoryRecords records = MemoryRecords.emptyRecords(buffer, compression, this.batchSize);
                RecordBatch batch = new RecordBatch(tp, records, time.milliseconds());
                //尝试往这个批次里面写数据,到这个时候 我们的代码会执行成功。

                //线程一,就往批次里面写数据,这个时候就写成功了。
                FutureRecordMetadata future = Utils.notNull(batch.tryAppend(timestamp, key, value, callback, time.milliseconds()));
                /**
                 * 步骤七:
                 *  把这个批次放入到这个队列的队尾
                 *
                 *
                 *  线程一把批次添加到队尾
                 */
                dq.addLast(batch);
                incomplete.add(batch);
                return new RecordAppendResult(future, dq.size() > 1 || batch.records.isFull(), true);
            }//释放锁
        } finally {
    
    
            appendsInProgress.decrementAndGet();
        }
    }

-> 




おすすめ

転載: blog.csdn.net/m0_46449152/article/details/114897101
おすすめ