kafka java 客户端发送请求,大量使用 RequestFuture,因此先说明下该类。

RequestFuture 类的成员属性 listeners 是 RequestFutureListener 的集合,调用 complete 方法,会触发 listener 的 onSuccess 方法。

  1. public void complete(T value) {
  2. try {
  3. if (value instanceof RuntimeException)
  4. throw new IllegalArgumentException("The argument to complete can not be an instance of RuntimeException");
  5.  
  6. if (!result.compareAndSet(INCOMPLETE_SENTINEL, value))
  7. throw new IllegalStateException("Invalid attempt to complete a request future which is already complete");
  8. fireSuccess();
  9. } finally {
  10. completedLatch.countDown();
  11. }
  12. }
  13.  
  14. private void fireSuccess() {
  15. T value = value();
  16. while (true) {
  17. RequestFutureListener<T> listener = listeners.poll();
  18. if (listener == null)
  19. break;
  20. listener.onSuccess(value);
  21. }
  22. }

值得关注的是 compose 和 chain 方法,这两个方法均是为当前 RequestFuture 添加 listener,listener 的 onSuccess 又是调用另一个 RequestFuture 的方法。

  1. public <S> RequestFuture<S> compose(final RequestFutureAdapter<T, S> adapter) {
  2. // 创建新的 RequestFuture 对象
  3. final RequestFuture<S> adapted = new RequestFuture<>();
  4. // 为旧的 RequestFuture 添加 listener
  5. addListener(new RequestFutureListener<T>() {
  6. @Override
  7. public void onSuccess(T value) {
  8. adapter.onSuccess(value, adapted);
  9. }
  10.  
  11. @Override
  12. public void onFailure(RuntimeException e) {
  13. adapter.onFailure(e, adapted);
  14. }
  15. });
  16. // 返回新的 RequestFuture 对象
  17. return adapted;
  18. }
  19.  
  20. public void chain(final RequestFuture<T> future) {
  21. // 为当前 RequestFuture 添加 listener
  22. addListener(new RequestFutureListener<T>() {
  23. @Override
  24. public void onSuccess(T value) {
  25. future.complete(value);
  26. }
  27.  
  28. @Override
  29. public void onFailure(RuntimeException e) {
  30. future.raise(e);
  31. }
  32. });
  33. }

rebalance 入口在 ConsumerCoordinator#poll

客户端判断是否需要重新加入组,即 rebalance

  1. //ConsumerCoordinator#needRejoin
  2. public boolean needRejoin() {
  3. if (!subscriptions.partitionsAutoAssigned())
  4. return false;
  5.  
  6. // 所订阅 topic 的分区数量发生变化
  7. // we need to rejoin if we performed the assignment and metadata has changed
  8. if (assignmentSnapshot != null && !assignmentSnapshot.equals(metadataSnapshot))
  9. return true;
  10.  
  11. // 所订阅的 topic 发生变化
  12. // we need to join if our subscription has changed since the last join
  13. if (joinedSubscription != null && !joinedSubscription.equals(subscriptions.subscription()))
  14. return true;
  15.  
  16. // 消费者加入组,或退出组,由心跳线程设置 rejoinNeeded = true
  17. return super.needRejoin();
  18. }

消费者开始 rebalance

  1. // AbstractCoordinator#joinGroupIfNeeded
  2. void joinGroupIfNeeded() {
  3. while (needRejoin() || rejoinIncomplete()) {
  4. ensureCoordinatorReady();
  5.  
  6. if (needsJoinPrepare) {
  7. // 调用用户传入的 ConsumerRebalanceListener
  8. onJoinPrepare(generation.generationId, generation.memberId);
  9. needsJoinPrepare = false;
  10. }
  11.  
  12. // 发送 join group 的请求
  13. RequestFuture<ByteBuffer> future = initiateJoinGroup();
  14. client.poll(future);
  15.  
  16. if (future.succeeded()) {
  17. onJoinComplete(generation.generationId, generation.memberId, generation.protocol, future.value());
  18.  
  19. resetJoinGroupFuture();
  20. needsJoinPrepare = true;
  21. } else {
  22. resetJoinGroupFuture();
  23. RuntimeException exception = future.exception();
  24. if (exception instanceof UnknownMemberIdException ||
  25. exception instanceof RebalanceInProgressException ||
  26. exception instanceof IllegalGenerationException)
  27. continue;
  28. else if (!future.isRetriable())
  29. throw exception;
  30. time.sleep(retryBackoffMs);
  31. }
  32. }
  33. }

