写在开头:
本章是Kafka学习归纳第二部分,着重于强调Kafka的java开发和生产消费过程。
文章内容输出来源:拉勾教育大数据高薪训练营。
Kafka开发实战
消息的发送与接收
生产者的主要对象有:KafkaProducer , ProducerRecord 。
KafkaProducer用于消息发送,ProducerRecord 用于创建发送实体,包括需要指定的参数
参数 | 说明 |
---|---|
bootstrap.servers | 配置生产者如何与当前broker建立连接,如果生产者连接的是kafka集群,那么这里配置集群中的多个broker地址而不是全部,因为生产者连上后会自动发现集群。 |
key.serializer | 指定key的序列化器,设置时可以写类名或class对象 |
value.serializer | 指定value的序列化器,设置时可以写类名或class对象 |
acks | 这个属性默认为all。总共三个属性 acks=0 生产者不等待broker的确认消息,将数据存在缓冲区就认为消息发送成功 acks=1 生产者的消息只要写到主分区,那么就响应客户端,不管同步副本什么情况。 acks=all或者acks=-1 效果相同 等待所有副本包括同步副本的确认,是kafka最强的可靠性保证 |
retries | 重试次数,消息发送错误时,系统就会重发消息, |
参数可以在org.apache.kafka.clients.producer.ProducerConfig该类中找到。
生产者生产消息后,broker端可以同步确认也可以异步确认,同步效率低,异步效率高,但需要设置回调对象。
消息被发送到缓冲区之前,要经过拦截器,系列化器和分区器。如下图所示
Producer拦截器(interceptor)是让用户在消息发送到缓冲区前,对消息做一些定制化需求,比如修改消息等。同时,Producer允许用户指定多个Interceptor按序作用于同一条消息从而形成一个拦截链(interceptor chain)
由于kafka中的数据都是字节数组,所以消息要序列化。序列化器结束后还要经过分区器,
关键是如何分区呢?
1. 如果record提供了分区号,则使用record提供的分区号
2. 如果record没有提供分区号,则使用key的序列化后的值的hash值对分区数量取模
3. 如果record没有提供分区号,也没有提供key,则使用轮询的方式分配分区号
详细的生产者发送消息过程
由上图可以看出:KafkaProducer有两个基本线程
主线程:
负责消息创建,拦截器,序列化器,分区器等操作,并将消息追加到消息收集器RecoderAccumulator中;可以看到消息累加器为每个分区维护了一个ProducerBatch的队列,其实就是一个ProducerRecord的集合,将数据一批批往外发。
Sender线程:
该线程从消息收集器获取缓存的消息,然后由sender创建request对象,在该对象中包含了想哪个节点发送消息。在发送前会先缓存将要发送的消息。最后交给Selector通过网络发送给broker集群。集群接收到后会给一个响应,最后会删除累加器中的消息。
消费者
消费者从订阅的主题消费消息,消费消息的偏移量保存在__consumer_offsets主题中。
多个从同一个主题消费的消费者可以加入到一个消费组中。消费者组中的消费者共享groupid。groupid可以设置为应用的逻辑名称,如多个订单处理程序组成一个消费组,就可以起名order_process。
订阅
同一个topic的数据,会被分散的存储到多个partition中,,这些partition可以在同一台机器上,也可以是在多台机器上,在多台机器上有利于水平扩展,避免单台机器在磁盘空间和性能上的限制,同时可以通过复制来增加数据冗余性,提高容灾能力。为了做到均匀分布,通常partition的数量通常是Broker Server数量的整数倍。
位移提交
Consumer需要向Kafka记录自己的位移数据,这个汇报过程称为 提交位移
Consumer 需要为分配给它的每个分区提交各自的位移数据
位移提交的由Consumer端负责的,Kafka只负责保管。保存在__consumer_offsets主题中
重平衡
为什么说重平衡为人诟病呢?因为重平衡过程中,消费者无法从kafka消费消息,这对kafka的TPS影响极大,而如果kafka集内节点较多,比如数百个,那重平衡可能会耗时极多。数分钟到数小时都有可能,而这段时间kafka基本处于不可用状态。所以在实际环境中,应该尽量避免重平衡发生
如何避免重平衡
要说完全避免重平衡,是不可能,因为你无法完全保证消费者不会故障。而消费者故障其实也是最常见的引发重平衡的地方,所以我们需要保证尽力避免消费者故障。
而其他几种触发重平衡的方式,增加分区,或是增加订阅的主题,抑或是增加消费者,更多的是主动控制。
实际中,会有一些情况,kafka错误地认为一个正常的消费者已经挂掉了,我们要的就是避免这样的情况出现。
首先要知道哪些情况会出现错误判断挂掉的情况
在分布式系统中,通常是通过心跳来维持分布式系统的,kafka也不例外。
在分布式系统中,由于网络问题你不清楚没接收到心跳,是因为对方真正挂了还是只是因为负载过重没来得及发生心跳或是网络堵塞。所以一般会约定一个时间,超时即判定对方挂了,而在kafka消费者场景中,session.timout.ms参数就是规定这个超时时间是多少。还有一个参数,heartbeat.interval.ms,这个参数控制发送心跳的频率,频率越高越不容易被误判,但也会消耗更多资源。此外,还有最后一个参数,max.poll.interval.ms,消费者poll数据后,需要一些处理,再进行拉取。如果两次拉取时间间隔超过这个参数设置的值,那么消费者就会被踢出消费者组。也就是说,拉取,然后处理,这个处理的时间不能超过 max.poll.interval.ms 这个参数的值。这个参数的默认值是5分钟,而如果消费者接收到数据后会执行耗时的操作,则应该将其设置得大一些。
综上:
session.timout.ms控制心跳超时时间,
heartbeat.interval.ms控制心跳发送频率,
max.poll.interval.ms控制poll的间隔。
这里给出一个相对较为合理的配置,如下:
session.timout.ms:设置为6s
heartbeat.interval.ms:设置2s
max.poll.interval.ms:推荐为消费者处理消息最长耗时再加1分钟
主题
使用kafka-topics.sh脚本:
创建主题
kafka-topics.sh --zookeeper localhost:2181/myKafka --create --topic topic_x -
-partitions 1 --replication-factor 1
kafka-topics.sh --zookeeper localhost:2181/myKafka --create --topic
topic_test_02 --partitions 3 --replication-factor 1
--config max.message.bytes=1048576 --config segment.bytes=10485760
查看主题
kafka-topics.sh --zookeeper localhost:2181/myKafka --list
kafka-topics.sh --zookeeper localhost:2181/myKafka --describe --topic topic_x
kafka-topics.sh --zookeeper localhost:2181/myKafka --topics-with-overrides --describe
修改主题
kafka-topics.sh --zookeeper localhost:2181/myKafka --alter --topic
topic_test_01 --config max.message.bytes=1048576
kafka-topics.sh --zookeeper localhost:2181/myKafka --alter --topic
topic_test_01 --config segment.bytes=10485760
kafka-topics.sh --zookeeper localhost:2181/myKafka --alter --delete-config
max.message.bytes --topic topic_test_01
删除主题
kafka-topics.sh --zookeeper localhost:2181/myKafka --delete --topic topic_x
并不会直接删除,而是给主题添加删除的标记,过一段时间删除
增加分区
通过命令行工具操作,主题的分区只能增加,不能减少。否则报错
kafka-topics.sh --zookeeper localhost/myKafka --alter --topic myTop1 --partitions 2
偏移量管理
Kafka 1.0.2,__consumer_offsets主题中保存各个消费组的偏移量
工具脚本的位置与名称为 bin/kafka-consumer-groups.sh
将偏移量设置为最早
kafka-consumer-groups.sh --bootstrap-server linux121/myKafka --reset-offsets
--group group1 --to-earliest --topic topic1
将偏移量设置为最新的:
kafka-consumer-groups.sh --bootstrap-server linux121/myKafka --reset-offsets
--group group1 --to-lastest --topic topic1
分别将指定主题的指定分区的偏移量向前移动10个消息:
kafka-consumer-groups.sh --bootstrap-server linux121/myKafka --reset-offsets
--group group1 --topic topic1:0 --shift-by -10
kafka-consumer-groups.sh --bootstrap-server linux121/myKafka --reset-offsets
--group group1 --topic topic1:1 --shift-by -10
kafka-consumer-groups.sh --bootstrap-server linux121/myKafka --reset-offsets
--group group1 --topic topic1:2 --shift-by -10