상세한 KafkaProducer 발신자 스레드 (상세 흐름도로 실행)

팁 :이 문서는 카프카 2.2.1 버전을 기반으로합니다.

위의 "카프카 메시지 소스 분석 과정" 과정 KafkaProducer의 전송 방법, 단순히 정말 브로커에 메시지를 보낼 메시지 KafKaProducer 캐시에 추가 된 방법은,이 논문은 카프카 보낸 사람 스레드의 미래를 논의 상세하게 설명하고있다.

된 ClientID ID 생산자입니다 | "clientid는 카프카 프로듀서 네트워크 스레드", KafkaProducer에서 자격이 별도의 스레드를 시작합니다.

1, 보낸 사람 스레드 설명

도 클래스 1.1

그림 삽입 설명 여기
우리는 먼저 다양한 속성의 의미를 보면 :

  • KafkaClient 클라이언트 카프카 클라이언트 네트워크 통신, 주요 패키지의 브로커와 네트워크 통신.
  • 메시지가 기록 RecordAccumulator 누산기 누산기 메시지는 입구 (RecordAccumulator Append 메서드)를 첨가한다.
  • 메타 데이터 메타 데이터 메타 데이터 관리, 즉 파티션의 라우팅 정보 항목을 참조하십시오.
  • 부울 guaranteeMessageOrder 필요한 경우 메시지의 순서를 보장합니다.
  • INT maxRequestSize 호출 요청 최대 크기의 전송 방식은 키의 총 크기가이 값을 초과 할 수 메시지 본문의 시퀀스를 포함하는 메시지를 전송. 매개 변수 max.request.size로 설정합니다.
  • 조건 "제출"(표준)을 정의하는 데 사용되는 쇼트의 ACK 메시지는, 단말 브로커가 클릭을 제출 한 클라이언트에 대한 조건, 선택 값이 0 -1.
  • INT 재시도 재시도.
  • 시간 시간 시간 도구.
  • 부울 진정한 스레드 상태를 실행하는 실행합니다.
  • 부울 forceClose 가까이에 강제 여부, 다음 메시지가 전송되는 무시합니다.
  • SenderMetrics 센서 송신 메시지 통계 지표 수집기 관련.
  • INT 타임 아웃 requestTimeoutMs을 요청합니다.
  • 긴 retryBackoffMs의 실패는 재 시도하기 전에 대기 시간을 요청합니다.
  • ApiVersions apiVersions API의 버전 정보를 표시합니다.
  • TransactionManager에의 transactionManager 트랜잭션 프로세서.
  • 지도 <TopicPartition, 목록 <ProducerBatch >> inFlightBatches은 배치에 관한 메시지를 보내가 실행되는 것을 확인할 수 있습니다.

1.2 실행 방법은 상세

발신자 번호 실행

public void run() {
    log.debug("Starting Kafka producer I/O thread.");
    while (running) {   
        try {
            runOnce();    // @1
        } catch (Exception e) {
            log.error("Uncaught error in kafka producer I/O thread: ", e);
        }
    }
    log.debug("Beginning shutdown of Kafka producer I/O thread, sending remaining records.");
    while (!forceClose && (this.accumulator.hasUndrained() || this.client.inFlightRequestCount() > 0)) {    // @2
        try {
            runOnce();
        } catch (Exception e) {
            log.error("Uncaught error in kafka producer I/O thread: ", e);
        }
    }
    if (forceClose) {                                                                                                                                     // @3
        log.debug("Aborting incomplete batches due to forced shutdown");
        this.accumulator.abortIncompleteBatches();
    }
    try {
        this.client.close();                                                                                                                               // @4
    } catch (Exception e) {
        log.error("Failed to close network client", e);
    }
    log.debug("Shutdown of Kafka producer I/O thread has completed.");
}
复制代码

