kafka消费者(五):heartbeat

消费者定期向服务端的GgroupCoordinator发送HeartbeatRequest来确定彼此在线。首先介绍心跳请求的格式,比较简单,依次包含group_id(String)、group_generation_id、member_id(String)三个字段。HeartbeatResponse则只包含一个short类型的error_code。
HeartbeatTask是一个实现DelayedTask接口的定时任务,负责定时发送HeartbeatRequest并处理其响应。

public void run(final long now) {
    if (generation < 0 || needRejoin() || coordinatorUnknown()) {
        // 判断是否需要发送心跳
        //1. GroupCoordinator已经确认并且已经连接
        //2. 不处于正在等在Partition分配结果的状态
        //3. 之前的心跳正常收到响应,并没有过期
        return;
    }

    if (heartbeat.sessionTimeoutExpired(now)) {
        // 检测心跳回应是否超时,如果超时,就认为GroupCoordinator宕机,调用coordinatorDead()
        coordinatorDead();
        return;
    }

    if (!heartbeat.shouldHeartbeat(now)) {
        // 检测心跳是否到期,如果没有到期则把heartbeatTask对象重新添加到DelayTaskQueue中,等到其后期执行。如果已经到期则发送Heartbeat请求
        client.schedule(this, now + heartbeat.timeToNextHeartbeat(now));
    } else {
        //更新最近一次发送Heartbeat请求的时间,把requestInFlight设置为true,表示有未响应的心跳请求,防止重复发送。
        heartbeat.sentHeartbeat(now);
        requestInFlight = true;
        //创建并缓存心跳请求,调用ConsumerNetworkClient.send()方法,把请求放入unsent集合中缓存。
        RequestFuture<Void> future = sendHeartbeatRequest();
        //添加监听器
        future.addListener(new RequestFutureListener<Void>() {
            @Override
            public void onSuccess(Void value) {
                requestInFlight = false;
                long now = time.milliseconds();
                //设置heartbeat.lastHeartbeatReceive=now
                heartbeat.receiveHeartbeat(now);
                long nextHeartbeatTime = now + heartbeat.timeToNextHeartbeat(now);
                client.schedule(HeartbeatTask.this, nextHeartbeatTime);
            }

            @Override
            public void onFailure(RuntimeException e) {
                requestInFlight = false;
                client.schedule(HeartbeatTask.this, time.milliseconds() + retryBackoffMs);
            }
        });
    }
}

protected void coordinatorDead() {
    if (this.coordinator != null) {
        log.info("Marking the coordinator {} dead for group {}", this.coordinator, groupId);
        //把unsent中缓存要发送给Coordinator节点的请求全部清空,并标记异常
        client.failUnsentRequests(this.coordinator, GroupCoordinatorNotAvailableException.INSTANCE);
        this.coordinator = null;
    }
}

发送心跳

public RequestFuture<Void> sendHeartbeatRequest() {
    //创建HeartbeatResponse
    HeartbeatRequest req = new HeartbeatRequest(this.groupId, this.generation, this.memberId);
    //使用HeartbeatCompletionHandler对RequestFuture<ClientResponse>进行适配
    return client.send(coordinator, ApiKeys.HEARTBEAT, req)
            .compose(new HeartbeatCompletionHandler());
}

对心跳进行解析

protected abstract class CoordinatorResponseHandler<R, T>
        extends RequestFutureAdapter<ClientResponse, T> {
    //待处理的响应
    protected ClientResponse response;
    //parse()和handle()两个抽象方法
    public abstract R parse(ClientResponse response);

    public abstract void handle(R response, RequestFuture<T> future);

    @Override
    public void onFailure(RuntimeException e, RequestFuture<T> future) {
        // mark the coordinator as dead
        if (e instanceof DisconnectException)
            coordinatorDead();
        //调用adapted对象的raise方法
        future.raise(e);
    }

    @Override
    public void onSuccess(ClientResponse clientResponse, RequestFuture<T> future) {
        try {
            this.response = clientResponse;
            //解析ClientResponse,handle
            R responseObj = parse(clientResponse);
            handle(responseObj, future);
        } catch (RuntimeException e) {
            if (!future.isDone())
                future.raise(e);
        }
    }
}

处理心跳响应

private class HeartbeatCompletionHandler extends CoordinatorResponseHandler<HeartbeatResponse, Void> {
    @Override
    public HeartbeatResponse parse(ClientResponse response) {
        return new HeartbeatResponse(response.responseBody());
    }

    @Override
    public void handle(HeartbeatResponse heartbeatResponse, RequestFuture<Void> future) {
        sensors.heartbeatLatency.record(response.requestLatencyMs());
        //解析错误码,ILLEGAL_GENERATION
        Errors error = Errors.forCode(heartbeatResponse.errorCode());
        if (error == Errors.NONE) {
            log.debug("Received successful heartbeat response for group {}", groupId);
            future.complete(null);
        } else if (error == Errors.GROUP_COORDINATOR_NOT_AVAILABLE
                || error == Errors.NOT_COORDINATOR_FOR_GROUP) {
            log.debug("Attempt to heart beat failed for group {} since coordinator {} is either not started or not valid.",
                    groupId, coordinator);
            coordinatorDead();
            future.raise(error);
        } else if (error == Errors.REBALANCE_IN_PROGRESS) {
            log.debug("Attempt to heart beat failed for group {} since it is rebalancing.", groupId);
            AbstractCoordinator.this.rejoinNeeded = true;
            future.raise(Errors.REBALANCE_IN_PROGRESS);
        } else if (error == Errors.ILLEGAL_GENERATION) {
            //generationID过期,GroupCoordinator已经开始了新的rebalance操作,会把rejoinNeeded设置为true,重新加入消费者组。
            log.debug("Attempt to heart beat failed for group {} since generation id is not legal.", groupId);
            AbstractCoordinator.this.rejoinNeeded = true;
            future.raise(Errors.ILLEGAL_GENERATION);
        } else if (error == Errors.UNKNOWN_MEMBER_ID) {
            //Coordinator识别不了Consumer,就清空memberId,重新加入消费者组
            log.debug("Attempt to heart beat failed for group {} since member id is not valid.", groupId);
            memberId = JoinGroupRequest.UNKNOWN_MEMBER_ID;
            AbstractCoordinator.this.rejoinNeeded = true;
            future.raise(Errors.UNKNOWN_MEMBER_ID);
        } else if (error == Errors.GROUP_AUTHORIZATION_FAILED) {
            future.raise(new GroupAuthorizationException(groupId));
        } else {
            future.raise(new KafkaException("Unexpected error in heartbeat response: " + error.message()));
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值