我们先来看一下哪些情况会发生Rebalance操作:
1、有新的消费者加入ConsumerGroup
2、有消费者宕机下线。
3、有消费者主动退出Consumer Group
4、Consumer Group订阅的任一Topic出现分区数量的变化
5、消费者调用unsubscribe取消对某Topic的订阅
下面对Reblance操作的具体实现进行分析
第一阶段
Rebalance操作的第一步就是查找GroupCoordinator,这个阶段消费者会向Kafka集群中的任意一个Broker发送GroupCoordinatorRequest请求,并处理返回的GroupCoordinatorResponse,
1、首先检测是否需要重新查找GroupCoordinator,主要是检查coordinator字段是否为空以及与GroupCoordinator之间的连接是否正常。
public boolean coordinatorUnknown() {
//检查coordinator字段是否为空
if (coordinator == null)
return true;
//检测连接是否正常
if (client.connectionFailed(coordinator)) {
//将unsent集合中对应的请求清空并将coordinator字段设置为null
coordinatorDead();
return true;
}
return false;
}
2、查找集群负载最低的Node节点,并创建GroupCoordinatorRequest请求。调用client.send方法将请求放入unsent队列中等待发送,并返回RequestFuture对象。
3、调用ConsumerNetworkClient.pool方法,将GroupCoordinatorRequest请求发送出去。
4、检测检查RequestFuture对象的状态。如果出现RetriableException异常,则调用ConsuemerNetworkClient.awaitMetadataUpdate()方法阻塞更新Metadata中的记录的集群元数据后跳转到步骤一
5、如果成功找到GroupCoordinator节点,但是网络连接失败,则将其unsent中对应的请求清空,并将coordinator字段置为1,退避一段时间后跳转到步骤1执行
下面我们就进入源码看一下这个过程:
public void ensureCoordinatorReady() {
//把是否需要查找GroupCoordinator作为循环条件
while (coordinatorUnknown()) {
//将GroupCoordinatorRequest放到unsent队列里面等待发送
RequestFuture<Void> future = sendGroupCoordinatorRequest();
//阻塞获取response
client.poll(future);
//判断是否串异常
if (future.failed()) {
//如果是这个异常,那么阻塞等待metadata更新
if (future.isRetriable())
client.awaitMetadataUpdate();
else
throw future.exception();
//如果获取失败,那么清空coordinator,等待一段时间再去请求
} else if (coordinator != null && client.connectionFailed(coordinator)) {
// we found the coordinator, but the connection has failed, so mark
// it dead and backoff before retrying discovery
coordinatorDead();
time.sleep(retryBackoffMs);
}
}
}
下面我们看里面的方法的具体实现:
private RequestFuture<Void> sendGroupCoordinatorRequest() {
// 找到负载最小的节点
Node node = this.client.leastLoadedNode();
if (node == null) {
//如果没有找到返回一个异常结束
return RequestFuture.noBrokersAvailable();
} else {
// 常见要给request
GroupCoordinatorRequest metadataRequest = new GroupCoordinatorRequest(this.groupId);
//调用ConsumerNetworkClient.send添加到unsent集合中
return client.send(node, ApiKeys.GROUP_COORDINATOR, metadataRequest)
.compose(new RequestFutureAdapter<ClientResponse, Void>() {
@Override
public void onSuccess(ClientResponse response, RequestFuture<Void> future) {
handleGroupMetadataResponse(response, future);
}
});
}
}
服务端会根据发出的请求返回要给Response,当接收到Response的时候需要对它进行处理:
1、调用coordinatorUnknown检测是否已经找到GroupCoordinator且成功连接。如果是则忽略此Response,因为会有重发机制
2、解析Response来解析GroupCoordinator
3、构建Node对象赋值给coordinator字段,并尝试与GroupCoordinator建立连接。
4、启动HeartbeatTask定时任务
5、最后调用RequestFuture.complete方法将正常收到的GroupCoordinatorResponse的事件传播出去
6、如果收到的Response中的错误码不为NONE,则将异常事件传播出去
private void handleGroupMetadataResponse(ClientResponse resp, RequestFuture<Void> future) {
log