코드 @ 1 : 운영 상태에서 보낸 스레드 기본 서비스 처리 방법, 메시지 캐시는 브로커에 메시지를 보냅니다. 코드 @ 2 : 강제로 종료하지 않을 경우 버퍼를 보낼 메시지가있는 경우 당신은 다음, 가까운 보낸 스레드에 주도권을 쥐고 경우, 다시는 runonce 남아있는 메시지가 완료 방법 다음 종료를 호출합니다. 코드 @ 3 : 가까운 보낸 스레드를 강제하는 경우, 메시지 전송이 완료되지 않은 거부됩니다. 코드 @ 4 : 닫기 카프카 클라이언트 즉, 네트워크 통신 객체입니다.

다음은 위의 방법이었다 구현 세부 사항을 논의 할 것이다.

1.2.1하여 runonce 상세

발신자 번호를하여 runonce

void runOnce() {
	// 此处省略与事务消息相关的逻辑
    long currentTimeMs = time.milliseconds();
    long pollTimeout = sendProducerData(currentTimeMs);   // @1
    client.poll(pollTimeout, currentTimeMs);                            // @2
}
复制代码

이 문서에서는 트랜잭션 메시지, 코드의 생략 부분의 구현 원리를 문제가되지 않습니다. 1 코드 @ : 호출 메시지를 전송 sendProducerData 방법. 코드 @ 2 :이 방법의 역할 전화?

다음에, 상기 방법은 두 깊이 탐구 하였다.

1.1.2.1 sendProducerData

다음 단계는 세부에서의 구현을 분석합니다. 발신자 번호 sendProducerData

Cluster cluster = metadata.fetch();
// get the list of partitions with data ready to send
RecordAccumulator.ReadyCheckResult result = this.accumulator.ready(cluster, now);
复制代码

단계 1 : 우선, 금회는 데이터 큐가 전송 상태에 도달하는 격벽 항목 판정 버퍼. 조건의 상세한 분석은 전송 가능한 섹션 2.1.1.1에 도달합니다.

# 보내기 생산 데이터를 전송

if (!result.unknownLeaderTopics.isEmpty()) {
    for (String topic : result.unknownLeaderTopics)
        this.metadata.add(topic);
    
    log.debug("Requesting metadata update due to unknown leader topics from the batched records: {}",
                result.unknownLeaderTopics);
    this.metadata.requestUpdate();
}
复制代码

2 단계 : 그것은 전송되는 라우팅 정보 메시지에서 찾을 수없는 경우, 필요한 라우팅 정보 (노드 정보 리더 파티션) 먼저 해당 브로커 서버로 당겨진다.

# 보내기 생산 데이터를 전송

long notReadyTimeout = Long.MAX_VALUE;
while (iter.hasNext()) {
    Node node = iter.next();
    if (!this.client.ready(node, now)) {
        iter.remove();
        notReadyTimeout = Math.min(notReadyTimeout, this.client.pollDelayMs(node, now));
    }
}
复制代码

3 단계 : 네트워크 수준에서 제거하지 준비 파티션 및 파티션이 준비가되지 않은 상태에서 얼마나 오래 다음 간격 내에서 계산이다. 1, 네트워크 링크가 준비되지 기준으로 다음입니다입니다 :

  • 분할 요소는 보류중인 갱신 요구 데이터 (메타 데이터)가있다.
  • 현재의 생산 및 브로커 피어 연결이 설정되고 TCP 세 방향 핸드 셰이크를 완료되었습니다.
  • SSL, ACL 및 기타 메커니즘 관련 상태를 활성화하면 준비가 된 것입니다.
  • 수 처리되는 요청 연결의 파티션에 대응하는 설정 값을 초과 할 경우, 기본적 5 max.in.flight.requests.per.connection의 특성에 의해 설정 될 수있다.

2, 클라이언트 pollDelayMs 다음 시간 간격에서 파티션이 그 기준대로 다음 좋은 상태 (준비되지 않음)에 변경되지 않습니다 시간 추정 :

  • 당신이 피어에 대한 TCP 연결이 생성 된 경우와 연결된 상태에서,이 경우에는 전류 제한이 트리거되지있을 경우 대기 시간이 반환됩니다 제한, 트리거 제한이있는 경우, 그것은 0을 반환합니다.
  • 당신이 TCP 연결이 마지막에 위치 설정하면 연결이 설정 한 후, 그것은 웨이크 업 스레드를 보낼 수 있기 때문에, 다음로부터 Long.MAX_VALUE을 반환합니다.

