文章目录
- 1. 消费位点提交
- 2. 消费位点重置
- 3. session 超时和心跳监测
- 4. 拉取大消息
- 5. 拉取公网
- 6. 消息重复和消费幂等
- 7. 消费失败
- 8. 消费延迟
- 9. 消费阻塞以及堆积
- 10. 提高消费速度
- 11. 消息过滤
- 12. 事务消息
- 13. 消息广播
- 14. 订阅关系
1. 消费位点提交
消息队列Kafka版消费者有两个相关参数:
-
enable.auto.commit
:默认值为 true,自动提交。 -
auto.commit.interval.ms
: 默认值为1000,也即1s。
这两个参数组合的结果就是,每次 poll 数据前会先检查上次提交位点的时间,如果距离当前时间已经超过参数auto.commit.interval.ms
规定的时长,则客户端会启动位点提交动作。
因此,如果将enable.auto.commit
设置为true,则需要在每次poll 数据时,确保前一次 poll 出来的数据已经消费完毕,否则可能导致位点跳跃。
如果想自己控制位点提交,请把enable.auto.commit
设为 false,并调用commit(offsets)
函数自行控制位点提交。但是需要注意,消息处理时间不能超过 max.poll.interval.ms
设置的时间,默认300s,否则会发生消费者 reblance 异常
2. 消费位点重置
以下两种情况,会发生消费位点重置:
- 当服务端不存在曾经提交过的位点时(例如客户端第一次上线)。
- 当从非法位点拉取消息时(例如某个分区最大位点是10,但客户端却从11开始拉取消息)。
Java 客户端可以通过auto.offset.reset
来配置重置策略,主要有三种策略:
-
latest
,从最大位点开始消费。 -
earliest
,从最小位点开始消费。 -
none
,不做任何操作,也即不重置。
3. session 超时和心跳监测
session.timeout.ms
:consumer 的 session 超时时间,默认 10s。超过这个时间将会认为 consumer 挂了。将 reblance (重新平衡)消费者。
heartbeat.interval.ms
:心跳监测时间,默认值 3s。consumer 每 3s发送一次心跳给协调者。
4. 拉取大消息
消费过程是由客户端主动去服务端拉取消息的,在拉取大消息时,需要注意控制拉取速度,注意修改配置:
-
max.poll.records
:默认值:500。如果单条消息超过1 MB,建议设置为1。 -
fetch.max.bytes
:设置比单条消息的大小略大一点。 -
max.partition.fetch.bytes
:设置比单条消息的大小略大一点。
拉取大消息的核心是一条一条拉的。
5. 拉取公网
通过公网消费消息时,通常会因为公网带宽的限制导致连接被干掉,此时需要注意控制拉取速度,修改配置:
-
fetch.max.bytes
:建议设置成公网带宽的一半(注意这里的单位是bytes,公网带宽的单位是bits) -
max.partition.fetch.bytes
: 建议设置成fetch.max.bytes
的三分之一或者四分之一。
6. 消息重复和消费幂等
消息队列Kafka版消费的语义是 “at least once”, 也就是至少投递一次,保证消息不丢,但是不会保证消息不重复。在出现网络问题、客户端重启时均有可能出现少量重复消息,此时应用消费端如果对消息重复比较敏感(例如说订单交易类),则应该做到消息幂等。
以数据库类应用为例,常用做法是:
- 发送消息时,传入 key 作为唯一流水号ID;
- 消费消息时,判断 key 是否已经消费过,如果已经消费过了,则忽略,如果没消费过,则消费一次;
当然,如果应用本身对少量消息重复不敏感,则不需要做此类幂等检查。
7. 消费失败
消息队列Kafka版是按分区一条一条消息顺序向前推进消费的,如果消费端拿到某条消息后执行消费逻辑失败,例如应用服务器出现了脏数据,导致某条消息处理失败,等待人工干预,那么有以下两种处理方式:
- 失败后一直尝试再次执行消费逻辑。这种方式有可能造成消费线程阻塞在当前消息,无法向前推进,造成消息堆积;
- 由于消息队列Kafka版没有处理失败消息的设计,实践中通常会打印失败的消息、或者存储到某个服务(例如创建一个Topic专门用来放失败的消息),然后定时check失败消息的情况,分析失败原因,根据情况处理。
8. 消费延迟
消息队列Kafka版的消费机制是由客户端主动去服务端拉取消息进行消费的。因此,一般来说,如果客户端能够及时消费,则不会产生较大延迟。如果产生了较大延迟,请先关注是否有堆积,并注意提高消费速度。
9. 消费阻塞以及堆积
消费端最常见的问题就是消费堆积,最常造成堆积的原因是:
- 消费速度跟不上生产速度,此时应该提高消费速度,详情请参见【提高消费速度】;
- 消费端产生了阻塞。
消费端拿到消息后,执行消费逻辑,通常会执行一些远程调用,如果这个时候同步等待结果,则有可能造成一直等待,消费进程无法向前推进。
消费端应该竭力避免堵塞消费线程,如果存在等待调用结果的情况,建议设置等待的超时时间,超时后作消费失败处理。
10. 提高消费速度
提高消费速度有以下两个办法:
- 增加 Consumer 实例个数
可以在进程内直接增加(需要保证每个实例对应一个线程,否则没有太大意义),也可以部署多个消费实例进程;需要注意的是,实例个数超过分区数量后就不再能提高速度,将会有消费实例不工作。 - 增加消费线程。
增加 Consumer 实例本质上也是增加线程的方式来提升速度,因此更加重要的性能提升方式是增加消费线程,最基本的步骤如下:
- 定义一个线程池。
- Poll数据。
- 把数据提交到线程池进行并发处理。
- 等并发结果返回成功后,再次poll数据执行。
11. 消息过滤
消息队列Kafka版自身没有消息过滤的语义。实践中可以采取以下两个办法:
- 如果过滤的种类不多,可以采取多个Topic的方式达到过滤的目的;
- 如果过滤的种类多,则最好在客户端业务层面自行过滤。
实践中请根据业务具体情况进行选择,也可以综合运用上面两种办法。
12. 事务消息
isolation.level
:默认值 read_uncommitted,此时 topic 的所有消息对 consumer 都可见。
consumer 缓存这些消息,直到收到事务控制消息。若事务commit,则对外发布这些消息;若事务abort,则丢弃这些消息。
read_committed
只会读取已提交的消息
13. 消息广播
消息队列Kafka版没有消息广播的语义,可以通过创建不同的Consumer Group来模拟实现。
14. 订阅关系
同一个Consumer Group内,各个消费实例订阅的Topic最好保持一致,避免给排查问题带来干扰。