Kafka producer initialization

1. Set partitioner
2. Set serializer
3. Set interceptor
4. Create cluster Metadata and update regularly
5. Set compression format
6. Create RecordAccumulator
7. Create NetworkClient
8. Create Sender thread and start

producer = new KafkaProducer<>(props);
-> 
 //1、设置分区器
 this.partitioner = config.getConfiguredInstance(ProducerConfig.PARTITIONER_CLASS_CONFIG, Partitioner.class);
 //重试时间 retry.backoff.ms 默认100ms
 long retryBackoffMs = config.getLong(ProducerConfig.RETRY_BACKOFF_MS_CONFIG);
 //2、设置序列化器
     ...
 //3、设置拦截器
 List<ProducerInterceptor<K, V>> interceptorList = (List) (new   ProducerConfig(userProvidedConfigs)).getConfiguredInstances(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,
                    ProducerInterceptor.class);
            this.interceptors = interceptorList.isEmpty() ? null : new ProducerInterceptors<>(interceptorList);

 /**4、生产者从服务端那儿拉取过来的kafka的元数据。
 //生产者要想去拉取元数据, 发送网络请求,重试,
 //metadata.max.age.ms(默认5分钟)
 //生产者每隔一段时间都要去更新一下集群的元数据。*/
 this.metadata = new Metadata(retryBackoffMs, config.getLong(ProducerConfig.METADATA_MAX_AGE_CONFIG), true, clusterResourceListeners);

 /**max.request.size 生产者往服务端发送消息的时候,规定一条消息最大多大?
  //如果你超过了这个规定消息的大小,你的消息就不能发送过去。
  //默认是1M,这个值偏小,在生产环境中,我们需要修改这个值。
  //经验值是10M。但是大家也可以根据自己公司的情况来。*/
  this.maxRequestSize = config.getInt(ProducerConfig.MAX_REQUEST_SIZE_CONFIG);
  
   //指的是缓存大小 RecordAccumulator 大小
   //buffer.memory 默认值是32M,这个值一般是够用,如果有特殊情况的时候,我们可以去修改这个值。
   this.totalMemorySize = config.getLong(ProducerConfig.BUFFER_MEMORY_CONFIG);

/**5、kafka是支持压缩数据的,这儿设置压缩格式。
//提高你的系统的吞吐量,你可以设置压缩格式。
//一次发送出去的消息就更多。生产者这儿会消耗更多的cpu.*/
this.compressionType = CompressionType.forName(config.getString(ProducerConfig.COMPRESSION_TYPE_CONFIG));

//TODO 6、 创建了一个核心的组件 RecordAccumulator
 this.accumulator = new RecordAccumulator(config.getInt(ProducerConfig.BATCH_SIZE_CONFIG),
         this.totalMemorySize,
         this.compressionType,
         config.getLong(ProducerConfig.LINGER_MS_CONFIG),
         retryBackoffMs,
         metrics,
         time); 
                    
/**去更新元数据  同4
//addresses 这个地址其实就是我们写producer代码的时候,传参数的时候,传进去了一个broker的地址。
//所以这段代码看起来像是去服务端拉取元数据,所以我们去验证一下,是否真的去拉取元数据。
//TODO update方法初始化的时候并没有去服务端拉取元数据。*/
this.metadata.update(Cluster.bootstrap(addresses), time.milliseconds());
ChannelBuilder channelBuilder = ClientUtils.createChannelBuilder(config.values());

/**
 * TODO 7、 初始化了一个重要的管理网路的组件 NetworkClient。
  *  (1)connections.max.idle.ms: 默认值是9分钟
  *      一个网络连接最多空闲多久,超过这个空闲时间,就关闭这个网络连接。
  *
  *  (2)max.in.flight.requests.per.connection:默认是5
  *    producer向broker发送数据的时候,其实是有多个网络连接。
  *    每个网络连接可以忍受 producer端发送给broker 消息然后消息没有响应的个数。
  *
  *    因为kafka有重试机制,所以有可能会造成数据乱序,如果想要保证有序,这个值要把设置为1.
  *    相当于一条一条的发送,每条发送成功并返回再发别的消息
  *
  * (3)send.buffer.bytes:socket发送数据的缓冲区的大小,默认值是128K
  * (4)receive.buffer.bytes:socket接受数据的缓冲区的大小,默认值是32K。
   */
 NetworkClient client = new NetworkClient(
         new Selector(config.getLong(ProducerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG), this.metrics, time, "producer", channelBuilder),
         this.metadata,
         clientId,
         config.getInt(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION),
         config.getLong(ProducerConfig.RECONNECT_BACKOFF_MS_CONFIG),
         config.getInt(ProducerConfig.SEND_BUFFER_CONFIG),
         config.getInt(ProducerConfig.RECEIVE_BUFFER_CONFIG),
         this.requestTimeoutMs, time);

 /***
  *  8、创建sender线程 并启动
  *   这个就是一个线程
     * 我们在项目中一般都会去设置重试,
     *
     * (1) retries:重试的次数
     * (2) acks:
     *   0:
     *      producer发送数据到broker后,就完了,没有返回值,不管写成功还是写失败都不管了。
     *   1:
     *      producer发送数据到broker后,数据成功写入leader partition以后返回响应。
     *      当刚写完leader partition 并发送响应后leader挂了,follower未拉取到数据就会进行重新选举,造成数据丢失
     *
     *   -1:
     *       producer发送数据到broker后,数据要写入到leader partition里面,并且数据同步到所有的
     *       follower partition里面以后,才返回响应。
     *       这种情况下,当无follower时会丢数,保证有多个副本时才能保证不丢数据
     *
     */
    this.sender = new Sender(client,
            this.metadata,
            this.accumulator,
            config.getInt(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION) == 1,
            config.getInt(ProducerConfig.MAX_REQUEST_SIZE_CONFIG),
            (short) parseAcks(config.getString(ProducerConfig.ACKS_CONFIG)),
            config.getInt(ProducerConfig.RETRIES_CONFIG),
            this.metrics,
            new SystemTime(),
            clientId,
            this.requestTimeoutMs);
   //启动线程。
   this.ioThread.start();

Guess you like

Origin blog.csdn.net/m0_46449152/article/details/114848346