# 보내기 생산 데이터를 전송

// create produce requests
Map<Integer, List<ProducerBatch>> batches = this.accumulator.drain(cluster, result.readyNodes, this.maxRequestSize, now);
复制代码

4 단계는 : 전송 될 버퍼 배치 (ProducerBatch)에서 메시지를 추출 할 준비를하고, 노드 아이디에 따라 파티션 : 목록 조직,주의, ProducerBatch은 과잉 공간을 사용할 수있는 경우에도, 추가 정보를 추출 할 수 없습니다 상세한 추출을 상세하게 설명한다.

# 보내기 생산 데이터를 전송

addToInflightBatches(batches);
public void addToInflightBatches(Map<Integer, List<ProducerBatch>> batches) {
    for (List<ProducerBatch> batchList : batches.values()) {
        addToInflightBatches(batchList);
    }
}
private void addToInflightBatches(List<ProducerBatch> batches) {
    for (ProducerBatch batch : batches) {
        List<ProducerBatch> inflightBatchList = inFlightBatches.get(batch.topicPartition);
        if (inflightBatchList == null) {
            inflightBatchList = new ArrayList<>();
            inFlightBatches.put(batch.topicPartition, inflightBatchList);
        }
        inflightBatchList.add(batch);
    }
}
复制代码

5 단계 :지도 <TopicPartition 목록 <ProducerBatch >> inFlightBatches, 즉 결합 topic- 분할되어있어서,이 속성의 추출 의미 기억 ProducerBatch가 저장 될 : ProducerBatch는 속성으로서 다음된다 선언 상기 추출 된 데이터 구조의 inFlightBatches 추가 메시지 배치. 메시지가 피드백 파티션 발신자 나사 사이즈 「잔고」에 전송 될 때를 알 수 데이터 구조에 의하면, max.in.flight.requests.per.connection이 값에 도달 잔고 경우에 대한 백 로그의 최대 수를 제어 할 수있다 메시지 큐는 제한 보내드립니다.

# 보내기 생산 데이터를 전송

accumulator.resetNextBatchExpiryTime();
List<ProducerBatch> expiredInflightBatches = getExpiredInflightBatches(now);
List<ProducerBatch> expiredBatches = this.accumulator.expiredBatches(now);
expiredBatches.addAll(expiredInflightBatches);
复制代码

STEP6는 : inflightBatches 배치 및 배치 만료 메시지 (ProducerBatch)을 찾아 상기 시간이 만료 시간 파라미터 delivery.timeout.ms 의해 설정 될 수 있고, 120을 초과의 표준 차동가 현재 시스템 시간 ProducerBatch로 만들어 만료 여부를 판정한다.

# 보내기 생산 데이터를 전송

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";
    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);
    }
}
复制代码

7 단계 : 일괄 프로세스가 호출의 GET을 차단하지 않습니다에 KafkaProducer 번호 전송 방법 FutureRecordMetadata를 제공하는 바우처에 의해 반환 된 오류 알림 메시지 배치, ProduceRequestResult 결과를 전송, 시간 초과 메시지가 있습니다.

# 보내기 생산 데이터를 전송

sensors.updateProduceRequestMetrics(batches);
复制代码

Step8 : 통계 지표의 수집, 우리는 구체적으로 분석하지 않으려하지만, 후속 특별히 메트릭 카프카에 대한 심도있는 토론과 연구를 수행하도록 설계됩니다.

# 보내기 생산 데이터를 전송

long pollTimeout = Math.min(result.nextReadyCheckDelayMs, notReadyTimeout);
pollTimeout = Math.min(pollTimeout, this.accumulator.nextExpiryTimeMs() - now);
pollTimeout = Math.max(pollTimeout, 0);
if (!result.readyNodes.isEmpty()) {
    log.trace("Nodes with data ready to send: {}", result.readyNodes);
    pollTimeout = 0;
}
复制代码

