Kafka之生产者

1.简介

从编程的角度而言,生产者就是负责向Kafka发送消息的应用程序。在Kafka的历史变迁中,一共有两个大版本的生产者客户端:第一个是于Kafka开源之初使用Scala语言编写的客户端,我们可以称之为旧生产者客户端(Old Producer)或Scala版生产者客户端;第二个是从Kafka0.9x 版本开始推出的使用Java语言编写的客户端,我们可以称之为新生产者客户端(New Producer)或Java版生产者客户端,它弥补了旧版客户端中存在的诸多设计缺陷。虽然Kafka是用Java/Scala语言编写的,但这并不妨碍它对于多语言的支持。

2.客户端开发

一个正常的生产逻辑需要具备以下几个步骤:

  1. 配置生产者客户端参数及创建相应的生产者实例。
  2. 构件待发送的消息。
  3. 发送消息。
  4. 关闭生产者实例。

生产者客户端示例代码:

public class KafkaProducerAnalysis {
   
    public static final String BROKER_LIST = "localhost:9092";
    public static final String TOPIC_NAME = "topic-demo";

    public static Properties initConfig(){
   
        Properties properties = new Properties();
        properties.put("bootstrap.servers", BROKER_LIST);
        properties.put("key.serializer",
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer",
                "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("client.id", "producer.client.id.demo");

        return properties;
    }

    public static void main(String[] args){
   
        Properties properties = initConfig();
        //配置生产者客户端参数并创建KafkaProducer实例
        KafkaProducer<String, String> producer = new KafkaProducer(properties);
        //构件所需要发送的消息
        ProducerRecord<String, String> record = new ProducerRecord<>(TOPIC_NAME, "hello,Kafka!");
        //发送消息
        try{
   
            producer.send(record);
        }catch (Exception e){
   
            e.printStackTrace();
        }finally {
   
            producer.close();
        }
    }
}

构件的消息对象ProducerRecord,它并不是单纯意义上的消息,它包含了多个属性,原来需要发送的与业务相关的消息体只是其中的一个value属性,比如“hello,Kafka!”只是ProducerRecord对象中的一个属性。ProducerRecord类的定义如下(只截取成员变量):

public class ProducerRecord<K, V> {
   
    private final String topic;//主题
    private final Integer partition;//分区号
    private final Headers headers;//消息头部
    private final K key;//键
    private final V value;//值
    private final Long timestamp;//消息的时间戳
    ...
}

其中topic和partition字段分别代表消息要发往主题和分区号。headers字段是消息的头部,Kafka 0.11.x版本才引入这个属性,它大多用来设定一些与应用相关的信息,如无需要也可以不用设置。key是用来指定消息的键,它不仅是消息的附加信息,还可以用来计算分区号进而可以让消息发往特定的分区。前面提及消息以主题为单位进行归类,而这个key可以让消息再进行二次归类,同一个key的消息会被划分到同一个分区中。有key的消息可以支持日志压缩的功能。value是指消息体,一般不为空,如果为空则表示特定的消息–墓碑消息。timestamp是指消息的时间戳,它有CreateTime和LogAppendTime两种类型,前者表示消息创建的时间,后者表示消息追加到日志文件的时间。

2.1.必要的参数配置

在创建真正的生产者实例前需要配置相应的参数,比如需要连接的Kafka集群地址。在Kafka生产者客户端KafkaProducer有3个参数是必填的。

