《Kafka权威指南》读书笔记4 Kafka消费者

又是重要的一章。

类似生产者,这里也是使用Kafka提供的消费者API就可以接收消息了。

【4.1 几个概念】

就像生产者可以有多个,消费者也可以有多个。

消费者群组

一个群组group里的消费者订阅同一个topic时,分工,每个consumer接收一部分分区的消息。这叫横向伸缩的一个手段。

同一个群组中,若有4个分区:

        1个消费者,那这个消费全部分区;

        2-3个消费者,他们分工;

        4个消费者,一人一个;

        大于4个,就只有4个消费,其他的没有分区消费了,就没活儿,闲着。

然后这里就有一个我的曾经傻瓜想法,我想给128张分表的消息,为了维护消费的顺序,对应建立128个分区,居然群里没人纠正我在胡说。。可能他们也不懂吧?还是以为我们的服务器有128+台?我就10台消费者,居然要搞128个分区,笑死,现在懂了,以后不能胡说了。

两个消费者群组之间互相不影响,被群组G1消费了的消息,G2正常消费。

再均衡:

又叫再平衡。指,分区的所有权从一个消费者转移到了另一个消费者,即分区重分配的机制。

场景:新增消费者、关闭消费者、消费者崩溃、添加了新的区分……

导致:再均衡期间,无法读取消息,整个群组一段时间不可用;分区从C1被重新分配到C2时,C1的读取状态丢失,有可能还会刷新缓存,消耗一段时间。

目标:安全的再均衡、避免不必要的再均衡

再均衡策略 3种:

可以参考博文:

Kafka再平衡机制详解_爱宝贝丶的博客-CSDN博客

Round Robin,Range和Sticky,默认使用的是Range。主要区别:

Round Robin:会采用轮询的方式将当前所有的分区依次分配给所有的consumer;我叫他排队瓜分地主的家产
Range:首先会计算每个consumer可以消费的分区个数,然后按照顺序将指定个数范围的分区分配给各个consumer;
Sticky:这种分区策略是最新版本中新增的一种策略,其主要实现了两个目的:
        将现有的分区尽可能均衡的分配给各个consumer,存在此目的的原因在于Round Robin和Range分配策略实际上都会导致某几个consumer承载过多的分区,从而导致消费压力不均衡;
        如果发生再平衡,那么重新分配之后在前一点的基础上会尽力保证当前未宕机的consumer所消费的分区不会被分配给其他的consumer上

群组协调器 GroupCoordinator

消费者群组的一个broker,主持工作。可以一个group一个,也可以兼职多个群组。

consumer在轮询消息or提交offset的时候,会发送心跳给群组协调器,维持他们和群组的从属关系、分区所有权,证明自己活跃,好的很。过长时间不发心跳,协调器就认为它挂了,触发再均衡。

怎么分配分区的?第一个加群的人就是群主,就第一个加群组的消费者是“群主”,他从协调器那里获得一份活跃成员名单,给他们分配分区。需要写一个实现PartitionAssignor接口的类。分完了以后,把分配结果回到给协调器。每个人只能看到自己分到的分区,只有群主知道所有的分配信息

【4.2 创建Kafka消费者】

3个必要属性

bootstrap.servers

key.deserializer

value.deserializer

参考第3章,分别是连接broker的地址,key序列化,value序列化

序列化,就是把Java对象转换成字节数组,生产者干这个;

反序列化,就是反过来,指定一个类,再把字节数组转回Java对象,消费者干这个。

group.id 是可选的,它标志一个消费者是哪个群组的,一般都指定一个群组。

【4.3 订阅主题】

subscribe()

可以一次性订阅一个list的topic,传入的可以是topic的名字,也可以是一个正则表达式!刚好我有一系列的topic要订阅!开头有规则,结尾有规则,nice!

当使用正则表达式订阅时,一旦创建了新的匹配的主题,就会触发一次再均衡,消费者就可以读取新增的主题了~