Step9이 : 다음 세트의 시간 지연을 보내 상세한 분석을 보충한다.

# 보내기 생산 데이터를 전송

sendProduceRequests(batches, now);
private void sendProduceRequests(Map<Integer, List<ProducerBatch>> collated, long now) {
    for (Map.Entry<Integer, List<ProducerBatch>> entry : collated.entrySet())
        sendProduceRequest(now, entry.getKey(), acks, requestTimeoutMs, entry.getValue());
}
复制代码

Step10는 : 송신 요구 구축의 단계는 각각 brokerId, 즉 복수 ProducerBatch 전송 요청에 함께 패키징 될 브로커 각각 동시에 각 접속 브로커 만 요청을 보내는 것, 여기에만 빌드 요청 주목 결국 NetworkClient #의 전송 방법을 통해 실제 네트워크 호출을 트리거하지 않습니다이 경우 NetworkClient 전송 될 데이터에 일괄 데이터를 설정합니다.

sendProducerData 방법은 여기에 설명하고, 또한 실제 네트워크 요청이 없기 때문에, 그것은 그것을 트리거 몇시에?

우리는 runonce 방법으로 돌아 계속합니다.

1.2.1.2 NetworkClient 설문 조사 방법
 public List<ClientResponse> poll(long timeout, long now) {
    ensureActive();

    if (!abortedSends.isEmpty()) {
        // If there are aborted sends because of unsupported version exceptions or disconnects,
        // handle them immediately without waiting for Selector#poll.
        List<ClientResponse> responses = new ArrayList<>();
        handleAbortedSends(responses);
        completeResponses(responses);
        return responses;
    }

    long metadataTimeout = metadataUpdater.maybeUpdate(now);   // @1
    try {
        this.selector.poll(Utils.min(timeout, metadataTimeout, defaultRequestTimeoutMs));    // @2
    } catch (IOException e) {
        log.error("Unexpected error during I/O", e);
    }

    // process completed actions
    long updatedNow = this.time.milliseconds();
    List<ClientResponse> responses = new ArrayList<>();            // @3
    handleCompletedSends(responses, updatedNow);
    handleCompletedReceives(responses, updatedNow);
    handleDisconnections(responses, updatedNow);
    handleConnections();
    handleInitiateApiVersionRequests(updatedNow);
    handleTimedOutRequests(responses, updatedNow);
    completeResponses(responses);                                               // @4
    return responses;
}
复制代码

이 문서는하지 상세 네트워크가 어떤 깊이 토론을 달성하기 위해 않습니다, 카프카의 네트워크 통신이 나는 것이다 구체적 세부 사항을 준수 곳의 주요 지점의 첫 번째 점. 코드 @ 1 : 업데이트 클라우드 데이터하려고합니다. 코드 @ 2 : 쓰기 이벤트가 준비되면 트리거 실제 네트워크 통신 것이다 () 메소드를 읽고 준비 이벤트를 쓰기 NIO 선택기 #에서 채널 수신 호출에 의해 처리되는 방법을 선택, 그들은 채널이 될 것입니다 원격 브로커에 메시지. 코드 @ 3 : 다음 메시지, 메시지 수신, 연결되면, API 버전, 타임 아웃을 수집 한 결과를 보내드립니다. 코드 @ 4 : 웨이크 업 및 결과를 순차적으로 다음 결과를 깨울 보내는 클라이언트, 하나의 완전한 메시지 전송 과정을 자격 증명 KafkaProducer # 보내기 메소드가 반환에 대한 응답으로 제공됩니다.

스레드 프로세스를 보내는 송신자는 다음 첫 번째 흐름도 및에 깊이 모양을 추가 할 수있는 중요한 몇 가지 방법 다음 위의 과정을 주어, 여기에 설명.

1.2.2 실행하는 방법의 흐름도