AbstractCoordinator#initiateJoinGroup

  1. private synchronized RequestFuture<ByteBuffer> initiateJoinGroup() {
  2. if (joinFuture == null) {
  3. disableHeartbeatThread();
  4.  
  5. state = MemberState.REBALANCING;
  6. joinFuture = sendJoinGroupRequest();
  7. joinFuture.addListener(new RequestFutureListener<ByteBuffer>() {
  8. @Override
  9. public void onSuccess(ByteBuffer value) {
  10. // handle join completion in the callback so that the callback will be invoked
  11. // even if the consumer is woken up before finishing the rebalance
  12. synchronized (AbstractCoordinator.this) {
  13. log.info("Successfully joined group with generation {}", generation.generationId);
  14. state = MemberState.STABLE;
  15. rejoinNeeded = false;
  16.  
  17. if (heartbeatThread != null)
  18. heartbeatThread.enable();
  19. }
  20. }
  21.  
  22. @Override
  23. public void onFailure(RuntimeException e) {
  24. // we handle failures below after the request finishes. if the join completes
  25. // after having been woken up, the exception is ignored and we will rejoin
  26. synchronized (AbstractCoordinator.this) {
  27. state = MemberState.UNJOINED;
  28. }
  29. }
  30. });
  31. }
  32. return joinFuture;
  33. }

AbstractCoordinator#sendJoinGroupRequest

  1. private RequestFuture<ByteBuffer> sendJoinGroupRequest() {
  2. if (coordinatorUnknown())
  3. return RequestFuture.coordinatorNotAvailable();
  4.  
  5. // send a join group request to the coordinator
  6. log.info("(Re-)joining group");
  7. JoinGroupRequest.Builder requestBuilder = new JoinGroupRequest.Builder(
  8. groupId,
  9. this.sessionTimeoutMs,
  10. this.generation.memberId,
  11. protocolType(),
  12. metadata()).setRebalanceTimeout(this.rebalanceTimeoutMs);
  13.  
  14. log.debug("Sending JoinGroup ({}) to coordinator {}", requestBuilder, this.coordinator);
  15. return client.send(coordinator, requestBuilder)
  16. .compose(new JoinGroupResponseHandler());
  17. }

重点关注 client.send(coordinator, requestBuilder).compose(new JoinGroupResponseHandler());
为老的 RequestFuture 添加 listener,返回新的 RequestFuture

ConsumerNetworkClient#send

  1. public RequestFuture<ClientResponse> send(Node node, AbstractRequest.Builder<?> requestBuilder) {
  2. long now = time.milliseconds();
  3. // 使用 RequestFutureCompletionHandler 作为回调函数
  4. RequestFutureCompletionHandler completionHandler = new RequestFutureCompletionHandler();
  5. ClientRequest clientRequest = client.newClientRequest(node.idString(), requestBuilder, now, true,
  6. completionHandler);
  7. unsent.put(node, clientRequest);
  8.  
  9. // wakeup the client in case it is blocking in poll so that we can send the queued request
  10. client.wakeup();
  11. return completionHandler.future;
  12. }