  1. bootstrap.servers:该参数用来指定生产者客户端连接Kafka集群所需的broker地址清单,具体的内容格式为host1:port1,host2:port2,可以设置一个或多个地址,中间以逗号隔开,此参数的默认值为“”。注意这里并非需要所有的broker地址,因为生产者会从给定的broker里查找到其他broker的信息。不过建立至少要设置两个以上的broker地址信息,当其中任意一个宕机时,生产者仍然可以连接到Kafka集群上。
  2. key.serializer和value.serializer:broker端接收的消息必须以字节数组(byte[])的形式存在。生产者使用的KafkaProducer<String, String>和ProducerRecord<String, String>中的泛型<String, String>对应的就是消息中key和value的类型,生产者客户端使用这种方式可以让代码具有良好的可读性,不过在发往broker之前需要将消息中对应的key和value做相应的序列化操作来转换成字节数组。key.serializer和value.serializer:broker端接收的消息必须以字节数组(byte[])的形式存在。生产者使用的KafkaProducer<String, String>和ProducerRecord<String, String>中的泛型<String, String>对应的就是消息中key和value的类型,生产者客户端使用这种方式可以让代码具体良好的可读性,不过在发往broker之前需要将消息中对应的key和value做相应的序列化操作来转换成字节数组。key.serializer和value.serializer这两个参数分别用来指定key和value序列化操作的序列化器,这两个参数无默认值。注意这里必须填写序列化器的全限定名,

initConfig()方法里还设置了一个参数client.id,这个参数用来设定KafkaProducer对应的客户端id,默认值为“”。如果客户端不设置,则KafkaProducer会自动生成一个非空字符串,内容形式如“producer-1”、“producer-2”,即字符串“producer-”与数字的拼接。
KafkaProducer中的参数众多,远非示例中initConfig()方法中的那样只有4个,开发人员可以根据业务应用的实际需求来修改这些参数的默认值,以达到灵活调配的目的。一般情况下,普通开发人员无法记住所有的参数名称,只能有个大致的印象。在实际使用过程中,诸如“key.serializer”、“max.request.size”、“interceptor.classes”之类的字符串经常由于人为因素而书写错误。为此,我们可以直接使用客户端中的org.apache.kafka.clients.producer.ProducerConfig类来做一定程度上的预防措施,每个参数在ProducerConfig类中都有对应的名称,以initConfig()方法为例,引入ProducerConfig后的修改结果如下:

public static Properties initConfig(){
   
    Properties properties = new Properties();
    properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
            BROKER_LIST);
    properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
            "org.apache.kafka.common.serialization.StringSerializer");
    properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
            "org.apache.kafka.common.serialization.StringSerializer");
    properties.put(ProducerConfig.CLIENT_ID_CONFIG,
            "producer.client.id.demo");

    return properties;
}

注意上面的代码中的key.serializer和value.serializer参数对应类的全限定名比较长,也比较容易写错,这里通过Java中的技巧来做进一步的改进,相关代码如下:

properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
                StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
        StringSerializer.class.getName());

如此代码便简洁了许多,同时进一步降低了人为出错的可能性。在配置完参数之后,我们就可以用它来创建一个生产者实例。示例如下:

KafkaProducer<String, String> producer = new KafkaProducer(properties);

Kafka Producer是线程安全的,可以在多个线程中共享单个Kafka Producer实例,也可以将Kafka Producer实例进行池化供其他线程调用。
Kafka Producer中有多个构造方法,比如在创建Kafka Producer实例时并没有设定key.serializer和value.serializer这两个配置参数,那么就需要在构造方法中添加对应的序列化器。示例如下:

KafkaProducer<String, String> producer =
        new KafkaProducer(properties,
                new StringSerializer(), 
                new StringSerializer());

其内部原理和无序列化器的构造方法一样,不过就实际应用而言,一般都选用public Kafka Producer(Properties properties)这个构造方法来创建Kafka Producer实例。

2.2.消息的发送

在创建完生产者实例之后,接下来的工作就是构建消息,即创建ProducerRecord对象,其中topic属性和value属性是必填项,其余都是选填项,对应的ProducerRecord的构造方法也很多种,参考如下:

public ProducerRecord(String topic, Integer partition, Long timestamp, K key, V value) {
   
        this(topic, partition, timestamp, key, value, (Iterable)null);
    }

    public ProducerRecord(String topic, Integer partition, K key, V value, Iterable<Header> headers) {
   
        this(topic, partition, (Long)null, key, value, headers);
    }

    public ProducerRecord(String topic, Integer partition, K key, V value) {
   
        this(topic, partition, (Long)null, key, value, (
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值