그림 삽입 설명 여기
코드 분석 위의 흐름도에 따르면, 도면은 구체적으로 중요한 단계는 또한 키에 의해 표시된다. 우리가 이해 보낸 스레드를 심화하기 위해, 플로우 차트 클래스 보낸 사람에 의존 스레드의 핵심 방법을 해석 할 수 있습니다.

전송 프로세스 보낸 사람 설명 때문에 방법에 대한 자세한 분석을 포함 다음 RecordAccumulator의 초점은 보낸 사람이 프로세스의 이해를 증진하므로, 대부분의 호출은 특정 로직을 달성하기 위해 RecordAccumulator 방법이다.

2 RecordAccumulator 코어 방법 상세

2.1 RecordAccumulator의 상세 준비 방법

이 방법은 송신 상태에 도달 된 파티션 판정 주로 캐시 메시지에 기초한다.

RecordAccumulator 번호 준비를

public ReadyCheckResult ready(Cluster cluster, long nowMs) {
    Set<Node> readyNodes = new HashSet<>();
    long nextReadyCheckDelayMs = Long.MAX_VALUE;
    Set<String> unknownLeaderTopics = new HashSet<>();

    boolean exhausted = this.free.queued() > 0;
    for (Map.Entry<TopicPartition, Deque<ProducerBatch>> entry : this.batches.entrySet()) {   // @1
        TopicPartition part = entry.getKey();
        Deque<ProducerBatch> deque = entry.getValue();

        Node leader = cluster.leaderFor(part);   // @2
        synchronized (deque) {
            if (leader == null && !deque.isEmpty()) {   // @3
                // This is a partition for which leader is not known, but messages are available to send.
                // Note that entries are currently not removed from batches when deque is empty.
                unknownLeaderTopics.add(part.topic());
            } else if (!readyNodes.contains(leader) && !isMuted(part, nowMs)) {    // @4
                ProducerBatch batch = deque.peekFirst();
                if (batch != null) {
                    long waitedTimeMs = batch.waitedTimeMs(nowMs);
                    boolean backingOff = batch.attempts() > 0 && waitedTimeMs < retryBackoffMs;
                    long timeToWaitMs = backingOff ? retryBackoffMs : lingerMs;
                    boolean full = deque.size() > 1 || batch.isFull();
                    boolean expired = waitedTimeMs >= timeToWaitMs;
                    boolean sendable = full || expired || exhausted || closed || flushInProgress();
                    if (sendable && !backingOff) {   // @5
                        readyNodes.add(leader);
                    } else {
                        long timeLeftMs = Math.max(timeToWaitMs - waitedTimeMs, 0);
                        // Note that this results in a conservative estimate since an un-sendable partition may have
                        // a leader that will later be found to have sendable data. However, this is good enough
                        // since we'll just wake up and then sleep again for the remaining time.
                        nextReadyCheckDelayMs = Math.min(timeLeftMs, nextReadyCheckDelayMs);   
                    }
                }
            }
        }
    }
    return new ReadyCheckResult(readyNodes, nextReadyCheckDelayMs, unknownLeaderTopics);
}
复制代码

