目录
1、bootstrap.servers=localhost:9092
2、key.serializer--消息key的序列化转换器
3、value.serializer--消息value的序列化转换器
4、acks--用于控制producer生产消息的持久性(怎样保证消息不丢失?)
Kafka快速搭建
以macOS 10.14版本系统为例,先到官网:https://kafka.apache.org/downloads下载二进制压缩包
这里以 kafka_2.11-2.0.0.tgz为例,下载完毕之后,打开终端,找到你希望安装的目录,如kafka_home,执行如下命令
tar -zxf kafka_2.11-2.0.0.tgz
解压成功以后,就可以开始着手启动kafka服务器了:
第一步先启动kafka内置的一个zookeeper服务器,到解压目录kafka_home下,执行如下命令
bin/zookeeper-server-start.sh config/zookeeper.properties
前面是zookeeper启动命令,后面是启动所使用的配置文件
第二步启动kafka,kafka_home目录下执行如下命令
bin/kafka-server-start.sh config/server.properties
同zookeeper,前面是启动命令,后面是所使用的配置文件(注意:在启动kafka之前,先确认电脑上已经安装了JDK)
kafka启动成功之后,可以开始创建topic,打开一个新的终端,到kafka_home目录下,执行命令,第二行是运行结果
bin/kafka-topics.sh --create --zookeeper localhost:2181 --topic test --partitions 1 --replication-factor 1
Created topic "test".
topic创建成功了,可以通过命令查看topic的状态,第二行开始是运行结果
bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test
Topic:test PartitionCount:1 ReplicationFactor:1 Configs:
Topic: test Partition: 0 Leader: 0 Replicas: 0 Isr: 0
执行结果可以看出,我们创建的topic是test,分区数(PartitionCount)是1,副本数(ReplicationFactor)是1
接着就可以开始发送消息了,执行命令如下,第二行开始,每一行都是一个消息内容
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
>Hello
>
打开另一个终端,就可以开始消费消息了,执行命令,第二行是消费的消息内容
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
Hello
至此,kafka已经可以开始使用,当然,这只是一个简单的单机版本
Kafka--producer开发
先上代码
public class ProducerTest {
public static void main(String[] args) {
//第一步
Properties props = new Properties();
//配置类--org.apache.kafka.clients.producer.ProducerConfig
props.put ("bootstrap.servers", "localhost:9092");
props.put ("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put ("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put ("acks","-1") ;
props.put ("retries", 3) ;
props.put ("batch.size", 323840) ;
props.put ("linger.ms", 10) ;
props.put ("buffer.memory", 33554432);
props.put ("max.block.ms", 3000);
//第二步
Producer<String, String> producer= new KafkaProducer<>(props);
for(int i = 0; i < 1; i++) {
producer.send(new ProducerRecord<>("my-topic", Integer.toString(i), "hello, world!"));
}
producer.close();
}
}
构造一个producer实例,大致需要以下几步
- 构造一个java.util.Properties实例,然后至少指定bootstrap.servers,key.serializer,value.serializer三个属性;
- 使用上一步的Properties实例,构建Producer实例
- 构造带发送的消息对象ProducerRecord(上面代码放在消息发送方法的参数里构建)
- 调用producer的send方法发送信息
- 关闭kafka-producer
下面对每一步的细节进行说明。
构造Properties对象
1、bootstrap.servers=localhost:9092
该参数指定了localhost:9092,producer使用时需要替换为实际的broker(kafka服务器)列表。如果kafka机器很多,只需要指定部分broker即可,不需要列出所有的broker,producer会通过指定的参数发现集群中的所有的broker。为该参数指定多台broker只是为了故障转移使用,这样某一台broker挂掉了,producer重启依然可以通过该参数指定的其他的broker连入kafka集群。
2、key.serializer--消息key的序列化转换器
被发送到broker端的任何消息格式都必须时字节数组,因此消息的各个组建必须首先做序列化,然后才能发送到broker。该参数就是为消息key做序列化只用。这个参数指定的是实现了org.apache.kafka.common.serialization.Serializer接口的类的全限定名称。Kafka为大部分的初始类型(primitive type)默认提供了现成的序列化器。上面的代码中使用了org.apache.kafka.common.serialization.StringSerializer会将一个字符串类型转换成字节数组。这个参数也可以用户自定义,只要实现Serializer接口即可。注意:producer在发送消息时即使不指定key,这个参数也是必须要设置的,否则程序会抛出ConfigException异常,提示"key.serializer"参数无默认值,必须要设置。
3、value.serializer--消息value的序列化转换器
和key.serializer类似,只是它被用来对消息体(即消息value)部分做序列化,将消息value部分转换成字节数组。上面的代码中该参数指定了与key.serializer相同的值,即都使用StringSerializer。当然了,value.serializer也可以设置成与key.serializer不同的值。
注意:这两个参数必须是全限定类名,只使用单独的类名是不行的。
4、acks--用于控制producer生产消息的持久性(怎样保证消息不丢失?)
对于producer而言,kafka在乎的是“已提交”消息的持久性。一旦消息被成功提交,那么只要有任何一个保存了该消息的副本“存活”,这条消息就会被视为“不会丢失的”。当producer发送一条消息给kafka集群时,这条消息会被送到指定的topic分区leader所在的broker上,producer等待从该leader broker返回消息的写入结果已确定消息被成功提交。这一切完成后producer可以继续发送新的消息。Kafka能保证的是consumer永远不会读取到尚未提交完成的消息。那么,leader何时发送写入结果返回给producer就是一个需要仔细考虑的问题了,它会直接影响消息的持久性甚至是producer端的吞吐量:producer端越快地接收到leader broker响应,它就越快的发送下一条消息,即吞吐量也就越大。acks参数就是来控制这件事情的,acks指定了在给producer发送响应前,leader broker必须要确保已成功写入该消息的副本数。当前acks有三个值:0、1和-1(all)
- acks="0":设置成0表示producer完全不理睬leader broker端的处理结果。此时,producer发送消息后立即开启下一条消息的发送,根本不等待leader broker端返回结果。由于不接收发送结果,因此在这种情况下producer.send的回调也就完全失去了作用,及用户无法通过回调机制感知任何发送过程中的失败,所以acks=0时producer并不保证消息会被发送成功。但是这种设置下producer的吞吐量是最高的。
- acks="-1"或者"all":表示当发送消息时,leader broker不仅将消息写入本地日志,同时还会等待ISR中所有其他副本都成功写入它们各自的本地日志后,才发送响应结果给producer。显然当设置acks=all时,只要ISR中至少有一个副本是处于“存活”状态的,那么这条消息就肯定不会丢失,因而可以达到最高的消息持久性,但通常这种设置下的producer的吞吐量也是最低的。
- acks="1":默认的参数值。producer发送消息后leader broker仅将该消息写入本地日志,然后发送响应结果给producer,而无需等待ISR中其他副本写入该消息。那么此时只要该leader broker一直存活,Kafka就能够保证者条消息不会丢失。这实际上是一种这种方案,既可以达到适当的消息持久性,同时也保证了producer端的吞吐量。
5、buffer.memory
该参数用于指定producer端用于缓存消息的缓冲区大小,单位是字节,默认值是33554432,即32MB。采用了异步发送消息的设计架构,Java版本producer启动时会首先创建一块内存缓冲区用于保存待发送的消息,然后由另一个专属线程负责从缓冲区中读取消息执行真正的发送。这部分内存空间大小即是由该参数buffer.memory指定。若producer向缓冲区写消息的速度超过了专属I/O线程发送消息的速度,那么必然造成该缓冲区空间的不断增大。此时producer会停止手头的工作等待I/O线程追上来,若一段时间后I/O线程还是无法追上producer的进度,那么producer会抛出异常并期望用户介入进行处理。
6、compression.type
该参数设置producer端是否缩消息,默认是none,即不压缩消息。Kafka的producer端引入压缩后可以显著的降低网络I/O传输开销从而提高吞吐量,但是同时也会增加producer端机器的CPU的开销。目前Kafka支持3种压缩算法:GZIP/Snappy/LZ4(这三者本人都不熟悉,在这就不说明了啊)。参数设置方式类似上面的代码
props.put ("compression.type", "lz4") ;
//或者
props.put (ProducerConfig.COMPRESSION_TYPE_CONFIG, "lz4") ;
7、retries
kafka broker在处理写入请求时可能因为某些瞬时原因导致消息发送失败,这种故障通常都是可以自行恢复的,因此producer内部自动实现了重试,当然前提就是要设置retries参数。默认为0,表示不进行重试。在实际使用过程中,设置重试可以很好地应对那些瞬时错误。但是在设置该参数时,需要注意以下亮点:
- 重试可能造成消息的重复发送
比如由于某个瞬时的错误导致broker端已经成功写入消息但是没有成功发送响应给producer,因此producer会认为消息发送失败,从而开启重试机制。不过用户可以在consumer端实现幂等等去重避免重复消费消息。
- 重试可能造成消息的乱序
当前producer会将多个消息发送请求缓存在内存中,如果由于某种原因发生了消息发送的重试,就可能造成消息流的乱序。为了避免乱序发生,producer提供了max.in.flight.requests.per.connection参数。一旦用户将此参数设置成1,producer将确保某一时刻只能发送一个请求。
8、batch.size和linger.ms
- batch.size
这是一个很重要的参数,默认值为16344,即16KB。它对于调优producer吞吐量和延时性能指标都有着非常重要的作用。producer会将发往统一分区的多条消息封装进一个batch中,当batch满了的时候,producer会发送batch中的所有消息。不过producer并不总是等待batch满了才发送消息,很有可能当batch还有很多空闲空间时producer就发送该batch。一个小的batch中包含的消息数很少,因而一次发送请求能够写入的消息数很少,所以producer的吞吐量会很低;但若一个batch非常之巨大,那么会给内存使用带来极大的压力,因为不管是否能够填满,producer都会为该batch分配固定大小的内存。因此batch.size参数的设置其实是一种时间与空间权衡的体现。
- linger.ms
上面说到没有填满batch.size也可以被发送的情况,这是因为该参数就是控制消息发送延迟行为的。该参数默认值为0,表示消息需要被立即发送,无需关心batch是否已被填满,不过这样会拉低producer的吞吐量。
9、max.request.size
该参数用于控制producer发送请求的大小。由于请求有一些头部数据结构,因此包含一条消息的请求的大小要比消息本身要打。
10、request.timeout.ms
当producer发送请求给broker后,broker需要在规定的时间范围内将处理结果返还给producer。这段时间便是由该参数控制的,默认是30秒。如果在设置时间内都没有给producer发送响应,那么producer就会任务该请求超时了,并在回调函数中显式地抛出TimeoutException异常交由用户处理。
构造KafkaProducer对象
属性设置好以后,下面就需要构造KafkaProducer对象了。KafkaProducer是producer的主入口,所有的功能基本上都是由KafkaProducer来提供的。创建KafkaProducer实例很简单,只需要下面一行代码即可
Producer<String, String> producer= new KafkaProducer<>(props);
KafkaProducer还有其他的构造函数,可以查看源码去详细了解。
构造ProducerRecord对象
构造好KafkaProducer实例后,下一步就是构造消息实例。Java版本producer使用ProducerRecord类来表示每条消息,创建ProducerRecord也很简单,最简单的形式就是指定topic和value,如下
new ProducerRecord<>("my-topic", "hello, world!")
当然ProducerRecord还支持指定更多的消息信息,比如可以控制该消息直接发往的分区以及消息的时间戳。
注意:时间戳指定需要谨慎,目前的Kafka设计中,时间戳索引文件中的索引项都是严格按照时间戳顺序排列的,所以如果在producer端随意指定时间戳,会导致该消息的时间序列混乱,这样在使用根据时间戳查询位移的功能时则不会找到这条消息,同时Kafka的消息留存侧拉也会收到影响,因此最好还是让Kafka自行来指定时间戳比较稳妥。
发送消息
Kafka Producer发送消息的主方法是send方法,虽然send方法只有两个简单的方法签名,但其实producer在底层完全的实现了异步化发送,并且通过Java体东的Future同时实现了同步发送和异步发送+回调(Callback)两种发送方式。