1. 目的
Kafka 作为高吞吐量的分布式发布订阅消息系统中间件,各个业务线都有较普遍使用。本规范期望通过明确Kafka中间件日常研发使用过程中的要求、建议、关注点等指引,以及申请使用、变更流程注意事项,规范业务应用接入使用,以期减少不规范操作或使用方式带来的影响和风险,保障集群稳定、高效运行,提升研发效能,保障业务连续性。
2. 试用范围
本规范适用于Kafka设计、开发、监控、运维过程
本规范主要面向Kafka使用业务线研发、SRE 人员
中间件运维相关规范、应急手册单独文档组织。
3. 职责分工
分为研发,业务线SRE, 中间件组
3.1.研发
负责:
1. Kafka主要使用方案、生产/消费使用设计、应用对接
2. 集群新增、变更、下线流程申请,及对应过程相关验证工作
3. 配合业务线SRE、中间件运维推动相关改进,问题处理等
3.2.业务线SRE
负责:
1. 容量管理
2. 对于属主为本部门的集群,容量管理包括:使用容量现状了解、业务增长预估、容量不足排查、扩缩容变更建议
3. 元数据管理
4. 对于属主为本部门的集群,各个topic使用情况分布,是否有跨部门的topic混用(topic 生产者需为本集群业务线应用),推动不符合规范的topic跨业务主体混用整改。
5. 集群业务影响分析、跟踪管理
6. 对于集群的各个topic涉及业务使用场景,相关业务应用重要性等级进行分析、跟踪管理。
7. 相关业务应用包括直接依赖kafka集群的应用和间接依赖kafka 集群的应用(如直接依赖kafka 集群接受消息,进行业务分析处理的应用同时又为其他联机应用或上游应用提供数据服务的场景)。 特别是对于联机申请、审批等核心业务链路直接或间接依赖的Kafka 集群,业务线SRE 需重点跟踪管理。
8. 集群稳定性观察:监控、巡检结果风险感知,及时与中间件运维团队保持同步。
9. 配合业务研发及中间件运维相关问题处理,中间件使用改进配合。
- 跟踪本业务线中间件改进进度,推动整改项落实。
3.3.中间件组
1. 提供中间件集群运维、管理相关工具、平台支撑
2. 元数据管理
3. 对于topic创建、变更执行,需同步进行访问元数据系统纳管;通过平台化技术手段支持元数据管理自动化。
4. 集群稳定性监控、监控工具平台集成、巡检工具支持
5. 容量管理,各业务线集群新增、扩缩容评估,容量变更执行
6. 集群申请、变更执行
7. 规范集群使用,推动改进
4. 功能及使用场景
Kafka是高吞吐量的分布式发布订阅消息西贡,适合应用解耦,异步消息等场景。
5. 集群规格与标准
5.1.组建方式
【强制】kafka + 独立zookeeper
5.2.规格
版本: 目前支持运维的核心版本2.5.1
基础配置:
zookeeper: [2C 4G 100GB] * 3
kafka:[4C 16G 1000GB] * 3
5.3.性能评估
【参考】标准配置下:[4C 16G] * 3 消息体1KB topic 3分区3副本
发送消息:峰值11.6W/S 均值 8.7W/S
消费消息:16线程同时消费,均值16W/S
5.4.重要配置
【强制】关闭自动创建topic,auto.create.topics.enable=false
【强制】开启删除topic,delete.topic.enable=true
【建议】数据保留时长,log.retention.hours,建议不超过72h,数据量大的时候可以适当减小
【强制】ISR最小同步副本数,min.insync.replicas=2,配合acks使用达到消息持久性。 业务集群强制要求;非业务集群一致性要求不高的,经中间件组评估后可以小于2。
【强制】自动创建topic的默认副本数,default.replication.factor=3。 业务集群强制要求,非业务集群经中间件组评估后可小于3
【强制】必须开启jmx端口,方便监控
5.5.日志规范
日志目录在/home/finance/Logs/mw-test-kafka/下,日志统一采集到ELK,日志级别最高为INFO,日志格式必须符合ELK中间件日志规范。
6. 访问
6.1.Topic & Partition
【强制】Topic 创建需提前评估容量,并提交中间件创建流程进行topic 创建。
对于将kafka 作为架构基础实施,并需要动态创建topic 的长青,需提请架构部评审,经确认可行的情况,中间件团队作为例外处理。
【建议】对于公司集群规格,参考:每个分区最大生产消费速率 2000 msg/s , 作为分区数的评估参考。
Partition作为Kafka中面向客户端(上层应用)的最小逻辑操作单元(注: 实际物理层面上,最小存储单元是segment,一个partition为一组segments)。在producer和broker端,向每一个partition写入数据是可以完全并行化的,在consumer端,Kafka只允许单个partition的数据被同一个Consumer Group中的一个consumer线程消费。因此,在一个Kafka集群中,partition的数量直接影响整个吞吐量。
通过目标吞吐量测算partition数量:producer端的可达吞吐量为p,consumer端的可达吞吐量为c,目标吞吐量为e,那么partition数量至少为max(e/p, e/c),其中p与c的值通过对producer端和consumer端应用进行性能测试后获得。其次,为满足后续因业务发展而对Kafka集群的横向扩展,通常情况应需根据未来一到两年的目标吞吐量来设计partition数量。
虽然更多的分区能够提高吞吐量,但是大量的分区可能造成一些其他的后果,比如broker需要更多的Open File Handles,客户端需要更多的内存
【建议】Kafka事务消息,对消息生产发送速率及吞吐有较大影响,暂不建议使用。
【建议】topic分区数为broker的整数倍,副本数至少为3但是不能大于broker数,特殊情况除外,比如需要保证顺序和QPS很小的topic,分区数可以设置为1,
【强制】业务topic命名支持小写字母、数字、中划线、下划线;应为字母开头;长度范围4到64 个字符。
6.2.生产者
【建议】生产过程中,client版本应该与kafka集群版本保持一致,避免出现兼容性问题。
【强制】bootstrap-servers不允许只设置一个节点,至少设置3个。多个节点使用逗号分隔
【强制】业务kafka 集群 ack 需设置为all 保证消息投递可靠性,不许设置成0;对于非重要消息投递的kafka 集群, 考虑性能和吞吐量要求,ack 设为1 需及时与中间件团队沟通确认集群高可用等级。
【强制】最大单条消息大小建议不要超过1m。如果消息太大选择其他方式或者拆分为多条数据后再发送。 若超出默认大小限制,请提交流程到中间件团队评估后调整(调整消息限制大小涉及服务端参数配合设置)
【强制】发送消息的时候不允许指定分区,这样极易造成数据不平衡。 消息顺序性控制通过key 和 partitioner 设置来控制。
【强制】不同的业务根据topic进行分类,不要使用分区来分类。
【建议】为每条消息设置Key,以便业务上更好的追踪该消息,该Key Hash值尽量满足随机性,以便更加均匀分配在各个broker中
【建议】关于batch和buffer需按照业务场景来设置,发往同一个Partition的消息会被放到一个Batch里,默认情况下一个Batch大小为16kB,这个可根据该业务场景下每条消息的具体大小而定,配合linger.ms的设置,在延迟能接受的范围内最大限度提高吞吐。(参考后参数解读)
l 消息发送可靠性:
各种消息中间件,消息投递可靠性保障,一般有三种:
Ø 最多一次(at most once):消息可能会丢失,但绝不会被重复发送。
Ø 至少一次(at least once):消息不会丢失,但有可能被重复发送。
Ø 精确一次(exactly once):消息不会丢失,也不会被重复发送。
这三种保障语义,实现要求越来越高,kafka 默认提供至少一次的保障语义。即便是保障至少一次,也需要客户端服务端参数正确设置并结合消息发送、消费正确处理方式才行。
1. 参数设置
a) 客户端配置: acks=all 或-1
取值all或-1表示必须所有ISR 都同步完成,才返回消息写成功。其他取值有0,1。 0 表示发送broker 主副本未完成写即返回,显然可能会丢消息;1 表示主副本写成功即返回,此时如果主副本broker 失能,其他副本未完成数据同步,显然也会丢消息; 只有all 或-1 才能保证所有ISR副本已完成同步的情况才返回。问题是如果当前ISR 为1(只包含leader),就也达不到同步多个副本效果。ISR 只包含leader的情况是存在的,一种情况是topic 副本为1, 另一种情况是topic 有多个副本,但所有follower都未完成同步。 所以还必须要求ISR 中至少有一个其他副本。 这就需要服务端的min.insync.replicas 参数控制。
b) 服务端配置: min.insync.replicas (需>=2)
min.insync.replicas 即是控制在客户端acks=all 条件下,必须保证发送消息在ISR 有2 个以上的副本完成同步,消息发送才算成功,需保证改配置值不小于2。
c) 服务端配置: replication.factor (需>=3)
要满足min.insync.replicas >=2,必须保证 ****replication.factor >= min.insync.replicas + 1 才能保证高可用。例如在副本数为2时,如果某个副本所在broker 失能,此时就剩下了一个副本,这个时候客户端继续投递消息,由于要求min.insync.replicas 至少为2(保证消息不丢),显然这时无法满足该条件,就会导致客户端消息投递均不能成功。即是说kafka 在一个broker 失能情况下,集群高可用无法保障了,是非常严重的问题。
所以规范如下: 业务kafka 集群 topic 副本数必须大于等于3,小于3 (即便是2个副本),也是无法保证消息不丢的情况下保证高可用的。
d) 客户端生产配置: retries
以上参数设置,从副本同步机制上保证消息不丢,但生产发送消息给kafka,可能网络异常(发送给broker 时异常,也可能broker rpc 返回时网络异常),如果kafka 客户端lib内部不支持重试,网络一般抖动就可能造成消息发送失败,业务程序需要明确重新发送消息。Kafka 客户端为方便业务程序使用,减少可恢复异常业务程序手动处理,针对一部分Exception 可以进行重试,重试次数可通过 retries 指定,默认值2147483647,不建议调整。
对于不可重试异常,业务程序需要进一步处理,这就涉及到send 方法的使用。
2. 生产发送消息处理方式
生产者发送消息,通过KafkaProducer的send 方法调用, send 方法有两种调用方式:
一种producer.send(msg) 这种模式叫“发送了就不管了”,如果最终发送给server 出现不可重试异常,或者重试超时,相关异常信息没法针对进行处理。不要使用这种方式。应使用带回调通知的producer.send(msg, callback) 方式,通过callback 回调异步接受成功或异常通知,进行针对性处理。 如果异常可以考虑手动再重新发送消息或者其他地方存储异常消息或日志记录异常后续补发等,这才是健壮的发送消息失败处理方式
【强制】明确接受消息发送结果通知,并进行异常处理。
l 重试含义
Kafka客户端发送消息,从调用KafkaProducer#send方法到服务端真正完成接收消息,内部其实是两阶段完成的。 KafkaProducer#send 方法调用在业务主线程完成,内部会将各个TopicPartition的消息做批量整合起来,再通过异步的Sender 线程发给服务端,即真正的发送消息是异步处理的。其结构如下:
Producer 的retries 参数针对的是sender 线程发送消息给服务端失败情况下的重试,而不是KafkaProducer#send调用失败的重试。因为KafkaProducer#send 方法,只是将消息暂存到ProducerBatch 中,一般不会失败,其中一种重要的需要关注的异常导致的send 失败是BufferExhaustedException,这涉及到几个参数的控制:buffer.memory,max.block.ms(见后参数解读) 。****
如果KafkaProducer#send方法调用正常,异步sender 线程发送失败,且不可重试,就需要上述的producer.send(msg, callback) 的callback 方法进行处理。
l 消息投递顺序性保障:
Kafka 的主题(topic)-分区(partition)模型,只能保证在同一分区下投递顺序和消费顺序一致,跨分区不能保证。如果业务确有整个topic 维度,消息投递和消费的顺序一致行保障要求,简单粗暴的想法就是topic 只设置一个分区,根据kafka 的分区消息处理模式,确实能够保证topic 级消息顺序,但同时也降低了生产消费并发处理能力,对吞吐造成极大影响。此种方式在生产消费处理能力消息不高的情况下可以考虑。
还有以下方式可以考虑:
a) 设置消息key,需要保证顺序的消息,都设置同样的key 即可保证同样key 的消息进入同一partition,这样既保证了多个partition 的高吞吐,又能保证需要顺序保证的相关消息进入同一partition
b) 自己实现partitioner (实现接口 org.apache.kafka.clients.producer.Partitioner),并作消费参数partitioner.class配置。 通过相同key保证相关消息进入同一partition 就是默认的partitioner 的实现逻辑,具体大家可以查询源码。
既然发送消息是针对TopicPartition 顺序按批次发送,若前面的批次发送失败,后面的批次能否发送? 这涉及到kafka发送请求及服务端请求方式。
Kafka服务端有多个borker节点,一个客户端进程会同时向多个broker 节点发送消息,因为各个borker 上分布着不同topic 或者同一topic 的不同分区。集群整体上,各个broker 能同时处理各个不同客户端进程发送的请求,这也是分区模型提高吞吐的法宝之一。 但kafka 为了保障分区级别的消息顺序性,一个borker 针对某个具体的客户端进程,接受处理的请求是串行的,只有上一个请求处理完了之后,才会处理下一个请求,所以是有顺序性保证的(其实不仅保证了这个客户端发送的各个分区消息的顺序,而且保证了这个客户端节点发送的所有消息顺序,即:针对单个节点的请求处理是串行的)。 但针对某个TopicPartition发送消息时,如果上一批次的消息发送失败了,默认是可以继续发送下一次批次的消息的,当上一批次处理失败时,下一批次已经发出去并且成功了;然后重试上一批次并成功发送,会造成服务端真正接收成功的顺序和客户端端预期的不一致(注意服务端单个borker 针对同一个客户端进程始终是一个个请求处理的,只是第一个请求处理的结果失败了)。 如果在此种重试情况下仍要保证顺序,需要在发送第一批次服务端返回前不允许发送第二个批次,这样即使第一个批次失败了,也是再重新发送第一个批次。 这是通过生产端参数 max.in.flight.requests.per.connection 控制的,默认值5,设成1 即可保证。
【强制】对于消息投递顺序有严格要求的场景,除了保证有顺序要求的消息进入同一分区之外,需明确设置max.in.flight.requests.per.connection 参数为1
其他重要参数:
batch.size:
默认16384byte(16k),ProducerBatch 字节大小,注意不是一个发送批次的消息条数
【建议】 根据业务消息体大小适当调整该参数大小,提高吞吐。
max.request.size :
默认1048576byte(1m),Sender线程一次请求broker 数据最大限制,该值会起到限制一次发送消息至broker 的ProducerBatch 数目。 Sender向borker 一次请求会发送这个broker 相关的所有TopicPartition相关的ProducerBatch,但针对单个TopicPartition 一次只会发送一个ProducerBatch
【建议】 根据业务消息体大小适当调整该参数大小,提高吞吐
buffer.memory
默认32m。 表示发送端发送消息总缓存大小,即所有TopicPartition 所有ProducerBatch 能分配的总内存大小。
【建议】根据应用内存及消息量发送大小,可适当调大buffer.memory,提高吞吐。****
max.block.ms
默认60s 。用于控制buffer.memory 不足时等待空闲内存的最大等待时间,超时后KafkaProducer#send 方法抛出异常。
max.in.flight.requests.per.connection :
borker 连接上并发请求数。 见上消息投递顺序性保障。****
delivery.timeout.ms 生产参数
默认120000ms(2m),消息在batch 中等待被发送+发送等待broker 确认+重试时间的总和的上限。 即一条消息到从调用send 之后到真正成功发送到server或者失败的超时时间。 需大于request.timeout.ms + linger.ms
linger.ms 消费参数
默认0。 ProducerBatch 如果没有收集到batch.size 大小情况下等待发送的最大时间。 默认立即发送。注意: 这里的立即发送并不意味着ProducerBatch 只能存放一条消息,根据Sender线程发送速率大小,ProducerBatch 里也可能缓存了多条消息,批次仍然对提升吞吐是有意义的。
【建议】建议适当提高该值,提高吞吐,例如10ms。
batch.size和linger.ms:合理配置这两个参数,达到批量发送的效果,增大batch size可减少了请求即网络I/O次数,从而增大吞吐量,但是,过大的batch size反而会浪费内存使用,因为无论是否一个batch能达到该值,Kafka都会事先申请该值大小的内存空间为其使用,而且也会增大消息时延。同时加大linger.ms也能增大吞吐量,但也会增加消息时延
compression.type :****
消息压缩,默认不压缩。支持gzip,snappy,lz4。合理使用消息压缩可以提高整体吞吐量,降低网络I/O,磁盘I/O
【建议】对于消息发送量大的业务建议开启压缩,建议gzip。
delivery.timeout.ms:
默认120000ms(2m),消息在batch 中等待被发送+发送等待broker 确认+重试时间的总和的上限。 即一条消息到从调用send 之后到真正成功发送到server或者失败的超时时间。 需大于request.timeout.ms + linger.ms 。 不建议调整
request.timeout.ms :
默认30000ms(30s), 客户端向server 发起请求,到最终成功或失败次数耗尽的超时时间,request.timeout.ms 需不小于replica.lag.time.max.ms(borker 配置参数,默认30000ms(30s),follower 未发起fetch请求或者发起了请求但一直未同步到最新offset 的超时时间。超过这个时间后leader 会将该follower 从ISR 中移除)。 该配置生产者消费者均可配置, 不建议调整。
6.3.消费者
【强制】bootstrap-servers不允许只设置一个节点,至少设置3个。多个节点使用逗号分隔
【建议】Consumer 实例数不要大于分区总数,因为超过分区数的消费实例,不能分配到对应的partition 进行消费。 这里的实例数指消费线程数,可能一个进程会有多个线程进行消费。
【建议】默认消费的起始位置配置(auto.offset.reset, 指首次消费或者没有上次消费位点时,默认消费的位置),默认值为latest。 业务方需要根据实际情况具体设置,不建议调整。 若为earliest, 在异常情况下消费位点丢失时,会从头消费消息,可能会造成大量消息重复消费; 若在异常情况下,确要从头消费的再明确指定即可。
【强制】Kafka不保证消费重复消息,业务侧需要保证消息的幂等。
【建议】使用手动提交方式确认消息消费位点,从业务场景需求考虑并明确控制消费提交时机
【建议】使用异步处理消息的方式,避免消费端处理业务耗时,导致将consumer剔出consumer group频繁发生消费组重平衡。
【建议】消息处理失败时,请捕获异常进行处理,不建议阻塞重试,造成消息积压,应将失败消息记录,做后续补偿
l 消息可靠性保证:
要保证消息不丢失,还涉及到消费方的正确处理,主要涉及消费位置的处理。Kafka 客户端业务消费消息处理,有以下几个动作:
1. 获取消息
2. 处理并消费消息
3. 提交已消费消息位点(其实提交位置是待消费的下一条消息的位点)
要保证消息一定能正确消费掉(消费不丢消息),必须保证的约束是不能提前提交未消费的消息位点。****
【强制】不能提前提交未消费处理的消息。
Kafka client 提供了两种提交消费位置的方式,一种是自动提交,一种是手动提交。 默认是自动提交方式。不管是自动提交还是手动提交,获取消息-> 处理并消费消息->提交消费位点,这个顺序都能保证。 对于自动提交,是在下一次poll 拉取消息中提交上一次poll 的消息的最大位点(不一定每次都提交,见后述); 对于手动提交,这个先处理消息再提交位点也是必须保证处理顺序
默认方式是自动提交(生产者参数enable.auto.commit 默认true),
通过源码可知其实自动提交并不会在每次poll 的时候一定提交,而是距上次提交超过一定时长(auto.commit.interval.ms 参数控制,默认5000ms)条件下才会提交。
同时由代码方法mayAutoCommitOffsetAsync 可知自动offset 提交是异步提交的。
由此可知,对于自动提交,会出现offset 提交延迟,在应用消费节点重启等情况下,可能会有重复消费。
对于手动提交offset,一个值得注意的点是, kafkaConsumer 两次poll 之间如果超过max.poll.interval.ms (默认5分钟),会触发主动rebalance。 Kafka rebalance 过程完成后才能继续正常处理消息,rebalance 过程会对消息处理消息有明显影响。为避免这种情况,如果一次poll到的消息较多,处理过程耗时较长,需要考虑异步任务处理,同步获取结果(设置获取超时时间),保证超时时间小于max.poll.interval.ms 值。
手动提交其实考虑的点还挺多
1. 消息处理如果发生异常是记录错误状态(可以转投递到其他topic, 类似rabbitmq 的死信队列或者记录到其他存储等,后期再打捞处理),还是重复尝试多次。这个根据不同业务情况可选择不同的方式,如果是前者,可照常提交offset; 如果是后者,可以再下一次poll 之前seek 到当前poll 返回的各个partition 消息的起始消费位置,让下一次poll 不再继续往后fetch 消息(达到重试当前poll 批次的消息目的, 不过重试当前poll 批次处理的消息,可能会造成重复消费)。
2. commit offset 选择同步还是异步,一般根据提交频次决定,如果消息消费处理效率较高,commit offset 不是很频繁(例如一次poll 提交一次),可以考虑同步。一般情况建议异步提交,能提升消息处理吞吐量。
3. commit offset 提交失败如何处理,处理方式同第一点。可以选择重试(原地重试或者seek 当前批次的开始消费位置后,下次poll 重新处理消息)或者错误日志记录,继续处理下一批次poll。
l 重平衡:
一个consumer实例对应一个topic分区,在此之上对消息处理可再使用异步多线程处理的方式(不保证顺序的情况下)。 Consumer端应避免过重的业务逻辑,过重的业务逻辑不仅会影响消息堆积,还可能引起频繁的rebalance。 Rebalance 触发条件有:
1) 当Consumer订阅的Topic的分区发生了变化会触发Coordinator重新分配分区,导致重平衡发生
2) 有新的Consumer加入Consumer Group
3) 有Consumer下线,可能由于长时间未向Coordinator发送心跳,Coordinator会认为其已下线
4) 有Consumer主动退出Consumer Group
5) Consumer调用unsubscribe()取消对某Topic的订阅
6) 注意heartbeat.interval.ms和session.timeout.ms配合使用,保证Broker知道Consumer保持存活状态。作用于Consumer的heartbeat线程。官方建议心跳间隔为会话超时的三分之一
l 重要参数解读:
fetch.min.byte :
默认1byte, 拉取消息最小大小,1byte 意味着有数据就返回,不建议调整。保证拉取消息实时性。
fetch.max.bytes :
默认52428800(50m),针对一个Consumer Receive线程发起一次fetch request,允许fetch的最大数据限制。一次Fetch Request代表对一个Broker发起一次fetch请求,这一次请求会去读分配给他的所有Leader在该Broker上的partition产生的数据
max.partition.fetch.bytes :
默认1048576(1m),一次消息拉取请求,每个分区返回的最大数据量。
该值参考message.max.bytes/max.message.bytes的最大值为基准,且一定要大于这个值,否则某些消息大小介于两值之间的将无法被Consumer拉取。注:低版本(sonsumer version < 0.10.2)会有该问题。
【强制】若调整了message.max.bytes/max.message.bytes大小,需对应调整该参数。注:低版本(sonsumer version < 0.10.2)会有该问题。
heartbeat.interval.ms :
默认3000(3s),消费者线程心跳周期
session.timeout.ms :
默认10000(10s),客户端和borker session 会话超时时间,过期服务端会从消费者组中踢出客户端节点,造成消费rebalance。
max.poll.interval.ms :
默认300000(5min),两次poll 拉取消息间隔时间超过该值,主动触发rebalance。 这个时长和Consumer本身对消息处理能力相关,由具体处理逻辑来决定,作用于Consumer的poll线程,如果消费处理时长太长,需考虑异步消息处理。
fetch.max.wait.ms :
默认500,当服务端没有足够数据满足fetch.min.bytes 条件时,等待的最长时间
【强制】对单次poll 请求进行异常处理,保证消息拉取循环不中断退出, 保证临时网络等故障恢复后消费继续。
kafka 消息消费模式为拉模式,消费者应用若直接使用原生kafka-client 进行消费时,一般会循环调用consumer.poll() 方法进行消息拉取,由于poll 消息过程可能会触发异常(如网络短暂中断导致客户端同步提交位点异常等),此时如果异常处理方式不是针对单次poll 请求(如在循环外层catch 异常),会导致消息拉取过程退出,从而当相关故障恢复后无法继续消费。
错误样例:
try {
while (!stop) {
//从队列拉取消息,并处理消息
ConsumerRecords<String, String> records = consumer.poll(pollInterval);
}
} catch (Exception e) {
logger.error("Consumer thread [{}-{}] exit due to error", topic, threadNumber, e);
}
正确样例:
while (!stop) {
try {
//从队列拉取消息,并处理消息
ConsumerRecords<String, String> records = consumer.poll(pollInterval);
} catch (Exception e) {
logger.error("Consumer thread [{}-{}] exit due to error", topic, threadNumber, e);
}
}
注: spring-data-kafka 消息处理过程已正确处理异常,推荐使用。
6.4.Broker 相关参数参考
num.replica.fetchers :****
该参数为broker开启的同步数据的线程数,在Broker中的Follower向其他Broker中的Leader同步数据时,会单独开启replication线程。开启的线程数由该值和需要同步的Leader所在的Broker数有关
在大多数保证数据不丢失的情况下,我们会设置Acks = -1,(即Producer发送的消息到达Leader后,需要在ISR里面所有Follower都同步数据后,Leader才提交响应回Producer),因此增大该配置值可以提高消息同步的效率,进而提高吞吐量,但是,也因此会增大网络I/O,增大Broker的CPU使用。这个值需要考虑实际的物理环境(网络,服务器)进行设置
replica.fetch.max.bytes :****
Broker复制数据时单个分区单词fetch消息的最大字节数限制。该值参考message.max.bytes/max.message.bytes的最大值为基准,应大于他们的最大值,否则会丢失消息。 此外,该值影响到Brokers为每个分区分配的内存空间大小
replica.fetch.response.max.bytes :
Broker复制数据时对一次复制请求最大数据限制,一般设置为replica.fetch.max.bytes的整数倍,默认是10倍
replica.lag.time.max.ms:
默认30000ms(30s),follower 未发起fetch请求或者发起了请求但一直未同步到最新offset 的超时时间。超过这个时间后leader 会将该follower 从ISR 中移除
message.max.bytes :
默认1048588(1m),broker 允许的最大record batch 大小,注意不是单条消息大小。
max.message.bytes (Topic 级别参数):
含义同message.max.bytes, topic 级别参数
6.5.其他SDK 封装及使用
【强制】业务方不允许使用AdminClient操作kafka,可以使用spring-kafka,spring-cloud提供的工具类进行生产,消费消息。
【建议】Spring-kafka 相对原生SDK 提供了一些增强功能。 对于java 应用,建议使用。例如:
1. RoutingKafkaTemplate: 根据目标topic名称在运行时选择producer发送消息(不支持事务,刷新等操作)
2. ReplyingKafkaTemplate:提供了request/reply语义(生产,消费要搭配使用)使用方式参考:docs.spring.io/spring-kafk…
3. 通过设置concurrency实现多线程并发消费(注意:若开启了并发消费,并且同时消费多个topic时,建议将PartitionAssignor设置为RoundRobinAssignor,其默认值为RangeAssignor)
4. 通过实现ConsumerSeekAware接口实现重置消费位点到指定的位置,使用方式参考:docs.spring.io/spring-kafk…
5. 支持为producer/consumer添加拦截器
6. 提供暂停(ConsumerAwareMessageListener)/重启消费(ListenerContainerIdleEvent)的监听器,非线程安全(其他事件支持参考:docs.spring.io/spring-kafk…)
7. 异常处理,详情参考:docs.spring.io/spring-kafk…
【建议】中间件团队提供了kafka 服务化SDK,针对集群迁移、多中心支持,版本升级提供了相应支持,推荐接入。
7. 安全
【强制】集群必须添加鉴权访问。 鉴权认证方式为SASL,认证机制为SCRAM。 具体请了解:kafka.apache.org/25/document…
【强制】不同应用使用不同账号密码访问,严禁私自跨应用及业务线分享账号访问集群
【强制】Kafka 内置zookeeper 用于存储kafka 内部相关元数据,业务方应用不能直接使用kafka内置zookeeper。
8. 兼容性建议
如果使用kafka-client,Kafka-clients版本和kafka 服务端保持一致即可。如果版本不一致,可以通过kafka-broker-api-version.sh来确认broker支持的版本
Spring-kafka
| Spring-boot | Spring-kafka | Kafka-clients |
|---|---|---|
| 1.5.x | 1.3.x | 0.11.0.x, 1.0.x |
| 2.0.x | 2.1.x | 1.0.2 |
| 2.1.x | 2.2.x | 2.0.1, 2.1.x, 2.2.x |
| 2.2.x | 2.3.x、2.4.x | 2.3.1、2.4.1 |
| 2.3.x | 2.5.x | 2.5.1 |
| 2.4.x | 2.6.x | 2.6.0 |
| 2.5.x | 2.7.0 | 2.8.0 |
Spring-cloud-kafka
| Spring-boot | Spring-cloud-stream-binder-kafka | Kafka-clients |
|---|---|---|
| 1.5.5.RELEASE | 1.3.4.RELEASE | 1.x,2.x |
9. 监控
关注监控指标:
| 指标名称 | 备注 | 单位 |
|---|---|---|
| kafka.server.TopicBytesInPerSec_OneMinuteRate | 数据In速率(字节/秒)(服务端) | bytes/s |
| kafka.server.BytesOutPerSec_OneMinuteRate | 数据Out速率(字节/秒)(服务端) | bytes/s |
| kafka.server.Produce_OneMinuteRate | Produce请求速率(请求次数/秒)(服务端) | counts/s |
| kafka.server.FetchConsumer_OneMinuteRate | Consumer请求速率(请求次数/秒)(服务端) | counts/s |
| kafka.server.IsrShrinksPerSec_OneMinuteRate | ISR的收缩(shrink)速率(服务端) | rate |
| kafka.server.IsrExpandsPerSec_OneMinuteRate | ISR的扩大(expansion)速率(服务端) | rate |
| kafka.server.ReplicaFetcher | Follower落后leader replicas的最大的消息数量(服务端) | counts |
| kafka.producer.record_send_rate | 生产者每秒发送消息的平均数(应用kafka-client采集) | messages/s |
| kafka.consumer.records_consumed_rate | 消费者每秒消费记录平均数(应用kafka-client采集) | messages/s |
| 每秒jvm young gc 次数 | events/s | |
| 每秒jvm old gc 次数 | events/s | |
| jvm young gc耗时ms | milliseconds | |
| jvm old gc 耗时ms | milliseconds | |
| 每秒流入的数据条数 | Counts |