kafka生产者的蓄水池机制

本文详细解析了Kafka生产者的蓄水池机制,包括整体架构、消息收集过程,特别是RecordAccumulator作为消息缓存池的角色。讨论了分区计算、集群信息获取以及消息发送的异步过程,最后提出了对优化的思考。
摘要由CSDN通过智能技术生成

1.1、整体架构图

Kafka还有蓄水池?大家先别急,我们先上一张架构图。

从上面的架构图可以看出,生产的流程主要就是一个producer线程和一个sender线程,它们之间通过BatchQueue来获取数据,它们的关系是一一对应的,所以kafka的生产过程都是异步过程,它的同步和异步指的是接收响应结果的模式是同步阻塞还是异步回调。同步和异步的生产者调用示例如下:

异步生产模式:

 producer.send(new ProducerRecord<>(topic,

                    messageNo,

                    messageStr), new DemoCallBack(startTime, messageNo, messageStr));

同步生产模式:

 producer.send(new ProducerRecord<>(topic,

                        messageNo,

                        messageStr)).get();

同步接收是依据send之后返回Future,再调用Future的get方法进行阻塞等待。下面我们就从producer和sender两个类所对应的流程来进行分析,他们分别是消息收集过程和消息发送过程。本文先介绍消息的收集过程,从上面的架构图我们可以看到这个过程的数据最终是放在BatchQueue,像是将水流入了一个蓄水池的场景,这就是本文称其为”蓄水池”的含义了。

1.2、消息收集过程

消息的收集过程主要涉及到的类有如下:

我们接下来也主要是从这几个类的功能来阐述消息收集的过程。

1.2.1、kafkaProducer字段含义及构造

kafkaProducer类包含的字段含义详见如下注释:

public class KafkaProducer<K, V> implements Producer<K, V> {

    /** clientId 生成器,如果没有明确指定客户端 ID,则使用该字段顺序生成一个 */
    private static final AtomicInteger PRODUCER_CLIENT_ID_SEQUENCE = new AtomicInteger(1);
    /** 生产者唯一标识(对应 client.id 属性配置 ) */
    private String clientId;
    /** 分区选择器(对应 partitioner.class 属性配置),如果未明确指定分区,则基于默认的策略RR为消息选择合适的分区 */
    private final Partitioner partitioner;
    /** 消息的最大长度(对应 max.request.size 配置,包含消息头、序列化之后的 key 和 value) */
    private final int maxRequestSize;
    /** 发送单条消息的缓冲区大小(对应 buffer.memory 配置) */
    private final long totalMemorySize;
    /** kafka 集群元数据 */
    private final Metadata metadata;
    /** 消息收集器,用于收集并缓存消息,等待 Sender 线程的发送 */
    private final RecordAccumulator accumulator;
    /** 消息发送线程对象 */
    private final Sender sender;
    /** 消息发送线程,Sender由此线程启动 */
    private final Thread ioThread;
    /** 压缩算法(对应 compression.type 配置) */
    private final CompressionType compressionType;
    /** 时间戳工具 */
    private final Time time;
    /** key 序列化器(对应 key.serializer 配置) */
    private final Serializer<K> keySerializer;
    /** value 序列化器(对应 value.serializer 配置) */
    private final Serializer<V> valueSerializer;
    /** 封装配置信息 */
    private final ProducerConfig producerConfig;
    /** 等待更新 kafka 集群元数据的最大时长 */
    private final long maxBlockTimeMs;
    /** 消息发送的超时时间(从发送到收到 ACK 响应) */
    private final int requestTimeoutMs;
    /** 发送拦截器(对应 interceptor.classes 配置),用于待发送的消息进行拦截并修改,也可以对 ACK 响应进行拦截处理 */
    private final ProducerInterceptors<K, V> interceptors;
	/** kafka定义的版本编号,现在为止有3个,分别为v0: kafka<0.10.0  v1:0.10.0<=kakfa<0.11.0 v2:kafka >=0.11.0 **/
	private final ApiVersions apiVersions;
	/** 生产者的事务管理器 **/
    private final TransactionManager transactionManager;

    // ... 省略方法定义

}
了解完kafkaProducer的字段含义,我们接下来看下kafkaProducer的构造过程:
 KafkaProducer(ProducerConfig config,
                  Serializer<K> keySerializer,
                  Serializer<V> valueSerializer,
                  Metadata metadata,
                  KafkaClient kafkaClient) {
        try {
			//获取用户配置信息
            Map<String, Object> userProvidedConfigs = config.originals();
            this.producerConfig = config;
            this.time = Time.SYSTEM;
			//生产者id的生成,优先使用用户配置的id,如果没有则使用PRODUCER_CLIENT_ID_SEQUENCE递增生成一个序列号
            String clientId = config.getString(ProducerConfig.CLIENT_ID_CONFIG);
            if (clientId.length() <= 0)
                clientId = "producer-" + PRODUCER_CLIENT_ID_SEQUENCE.getAndIncrement();
            this.clientId = clientId;

            //省略度量打点及日志相关信息

			//获取用户配置的分区、序列化的自定义类,并实例化
            this.partitioner = config.getConfiguredInstance(ProducerConfig.PARTITIONER_CLASS_CONFIG, Partitioner.class);
            long retryBackoffMs = config.getLong(ProducerConfig.RETRY_BACKOFF_MS_CONFIG);
            if (keySerializer == null) {
                this.keySerializer = ensureExtended(config.getConfiguredInstance(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                                                                                         Serializer.class));
                this.keySerializer.configure(config.originals(), true);
            } else {
                config.ignore(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG);
                this.keySerializer = ensureExtended(keySerializer);
            }
            if (valueSerializer == null) {
                this.valueSerializer = ensureExtended(config.getConfiguredInstance(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                                                                                           Serializer.class));
                this.valueSerializer.configure(config.originals(), false);
            } else {
                config.ignore(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG);
                this.valueSerializer = ensureExtended(valueSerializer);
            }

            // load interceptors and make sure they get clientId
            userProvidedConfigs.put(ProducerConfig.CLIENT_ID_CONFIG, clientId);
			//获取用户自定义的拦截器列表
            List<ProducerInterceptor<K, V>> interceptorList = (List) (new ProducerConfig(userProvidedConfigs, false)).getConfiguredInstances(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,
                    ProducerInterceptor.class);
            this.interceptors = new ProducerInterceptors<>(interceptorList);
            ClusterResourceListeners clusterResourceListeners = configureClusterResourceListe
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值