JoinGroupResponseHandler#handle

  1. public void handle(JoinGroupResponse joinResponse, RequestFuture<ByteBuffer> future) {
  2. Errors error = joinResponse.error();
  3. if (error == Errors.NONE) {
  4. log.debug("Received successful JoinGroup response: {}", joinResponse);
  5. sensors.joinLatency.record(response.requestLatencyMs());
  6.  
  7. synchronized (AbstractCoordinator.this) {
  8. if (state != MemberState.REBALANCING) {
  9. // if the consumer was woken up before a rebalance completes, we may have already left
  10. // the group. In this case, we do not want to continue with the sync group.
  11. future.raise(new UnjoinedGroupException());
  12. } else {
  13. AbstractCoordinator.this.generation = new Generation(joinResponse.generationId(),
  14. joinResponse.memberId(), joinResponse.groupProtocol());
  15. if (joinResponse.isLeader()) {
  16. onJoinLeader(joinResponse).chain(future);
  17. } else {
  18. onJoinFollower().chain(future);
  19. }
  20. }
  21. }
  22. } else if (error == Errors.COORDINATOR_LOAD_IN_PROGRESS) {
  23. log.debug("Attempt to join group rejected since coordinator {} is loading the group.", coordinator());
  24. // backoff and retry
  25. future.raise(error);
  26. } else if (error == Errors.UNKNOWN_MEMBER_ID) {
  27. // reset the member id and retry immediately
  28. resetGeneration();
  29. log.debug("Attempt to join group failed due to unknown member id.");
  30. future.raise(Errors.UNKNOWN_MEMBER_ID);
  31. } else if (error == Errors.COORDINATOR_NOT_AVAILABLE
  32. || error == Errors.NOT_COORDINATOR) {
  33. // re-discover the coordinator and retry with backoff
  34. markCoordinatorUnknown();
  35. log.debug("Attempt to join group failed due to obsolete coordinator information: {}", error.message());
  36. future.raise(error);
  37. } else if (error == Errors.INCONSISTENT_GROUP_PROTOCOL
  38. || error == Errors.INVALID_SESSION_TIMEOUT
  39. || error == Errors.INVALID_GROUP_ID) {
  40. // log the error and re-throw the exception
  41. log.error("Attempt to join group failed due to fatal error: {}", error.message());
  42. future.raise(error);
  43. } else if (error == Errors.GROUP_AUTHORIZATION_FAILED) {
  44. future.raise(new GroupAuthorizationException(groupId));
  45. } else {
  46. // unexpected error, throw the exception
  47. future.raise(new KafkaException("Unexpected error in join group response: " + error.message()));
  48. }
  49. }

收到响应后,最终的执行流是 RequestFutureCompletionHandler -> JoinGroupResponseHandler#handle

  1. private RequestFuture<ByteBuffer> onJoinLeader(JoinGroupResponse joinResponse) {
  2. try {
  3. // perform the leader synchronization and send back the assignment for the group
  4. Map<String, ByteBuffer> groupAssignment = performAssignment(joinResponse.leaderId(), joinResponse.groupProtocol(),
  5. joinResponse.members());
  6.  
  7. SyncGroupRequest.Builder requestBuilder =
  8. new SyncGroupRequest.Builder(groupId, generation.generationId, generation.memberId, groupAssignment);
  9. log.debug("Sending leader SyncGroup to coordinator {}: {}", this.coordinator, requestBuilder);
  10. return sendSyncGroupRequest(requestBuilder);
  11. } catch (RuntimeException e) {
  12. return RequestFuture.failure(e);
  13. }
  14. }
  15.  
  16. private RequestFuture<ByteBuffer> onJoinFollower() {
  17. // send follower's sync group with an empty assignment
  18. SyncGroupRequest.Builder requestBuilder =
  19. new SyncGroupRequest.Builder(groupId, generation.generationId, generation.memberId,
  20. Collections.<String, ByteBuffer>emptyMap());
  21. log.debug("Sending follower SyncGroup to coordinator {}: {}", this.coordinator, requestBuilder);
  22. return sendSyncGroupRequest(requestBuilder);
  23. }
  24.  
  25. private RequestFuture<ByteBuffer> sendSyncGroupRequest(SyncGroupRequest.Builder requestBuilder) {
  26. if (coordinatorUnknown())
  27. return RequestFuture.coordinatorNotAvailable();
  28. return client.send(coordinator, requestBuilder)
  29. .compose(new SyncGroupResponseHandler());
  30. }