书看到这,解决了业务代码里订阅一个topic就写一套try-catch的问题。我一直想优化,不想订阅10个topic,写一排订阅和try-catch。之前不懂原理,现在稍微懂一点了,知道订阅是消费者维度的,就是一个服务订阅一次,跟谁在实例化抽象类无关,所以我用一个实例就可以订阅全部的topic了。终于把这里简化了,虽然不是多么牛逼的优化,但是我一直惦记这个事儿,现在心里痛快了一些。

第54页 看到这周围就很吵,先暂停,改天接着看。

20211206更新

 消费的代码↑

消费者就持续轮询Kafka,必须这样做,因为轮询时会发送心跳,否则会被认为已经死亡,它的分区就会被交给同群组中的其他消费者。poll()传参是获取消息的超时时间。

从代码可以看到,返回的list里,有topic、partition、offset、key、value

圈5的close()方法关闭消费者,同时关闭网络连接、socket,并立即触发一次再均衡。而不是等待群组协调器发现他死了再触发,等待需要更多时间,而且会导致整个群组在一段时间内无法读消息(为什么会影响其他consumer?)

消费者第一次调用poll()方法时,要先找到群组协调器,然后加入群组,接受分配的分区。

这里提醒到,规则是,一个消费者使用一个线程

【4.5 消费者的配置】

从55页开始,介绍了一些配置项,10个!作者说,虽然有时候不需要修改它们,但是他们与消费者的性能、可用性,会有很多关系。

作为消费者协会的一员,跟消费者有关的事情,我肯定要重视,所以这里要看(假的,只是为了搞笑)。

1. fetch.min.bytes

消费者从服务器获取记录的最小字节数。broker收到请求时,需要攒够了这个fetch.min.bytes才返回。这也可以降低消费者和broker的工作负载(很理解,不然不值当的让请求跑一趟)。所以如果没有很多数据,消费者的cpu使用率却很高,就应该调大这个值;反之调小;

书上说这有个默认值,但是没写是多少,可能是业务自己看着办吧

2. fetch.max.wait.ms

指定broker的等待时间,默认500ms。就是结合配置项fetch.min.bytes,如果一直攒不够指定的消息量,总不能让消费者一直等吧,所以这里设置了一个broker的最长等待时间来兜底,到点就给消费者返回啦,有多少返回多少。所以这俩配置项是结合着用的,谁先满足都可以让消息返回。

3.max.partition.fetch.bytes

服务器从分区里返回给消费者的最大字节数。默认值 1MB

即,KafkaConsumer.poll()方法,从单个分区里取的字节上限。

比如topic有20个分区,5个消费者,结合前面咱们知道,一个消费者可以处理4条流水线,划掉,4个分区,所以每个消费者至少需要4MB的可用内存来接收记录。最好多分配一点内存,因为如果其中一个工人崩了,其他人还得肩负起他的责任。

这里还提到一个配置项,max.message.size,是broker能接收的消息的字节数最大值。比如一条消息就2MB,但是消费者最大只肯接收1MB,那这条消息就卡了,没人能处理,导致消费者一直挂起重试。所以max.partition.fetch.bytes > max.message.size

4.session.timeout.ms

消费者在被认为死亡之前,可以与服务器断开连接的时间。默认3s。

我们都知道,消费者会一直发心跳给群组协调器,如果他太忙了,比如业务消费写的太复杂,1MB处理了5s,那就会被认为已经死亡,协调器就会触发再均衡,为了把分区分配给群组里的其他消费者。

那么多久发送一次心跳呢?这里也有个配置项,heartbeat.interval.ms。一般发送间隔时间,是,失联死亡时间的1/3。发送的间隔不能大于失联的时间,这个也很好理解,经常性检查的时候都认为消费者死了,家产分完了又回来干活了,又要重新分配集体财产……浪费时间和性能。

那么这个断开的时间,如果比默认3s更小,就会更及时的发现和恢复崩溃的节点;如果更大,就可以减少意外的再均衡。

5.auto.offset.reset

如果一个分区没有偏移量、或偏移量失效时,怎么处理。

latest,默认值。就是从最新的记录开始读(最新指,消费者启动之后)。

earliest,最早的,就是从分区的头开始读。

6.enable.auto.commit