코드 @ 1 : 생산자 캐시 ConcurrentHashMap의 <TopicPartition, Deque와 <ProducerBatch >> 일괄 통과, 좋은 뉴스 배치를 선택하고 선택 할 준비가. 코드 @ 2 : 존재하지 않는 경우 주제는 unknownLeaderTopics에 추가 될 때, 생산자에서 리더 메타 데이터 캐시 정보의 파티션 (TopicPartition)을 찾아보십시오 (코드 @ 3), 나중에 브로커 터미널을 찾기 위해 메타 데이터를 업데이트 요청을 보냅니다 정보 파티션을 라우팅. 코드 @ 4 : 당신이 조건이 만족이 시간이 걱정하지되는 순서에 대한 메시지를 isMuted 여부를 결정하기 위해 필요에없는 readyNodes 할 경우 주문 뒷면의 뉴스 섹션에 초점을 맞출 것이다. 코드 @ 5 : 여기에 준비 상태, 첫 번째는 지역 변수의 의미를 해석 할 것인지 여부를 결정하는 것이다.

  • 긴 대기 되었는가 ProducerBatch 긴 waitedTimeMs는, 현재 타임 스탬프와 ProducerBatch lastAttemptMs 사이의 차이와 동일한, 현재 시간 ProducerBatch 만들거나 시도 lastAttemptMs 시간에 할당한다.
  • retryBackoffMs 이상이 재 시도하기 전에 대기 시간을 발생하면 시작, 기본값이 100ms이며, 속성 retry.backoff.ms를 통해 구성 할 수 있습니다.
  • 현재 재시도 batch.attempts () 배치 번호.
  • 원하는 재시도 지연보다 적은 retryBackoffMs에서, backingOff 일괄 준비 아니라고 수단 진정한 = 경우 backingOff 배경 송신, 즉, 턴 오프된다.
  • timeToWaitMs 스레드 대기 시간이 메시지를 보낼 필요 보내 backingOff가 true의 경우, 그것은 배치가 재 시도에 있음을 나타냅니다 및 대기 시간이 경우 timeToWaitMs = retryBackoffMs에, 적은 시스템에 의해 대기 시간 설정보다. 그렇지 않으면, 시간은 lingerMs을 기다리는.
  • 만족하는 임의의 두 조건에 해당 한 경우 배치, 전체인지 전체 부울입니다.
    • 양단 <ProducerBatch> 큐의 개수가 1보다 크면, 확실하게 채워져 ProducerBatch를 나타낸다.
    • ProducerBatch 가득하고.
  • 부울이 만료 만료, 대기 시간과 동일하고, 요구 사항을 수행 할 수 있다는 점에 도달 한 타이머를 트리거 할 사실 만료 정기적으로 보내 보내 것처럼, 대기 시간보다 큰했다.
  • 차단 된 스레드 응용 프로그램 캐시 공간은 새 ProducerBatch을 만들 때, 그것은 즉시 메시지 캐시가 즉시 서버로 전송한다, 캐시가 충분하지 않습니다 0보다 큰 부울 소모 전류를 생산한다.
  • 있는지 여부를 sendable을 보낼 수 있습니다. 어느 중 어느 하나에 만족 다음과 같은 조건을 :
    • 배치가 작성되었습니다. (전체 사실 =).
    • 때 시스템은 소정의 길이 기다리고있다. (만료 = TRUE)
    • 발신자 내부 버퍼가 소모되고 (= 참 소모) 새로운 쓰레드를 적용 할 필요가있다.
    • 닫기 방법은 송신기 (근접 = TRUE)라고한다.
    • 보낸 사람 플러시 메서드가 호출됩니다.

방법을 상세 2.2 RecordAccumulator 드레인

RecordAccumulator 번호 유출

public Map<Integer, List<ProducerBatch>> drain(Cluster cluster, Set<Node> nodes, int maxSize, long now) { // @1
    if (nodes.isEmpty())
        return Collections.emptyMap();

    Map<Integer, List<ProducerBatch>> batches = new HashMap<>();
    for (Node node : nodes) {                                                                                                                              
        List<ProducerBatch> ready = drainBatchesForOneNode(cluster, node, maxSize, now);                      // @2
        batches.put(node.id(), ready);
    }
    return batches;
}
复制代码

코드 @ 1 : 우리는 먼저 메소드의 매개 변수를 소개 :

  • 클러스터 클러스터 클러스터 정보.
  • 설정 <노드> 노드 유무 노드 세트를 준비했다.
  • INT이 maxSize는 바이트의 최대 번호를 요청합니다.
  • 긴 지금 현재 시간.

코드 @ 2 : 모든 노드를 탐색, 데이터는지도 <정수 / ** brokerId * / 목록 <ProducerBatch >> 일괄 조립 drainBatchesForOneNode 추출 메소드를 호출합니다.

다음으로, drainBatchesForOneNode의 초점 봐. RecordAccumulator # drainBatchesForOneNode