用 RequestFuture 把 JoinGroupResponseHandler 和 SyncGroupResponseHandler 串联起来了

  1. private class SyncGroupResponseHandler extends CoordinatorResponseHandler<SyncGroupResponse, ByteBuffer> {
  2. @Override
  3. public void handle(SyncGroupResponse syncResponse,
  4. RequestFuture<ByteBuffer> future) {
  5. Errors error = syncResponse.error();
  6. if (error == Errors.NONE) {
  7. sensors.syncLatency.record(response.requestLatencyMs());
  8. future.complete(syncResponse.memberAssignment());
  9. } else {
  10. requestRejoin();
  11.  
  12. if (error == Errors.GROUP_AUTHORIZATION_FAILED) {
  13. future.raise(new GroupAuthorizationException(groupId));
  14. } else if (error == Errors.REBALANCE_IN_PROGRESS) {
  15. log.debug("SyncGroup failed because the group began another rebalance");
  16. future.raise(error);
  17. } else if (error == Errors.UNKNOWN_MEMBER_ID
  18. || error == Errors.ILLEGAL_GENERATION) {
  19. log.debug("SyncGroup failed: {}", error.message());
  20. resetGeneration();
  21. future.raise(error);
  22. } else if (error == Errors.COORDINATOR_NOT_AVAILABLE
  23. || error == Errors.NOT_COORDINATOR) {
  24. log.debug("SyncGroup failed: {}", error.message());
  25. markCoordinatorUnknown();
  26. future.raise(error);
  27. } else {
  28. future.raise(new KafkaException("Unexpected error from SyncGroup: " + error.message()));
  29. }
  30. }
  31. }
  32. }

rebalance 过程最后的 listener

  1. joinFuture.addListener(new RequestFutureListener<ByteBuffer>() {
  2. @Override
  3. public void onSuccess(ByteBuffer value) {
  4. // handle join completion in the callback so that the callback will be invoked
  5. // even if the consumer is woken up before finishing the rebalance
  6. synchronized (AbstractCoordinator.this) {
  7. log.info("Successfully joined group with generation {}", generation.generationId);
  8. state = MemberState.STABLE;
  9. rejoinNeeded = false;
  10.  
  11. if (heartbeatThread != null)
  12. heartbeatThread.enable();
  13. }
  14. }
  15.  
  16. @Override
  17. public void onFailure(RuntimeException e) {
  18. // we handle failures below after the request finishes. if the join completes
  19. // after having been woken up, the exception is ignored and we will rejoin
  20. synchronized (AbstractCoordinator.this) {
  21. state = MemberState.UNJOINED;
  22. }
  23. }
  24. });

