Kafka(二)Producer第一篇

一,Client开发


生产逻辑需要具备以下几个 步骤:
(1)配置生产者客户端参数及创建相应的生产者实例。
(2)构建待发送的消息。
(3)发送消息。
(4)关闭生产者实例。

public class Producer {
    private static final String BROKER_LIST = "localhost:9092";
    private static final String TOPIC = "TOPIC-A";
    public static Properties initConfig() {
        Properties properties = new Properties();
        // 以下3个必须
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BROKER_LIST);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
       
        // 客户端ID
        properties.put(ProducerConfig.CLIENT_ID_CONFIG, "eris-kafka-producer");
        // 重试次数
        properties.put(ProducerConfig.RETRIES_CONFIG, 3);
        // 成功收到消息分区副本数,默认为1,即leader副本收到就返回成功。注意是字符串!
        properties.put(ProducerConfig.ACKS_CONFIG, "1");
        // 生产者客户端能发送的消息的最大值,单位为B,默认1M
        properties.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG,  1048576);
        // Producer等待请求响应的最长时间,单位ms,默认值为30000
        properties.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG,  30000);
        // 生产者发送ProducerBatch之前等待更多ProducerRecord加入的时间。默认为0,ProducerBatch被填满时发出
        properties.put(ProducerConfig.LINGER_MS_CONFIG,  0);
        return properties;
    }

    public static ProducerRecord<String, String> initMessage() {
        return new ProducerRecord<>(TOPIC, "hi eris");
    }

    public static void main(String[] args) {
        Properties properties = initConfig();
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
        ProducerRecord record = initMessage();
        kafkaProducer.send(record);
        kafkaProducer.close();
    }
}

注意:KafkaProducer,它是线程安全的,我们可以在多线程的环境中复用它。
而 消费者客户端KafkaConsumer是非线程安全的。
1,发送消息的构造
消息对象是ProducerRecord,业务信息是value字段。

  • key可以让消息再二次归类,同一个key的消息会被划分到同一个分区中;
  • topic属性和value属性是必填项,其余属性是选填项;

2,主要Producer参数
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG并非需要所有的broker地址,生产者会从给定的broker里查找到其他broker的信息,一般至少要2个。
3, 消息的发送
3-1,消息发送的3种模式:发后即忘、同步及异步
A,发后即忘:
kafkaProducer.send(record);
B,同步:
kafkaProducer.send(record).get();
C,异步:

  • 不推荐手动用future实现业务异步,诸多消息对应的Future对象的处理会引起逻辑的混乱。
  • 对于同一个分区,如果消息record1于record2之前先发送,那么KafkaProducer可以保证对应的callback1在callback2之前调用,也就是说 ,回调函数的调用也可以保证分区有序。

3-2,KafkaProducer中一般会发生两种类型的异常:可重试的异常和不可重试的异常。
对于可重试异常,如果配置了 retries 参数,那么只要在规定的重试次数内自行恢复了,就不会抛出异常;不可重试异常会直接抛出异常。
3-3,close方法会回收资源,并且阻塞等待之前所有的发送请求完成后再关闭 KafkaProducer,可带timeout。
3-4,消息在通过send发往broker的过程中,有可能需要经过拦截器(Interceptor)、序列化器(Serializer)和分区器(Partitioner)才能被真正地发往 broker。
4,序列化
生产者需要用序列化器(Serializer) 把对象转换成字节数组才能通过网络发送给Kafka。
如果 Kafka 客户端提供的几种序列化器都无法满足需求,则可以选择使用如Avro、JSON、Thrift、ProtoBuf和Protostuff等通用的序列化工具来实现,或者使用 自定义的序列化器来实现
5,分区器
如果ProducerRecord中指定了partition字段,那么就不需要分区器的作用。
反之若没有指定partition字段,那么就需要依赖分区器, 根据key这个字段来计算partition的值。

  • 如果key不为null,计算得到的分区会是所有分区中的任意一个;
  • 如果key为null,计算得到的分区号仅为可用分区中的任意一个;

一旦主题中增加了分区,那么就难以保证key与分区之间的映射关系了。
可以自定义分区器。
6,拦截器
自定义拦截器需要实现org.apache.kafka.clients.producer.ProducerInterceptor接口,接口中包含3个方法:

  • onSend:将消息序列化和计算分区之前调用;
  • onAcknowledgement:消息被应答(ack)之前或消息发送失败时调用,优先于用户设定的 Callback之前执行;
  • interceptor.classes配置拦截器链(逗号分隔),拦截链会按照参数配置的顺序一一执行。如果某个拦截器执行失败,下一个拦截器会接 着上一个执行成功的拦截器继续执行。

二,重要参数


1.acks
用来指定分区中必须要有多少个副本收到这条消息,之后生产者才会认为这条消息是成功写入的。

  • ack=1(默认),leader写入(落盘)即成功;
  • ack=0,发送后就认为成功,不需要等待任何服务端的响应;
  • ack=-1或者ack="all",需要ISR中的所有副本都成功写入;

2,retries
生产者重试的次数,默认值为0,即不重试。
保证消息顺序
max.in.flight.requests.per.connection
该参数指定了生产者在收到服务器响应之前可以发送多少个消息(即最多缓存的请求数)。它的值越高,就会占用越多的内存,不过也会提升吞吐量。把它设为 1 可以保证消息是按照发送的顺序写入服务器的,即使发生了重试。
如果把 retries 设为非零整数,同时把 max.in.flight.requests.per.connection 设为比 1 大的数,那么,如果第一个批次消息写入失败,而第二个批次写入成功,broker 会重试写入第一个批次。如果此时第一个批次也写入成功,那么两个批次的顺序就反过来了。
一般来说,如果某些场景要求消息是有序的,那么消息是否写入成功也是很关键的,所以不建议把 retries 设为 0。可以把 max.in.flight.requests.per.connection 设为 1,这样在生产者尝试发送第一批消息时,就不会有其他的消息发送给 broker。不过这样会严重影响生产者的吞吐量,所以只有在对消息的顺序有严格要求的情况下才能这么做。
3,linger.ms
这个参数用来指定生产者发送ProducerBatch之前等待更多ProducerRecord加入ProducerBatch的时间,默认值为0,即生产者会在 ProducerBatch 被填满时发送出去。
其他:

相关推荐
喜欢猪猪1 小时前
深度解析ElasticSearch:构建高效搜索与分析的基石原创
分布式
码上一元2 小时前
消息队列:如何确保消息不会丢失?
kafka·消息队列·rocketmq
蘑菇蘑菇不会开花~2 小时前
分布式Redis(14)哈希槽
redis·分布式·哈希算法
问道飞鱼4 小时前
分布式中间件-Pika一个高效的分布式缓存组件
分布式·缓存·中间件
小宋10215 小时前
玩转RabbitMQ声明队列交换机、消息转换器
服务器·分布式·rabbitmq
懒洋洋的华36910 小时前
消息队列-Kafka(概念篇)
分布式·中间件·kafka
March€11 小时前
分布式事务的基本实现
分布式
DieSnowK12 小时前
[Redis][环境配置]详细讲解
数据库·redis·分布式·缓存·环境配置·新手向·详细讲解
Lill_bin13 小时前
深入理解ElasticSearch集群:架构、高可用性与数据一致性
大数据·分布式·elasticsearch·搜索引擎·zookeeper·架构·全文检索
happycao12315 小时前
kafka之路-01从零搭建环境到SpringBoot集成
kafka