Kafka源码之Sender分析

本文详细解析了Kafka Sender发送消息的流程,包括从Metadata获取元数据、RecordAccumulator的缓存处理、网络I/O操作以及Metadata更新机制。重点介绍了创建ClientRequest的过程、KSelector的网络管理以及MetadataUpdater在更新元数据中的作用。
摘要由CSDN通过智能技术生成

我们先来介绍一下Sender发送消息的整个流程:首先根据RecordAccumulator的缓存情况,利用ready筛选出可以向哪些节点发送消息,然后根据生产者和各个节点的连接爱你概况,过滤Node节点,之后,生成相应的请求,这里要特别注意的是每个Node节点只生成一个请求,最后,调用NetWorkClient将请求发送出去。
在这里插入图片描述

1、从Metadata获取Kafka集群元数据
2、调用RecordAccumulator.ready方法,根据缓存情况返回可以向哪些Node节点发送信息,返回ReadyCheckResult对象
3、如果ReadyCheckResult中表示有unknownLeadersExist,则调用Metadata的requestUpdate方法,标记需要更新Kafka的集群消息
4、针对ReadyCheckResult的readyNodes集合,循环调用NetworkClient的ready方法,检查网络I/O方面是否符合发送消息的条件,不符合条件的Node将会从readyNodes集合里面删除
5、针对经过第4步处理的readyNodes集合,调用drain方法获取待发送的消息集合
6、调用abortExpireBatches方法处理超时的消息。遍历RecordAccumulator中保存的全部RecordBatch,调用mybeExpire方法进行处理。如果已超时,则调用done方法,其中会触发callback,并将RecordBatch从队列中移除,释放ByteBuffer空间
7、调用Sender.createProduceRequests方法将待发送的消息封装成ClientRequest
8、调用NetWorkClient.send方法,将ClientRequest写入KafkaChannel的send字段。
9、调用NetWorkClient.poll方法,将KafkaChannel.send字段中保存的ClientRequest发送出去,同时,还会处理服务端发回的响应、处理超时的请求、调用用户自定义Callback等
下面我们将对上面的步骤逐个分析
创建请求

private List<ClientRequest> createProduceRequests(Map<Integer, List<RecordBatch>> collated, long now) {
		//用来存储创建的ClientRequest
        List<ClientRequest> requests = new ArrayList<ClientRequest>(collated.size());
        //遍历,将collated中的每项都封装成ClientRequest
        for (Map.Entry<Integer, List<RecordBatch>> entry : collated.entrySet())
            requests.add(produceRequest(now, entry.getKey(), acks, requestTimeout, entry.getValue()));
        return requests;
    }
private ClientRequest produceRequest(long now, int destination, short acks, int timeout, List<RecordBatch> batches) {
		//用来存储每个分区的数据
        Map<TopicPartition, ByteBuffer> produceRecordsByPartition = new HashMap<TopicPartition, ByteBuffer>(batches.size());
        final Map<TopicPartition, RecordBatch> recordsByPartition = new HashMap<TopicPartition, RecordBatch>(batches.size());
        //遍历当前node上的需要发送的所有的数据
        for (RecordBatch batch : batches) {
        	//获取每项的分区
            TopicPartition tp = batch.topicPartition;
            //分别添加到两个Map中
            produceRecordsByPartition.put(tp, batch.records.buffer());
            recordsByPartition.put(tp, batch);
        }
        //将要发送到这node上的数据封装成一个request
        ProduceRequest request = new ProduceRequest(acks, timeout, produceRecordsByPartition);
        //创建RequestSend 
        RequestSend send = new RequestSend(Integer.toString(destination),
                                           this.client.nextRequestHeader(ApiKeys.PRODUCE),
                                           request.toStruct());
        //创建一个回调对象
        RequestCompletionHandler callback = new RequestCompletionHandler() {
            public void onComplete(ClientResponse response) {
                handleProduceResponse(response, recordsByPartition, time.milliseconds());
            }
        };
		//创建ClientRequest
        return new ClientRequest(now, acks != 0, send, callback);
    }

我们简单总结一下创建ClientRequest的流程:
1、将一个NodeId对应的RecordBatch集合,重新整理为produceRecordsByPartition和recordsByPartition两个集合
2、创建RequestSend和ProduceRequest
3、创建回调对象
4、创建并返回ClientRequest
在之后的流程中,发送的是RequestSend,会将ClientRequest放到缓存中,当请求收到响应或发现异常的时候,会通过缓存的ClientRequest调用其RequestCompletionHandler对象。
KSelector
KSelector使用NIO异步非阻塞模式实现网络I/O操作,KSelector使用一个单独的线程可以管理多条网络连接上的连接、读、写等操作。下面来看一下它的和新方法:

public void connect(String 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值