是否自动提交偏移量,默认是true。自动提交时,可以通过小弟配置项,auto.commit.interval.ms控制提交频率,默认是5s。

为了尽量避免出现重复数据、数据丢失,可以设为false。

7.partition.assignment.strategy

分区的分配策略,类名。给PartitionAssignor用的。为了负载均衡,需要给一个策略。

Range:把主题的n个连续的分区分配给消费者。当分区/消费者线程不是整数场景时,会把多的分区派给前面的消费者线程。

配置项值:org.apache.kafka.clients.cosumer.RangeAssignore

【书外话】

Range的问题:注意这里我特意写的是消费者线程,前面也写过,一个消费者一个线程。所以如果两台机器A、B,都启动了5个消费者线程A1-5,、B1-5,即消费者数是10,那当分区是15个时,机器A处理10分区,机器B处理5分区,工作量差了整整一倍。机器A的CPU就会比较高,可能处理不过来。

优化思路:排消费者线程的时候,不再是A1、A2、A3、B1、B2、B3这样排,而是给他间隔开,A1、B1、A2、B2……这样再有余数分配时,就能保证A、B收到的分区一样or就差一个,达到更均衡的目的

RoundRobin:轮询分区,把分区逐个分配给消费者。

配置项值:org.apache.kafka.clients.cosumer.RangeRobinAssignore

自定义策略:写自定义的类名

8.client.id

任意字符。broker用的,用来标志从客户端发来的消息。常见于日志、度量指标、配额里

9.max.poll.records

消费者调用call()(我觉得这里写错了,应该是poll()方法)返回的记录上限,即一次消费几条

10.receive.buffer.bytes 和 send.buffer.bytes

读写数据时,用到TCP缓冲区大小。-1是系统默认值。这个配置项我估计用不到,不详细写了

【4.6】提交和偏移量

提交:更细分区当前位置的操作

消费者怎么提交偏移量?他们往一个叫_consumer_offset的特殊主题发消息,消息里包括每个分区的偏移量。如果所有消费者一直正常运行,偏移量就一直是最新的,没什么作用。但是新消费者加入、或有消费者崩溃时,触发再均衡,为了继续工作,消费者就要从分区的偏移量开始处理。

【4.6.1】自动提交

最简单,但是不太建议。

当自动提交配置项enable.auto.commit设置为true时,则每过5s(默认值),消费者就自动把poll()方法收到的最大偏移量提上去。

自动提交是基于时间间隔的,在发生再均衡时,会导致消息的重复处理

【4.6.2】提交当前偏移量

程序自己按照自己节奏,决定何时提交偏移量,用commitSync()方法。这个方法不是随便提交任何偏移量的,它是提交poll()方法返回的最新偏移量。注意这个方法名,他叫commit不就完事儿了吗,为啥还加个sync,因为他是同步方法啊,哎,名字起得好。

看到这里的时候,我曾有个疑问,是处理一条消息提交一次偏移量吗?那不太麻烦了?果然不是,poll()下100条,那就处理完100条后,提交一次

代码的对齐方式让我很难受,这个for循环的俩括号整的,非常难受,一开始还以为commitSync() 写在的循环里呢。所以我把他们框了一下,for就是业务的处理逻辑,处理完后,提交。

【4.6.3】异步提交

上小一节讲手动提交,broker还没处理完提交的请求之前,业务程序就一直阻塞,影响后续的业务

我的话:这时候我们就想了,反正我把偏移量提给你broker啦,我不想等你,我先干点别的,反正你等下也会告诉我的嘛(broker回调)。

这时候我们就用commitAsync(),异步提交了。

但是他有缺点,提2000的时候突然堵了,提3000的时候又好了,那2000就晚一些处理完,如果这时候发生再均衡,就有重复消息了

【4.6.4】同步和异步组合提交

【4.6.5】提交特定的偏移量

刚才说到,处理完一批消息,进行一次提交。那如果消息我一次拉100条,但是我知道自己处理的慢,都处理完就有可能超时,也想避免再均衡的时候少重复处理几条,比如我想处理5条就提交一次,怎么办呢?那就提交特定的偏移量。

 【4.7】再均衡监听器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值