从消费者看 rebalance的更多相关文章

  1. OpenStack_Swift源代码分析——Ring的rebalance算法源代码具体分析

    1 Command类中的rebalnace方法 在上篇文章中解说了,创建Ring已经为Ring加入设备.在加入设备后须要对Ring进行平衡,平衡 swift-ring-builder object.b ...

  2. RocketMQ 消费者

    本文分析 DefaultMQPushConsumer,异步发送消息,多线程消费的情形. DefaultMQPushConsumerImpl MQClientInstance 一个客户端进程只有一个 M ...

  3. kafka消费者offset存储策略

    由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故 障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢 ...

  4. Kafka Rebalance机制和选举策略总结

    自建博客地址:https://www.bytelife.net,欢迎访问! 本文为博客同步发表文章,为了更好的阅读体验,建议您移步至我的博客 本文作者: Jeffrey 本文链接: https://w ...

  5. Kafka 0.8源码分析—ZookeeperConsumerConnector

    1.HighLevelApi High Level Api是多线程的应用程序,以Topic的Partition数量为中心.消费的规则如下: 一个partition只能被同一个ConsumersGrou ...

  6. RocketMQ之十:RocketMQ消息接收源码

    1. 简介 1.1.接收消息 RebalanceService:均衡消息队列服务,负责通过MQClientInstance分配当前 Consumer 可消费的消息队列( MessageQueue ). ...

  7. Kafka学习笔记(四)—— API使用

    Kafka学习笔记(四)-- API使用 1.Producer API 1.1 消息发送流程 Kafka的Producer发送消息采用的是异步发送的方式.在消息发送的过程中,涉及到了两个线程--mai ...

  8. 【原创】美团二面:聊聊你对 Kafka Consumer 的架构设计

    在上一篇中我们详细聊了关于 Kafka Producer 内部的底层原理设计思想和细节, 本篇我们主要来聊聊 Kafka Consumer 即消费者的内部底层原理设计思想. 1.Consumer之总体 ...

  9. ASM磁盘组扩容流程

    环境:RHEL 6.5 + GI 11.2.0.4 + Oracle 11.2.0.4 1.确认磁盘权限正确 2.图形界面配置 3.启用asmca配置 4.修改磁盘组rebalance power级别 ...

随机推荐

  1. openlayers 添加标记点击弹窗 定位图标闪烁

    环境vue3.0 ,地图为公用组件,将添加图标标记的方法放在公共地图的初始化方法里 同一时间弹窗和定位标识都只有一个,因而我把弹窗和定位标记的dom预先写好放到了页面 //矢量标注样式设置函数,设置i ...

  2. nacos集群搭建

    nacos介绍 Nacos 支持基于 DNS 和基于 RPC 的服务发现(可以作为springcloud的注册中心).动态配置服务(可以做配置中心).动态 DNS 服务. 1.从官网下载nacos压缩 ...

  3. id 显示用户与用户组的信息

    id 显示用户与用户组的信息 1.命令功能 id显示指定用户的用户ID和组ID等信息. 2.语法格式 id  option  username 参数说明 选项 选项说明 -gx 显示用户组ID -G ...

  4. robotframework ride报错 Keyword 'BuiltIn.Log' expected 1 to 5 arguments, got 12.

    错误原因,else和else if使用了小写,必须使用大写才能识别到.

  5. JQuery 处理 微擎传递过去数据

    JQuery 处理 微擎传递过去数据 PS:微擎得到的数据大多数是数组(我们这里处理数组) 将数组使用 json_encode() 函数处理成 JSON 格式 前端在 script 中使用 引号 将变 ...

  6. 使用kettle实现循环

    Kettle使用脚本实现循环(十) https://blog.csdn.net/BushQiang/article/details/90264616 kettle实现循环 https://blog.c ...

  7. 【react学习二】create-react-app 接入antd 并按需加载组件

    1.安装 cnpm i babel-plugin-import --save-dev 2.使用 在根目录下的package.json下的bable中添加相应代码 "babel": ...

  8. mysql——非主键自增

    今天遇到一个问题: 要创建一张表,其中我想将ip和date这两列作为一个复合主键,可以通过如下语句修改表结构: alter table tb_name add primary key (字段1,字段2 ...

  9. 【leetcode】1224. Maximum Equal Frequency

    题目如下: Given an array nums of positive integers, return the longest possible length of an array prefi ...

  10. 对ECMAScript的研究-----------引用

    ECMAScript 新特性与标准提案 一:ES 模块 第一个要介绍的 ES 模块,由于历史上 JavaScript 没有提供模块系统,在远古时期我们常用多个 script 标签将代码进行人工隔离.但 ...