private List<ProducerBatch> drainBatchesForOneNode(Cluster cluster, Node node, int maxSize, long now) {
    int size = 0;
    List<PartitionInfo> parts = cluster.partitionsForNode(node.id());   // @1
    List<ProducerBatch> ready = new ArrayList<>();
    int start = drainIndex = drainIndex % parts.size();                        // @2
    do {                                                                                                // @3 
        PartitionInfo part = parts.get(drainIndex);
        TopicPartition tp = new TopicPartition(part.topic(), part.partition()); 
        this.drainIndex = (this.drainIndex + 1) % parts.size();                     
            
        if (isMuted(tp, now))
            continue;

        Deque<ProducerBatch> deque = getDeque(tp);                              // @4
        if (deque == null)
            continue;

        synchronized (deque) {
            // invariant: !isMuted(tp,now) && deque != null
            ProducerBatch first = deque.peekFirst();                                         // @5
            if (first == null)
                continue;

            // first != null
            boolean backoff = first.attempts() > 0 && first.waitedTimeMs(now) < retryBackoffMs;   // @6
            // Only drain the batch if it is not during backoff period.
            if (backoff)                                                                                     
                continue;

            if (size + first.estimatedSizeInBytes() > maxSize && !ready.isEmpty()) {     // @7
                break;
            } else {
                if (shouldStopDrainBatchesForPartition(first, tp))                                  
                    break;

                // 这里省略与事务消息相关的代码,后续会重点学习。
                batch.close();                                                                                            // @8
                size += batch.records().sizeInBytes();
                ready.add(batch);                                                                            

                batch.drained(now);                                                                             
            }
        }
    } while (start != drainIndex);
    return ready;
}
复制代码

코드 @ 1 : brokerId은 기본 브로커의 모든 파티션에 따라 얻을. 코드 @ 2 : 초기화 시작합니다. 먼저, 여기에서 시작 drainIndex에 정교한 수 있습니다.

  • 파티션 번호를 통과하는 현재의 시작을 시작합니다.
  • 최종 추출 drainIndex 큐 인덱스 후, 여기에 제로 분할 개시 추출에서 각 대기열 번호에 주로있다.

코드 @ 3 : 버퍼에서 대응하는 파티션에 축적 추출주기 데이터. 코드 @ 4 : 축적 된 덱 인수 생산자의 전송 버퍼에서 주제 + 파티션 번호. 코드 @ 5 : 헤드 양단 큐에서 하나 개의 요소를 가져옵니다. (추가 된 메시지가 큐의 말미에 부가된다). 코드 @ 6 : 현재 재 시도가 차단 시간에 배치하지 인 경우, 파티션은 생략됩니다. 코드 @ 7 : 현재 메시지가 그려 플러스 새 메시지의 전체 크기가 maxRequestSize, 추출의 끝 이상이 된 경우. 8 코드 @ : 배치 현재 세트에 추가된다, 즉이 배치가 메시지에 추가 될 수없는, 근접 배치를 제조하고있다.

여기에서 소개 메시지에 대한, NetworkClient 내부의 여론 조사 방법은 네트워크의 구체적인 실현의 뒷면에 후속 기사에서 별도로 표시됩니다, 네트워크를 통해 브로커 서버에 선택한 이벤트를 수행 할 준비가 선택기, 추출 메시지를 호출합니다.


저자 : 땡 웨이는 "RocketMQ 기술 내부자"저자, RocketMQ 커뮤니티 목사, 공개 번호 : 미들웨어이자 원 수비수는 자바 소스 코드를 분석, 자바와 계약 (JUC) 인 Netty, Mycat의 집합 게시 된 , 두보를 RocketMQ, MyBatis로 다른 소스 컬럼. 가입 할 수있는 링크를 클릭 행성의 미들웨어 지식을 높은 동시성, 분산 서비스 아키텍처, AC 소스를 탐험.

그림 삽입 설명 여기

추천

출처juejin.im/post/5decfcf7f265da33c028a32e