Kafka生产实践指南简单总结

Kafka

关键原则:一定要做基准测试,根据测试报告考察组件或者应用是否符合系统设计要求

kafka自带的基准测试套件-查看测试环节

Kafka问题:一个或多个副本在同步和非同步之间快速切换?

通常是java不恰当的垃圾回收配置导致的

Kafka问题:CPU和磁盘IO会突然上升,一会儿回降下来?

通常是未设置日志切换的时间抖动,致使所有topic的日志切换发生在同一个小的时间段内

Kafka关键:本质上是消息传递中间件,注意生产者、消费者和broker的配置

kafka基于LSM磁盘数据结构存储消息的,LSM的三大结构,日志段,WAL和页缓存,构成基于磁盘实现的高性能、大吞吐量的消息中间件

Kafka为什么不支持读写分离?

这其实是分布式场景下的通用问题,因为我们知道CAP理论下,我们只能保证C(可用性)和A(一致性)取其一,如果支持读写分离,那其实对于一致性的要求可能就会有一定折扣,因为通常的场景下,副本之间都是通过同步来实现副本数据一致的,那同步过程中肯定会有时间的消耗,如果支持了读写分离,就意味着可能的数据不一致,或数据滞后。
Leader/Follower模型并没有规定Follower副本不可以对外提供读服务。很多框架都是允许这么做的,只是 Kafka最初为了避免不一致性的问题,而采用了让Leader统一提供服务的方式。
不过,自Kafka 2.4之后,Kafka提供了有限度的读写分离,也就是说,Follower副本能够对外提供读服务。

Controller发生网络分区(Network Partitioning)时,Kafka会怎么样?

这道题目能够诱发我们对分布式系统设计、CAP理论、一致性等多方面的思考。

一旦发生Controller网络分区,那么,第一要务就是查看集群是否出现“脑裂”,即同时出现两个甚至是多个Controller组件。这可以根据Broker端监控指标ActiveControllerCount来判断。

不过,通常而言,我们在设计整个部署架构时,为了避免这种网络分区的发生,一般会将broker节点尽可能的防止在一个机房或者可用区。

由于Controller会给Broker发送3类请求,LeaderAndIsrRequest,StopReplicaRequest,UpdateMetadataRequest,因此,一旦出现网络分区,这些请求将不能顺利到达Broker端。

这将影响主题的创建、修改、删除操作的信息同步,表现为集群仿佛僵住了一样,无法感知到后面的所有操作。因此,网络分区通常都是非常严重的问题,要赶快修复。

Java Consumer为什么采用单线程来获取消息?

在回答之前,如果先把这句话说出来,一定会加分:Java Consumer是双线程的设计。一个线程是用户主线程,负责获取消息;另一个线程是心跳线程,负责向Kafka汇报消费者存活情况。将心跳单独放入专属的线程,能够有效地规避因消息处理速度慢而被视为下线的“假死”情况。这个问题跟Redis究竟是单线程还是多线程类似。

单线程获取消息的设计能够避免阻塞式的消息获取方式。单线程轮询方式容易实现异步非阻塞式,这样便于将消费者扩展成支持实时流处理的操作算子。因为很多实时流处理操作算子都不能是阻塞式的。另外一个可能的好处是,可以简化代码的开发。多线程交互的代码是非常容易出错的。

简述Follower副本消息同步的完整流程
首先,Follower发送FETCH请求给Leader。
接着,Leader会读取底层日志文件中的消息数据,再更新它内存中的Follower副本的LEO值,更新为FETCH请求中的fetchOffset值。
最后,尝试更新分区高水位值。Follower接收到FETCH响应之后,会把消息写入到底层日志,接着更新LEO和HW值。
Leader和Follower的HW值更新时机是不同的,Follower的HW更新永远落后于Leader的HW。这种时间上的错配是造成各种不一致的原因。

因此,对于消费者而言,消费到的消息永远是所有副本中最小的那个HW。

broker配置

常规配置

broker.id
在集群中必须保证此数字唯一,可以根据自身集群地理位置、机架、机房等情况配置,例如机房A,100-199;机房B,200-299等规则

port、zookeeper.connect
端口(>1000)与zookeeper连接配置

log.dirs
指定消息存储的路径,可以设置多个,原则上,将同一分区的日志片段保存在同一路径下,broker会往拥有最少数目分区的路径新增分区

num.recovery.threads.per.data.dir
用于服务启动时,加快日志处理过程,包括日志的检查、偏移量的确认和设置
与log.dirs相关,如果dirs设置两个路径,该参数设置为4,则,启动时总共有8个线程并行处理日志检查工作,加快重新启动时恢复的工作

Topic相关(默认情况下,如果主动创建Topic时指定了不同参数,以创建的参数为准)

num.partitions
指定新建主题的默认分区数(只能增加分区数,不能减少)
分区应该考虑的因素:
- Topic的吞吐量
- 单个分区的吞吐量
- broker的分区个数,可用磁盘和带宽
- 单个broker的分区数上限

log.retention.ms
通常根据时间决定数据的可以被保留多久

log.retention.byte
通过保留的字节数来判断消息是否过期

log.segment.bytes
日志片段的大小上限,超过此值,则会触发日志分段操作
日志片段关闭之前消息是不会过期的,举个例子说明,一个主题每天接受100m的消息数据,日志段默认值(1G),则需要10天才能填满一个日志段,如果log.retention.ms设置为604800000(1周),那么日志片段最多需要17天才会过期
同样说明,log.retention对应的是已经不再使用的日志段的存活时间或者上限,所以,在设置时需要考虑kafka集群的其他Topic的情况进行设置

log.segment.ms
通常不设置此参数,用于控制一个日志片段关闭的时间参数,设置此参数,需要考虑并行关闭多个日志片段对磁盘性能的影响(问题2

另外,broker有3个配置参数会影响kafka消息存储的可靠性

复制系数

主体级别的配置参数是replication.factor;在broker级别则是通过default.replication.factor来配置自动创建的主题
更高的复制系数会带来更高的可靠性、可用性和更少的故障;复制系数N需要至少N个broker,N份的数据复制,会占用N-1倍的磁盘空间;需要在可用性和存储硬件之间做出权衡
保证broker的合理分布,可使用broker.rack参数设置机架名称,尽量让broker分布在不同机架上

不完全首领选举

unclean.leader.election只能在broker级别设置,默认值是true,不允许不同步的副本成为首领
如果不允许,降低了可用性,允许则会出现数据丢失的情况

最少同步副本

主题和级别上,min.insync.replicas
设置副本同步情况,根据实际使用场景设置,可在主题级别上控制可用性

JVM相关设置

推荐使用G1,G1会对工作负载情况进行自我调节,而且它的停顿时间是恒定的,并且可以轻松处理大块内存,配置项较少但关键
MaxGCPauseMillis(200ms)
指定默认的停顿时间,不是固定的,G1会根据需要调整

InitiatingHeapOccupancyPercent(45)
设定执行GC操作的内存使用占比阀值

生产实例:
服务器内存64G,分配5G的堆内存运行Kafka,参考配置:MaxGCPauseMillis:20ms,InitiatingHeapOccupancyPercent:35,让垃圾回收比默认早启动,快速清除少量的内存垃圾

kafka的默认启动脚本采用Parallel New和CMS,需要修改kafka的启动脚本的参数

可靠系统中使用生产者

生产者消息提交

使用异步和回调接口提交消息,注意处理好回调,对可重试错误进行提交重试,记录不可重试的错误
保存死信消息

发送确认

acks=0意味着只要生产者将消息发出,即可认为是成功写入Kafka(基准测试基于此配置),运行速度非常快
acks=1意味着只要首领确定接受消息后就返回写入成功
acks=all意味着所有的副本确定接受消息,才返回写入成功,结合最少同步副本可优化性能;通过使用异步模式和大批次消息提交模式加快速度,但这样做通常会降低吞吐量

配置生产者的重试参数

遇到可重试错误时,生产者进行重试操作

额外的错误处理
- 不可重试的broker错误
- 消息发送至前的错误例如序列化错误
- 生产者达到重试次数上限
生产者本地消息缓存问题

如果对消息的及时性没有要求,尽量使用消息缓存机制,减少在多生产者情况下频繁提交修改zookeeper偏移量的操作
及时性要求高的情况下,对topic的消息样本做最大化大小确认,设置比消息最大值稍大的提交缓冲内存大小
一些生产者的参数设置参考(yml文件):


spring:
  kafka:
    producer:
      batch-size: 65536
      # 发送端批量发送的的缓存大小,默认是16kB,意思是缓存中的数据达到配置的数值大小,kafka的生产端发送数据
      # kafka.producer.batch.size小,吞吐量低,延时低,kafka.producer.batch.size大,吞吐量高,延时高
      buffer-memory: 33554432
      # 指定producer端用于缓存消息的缓冲区的大小,单位是字节,默认32M,采用异步发送消息的架构,
      # Java版Producer启动时会首先创建一块内存缓冲区用于保存待发送消息,然后由另一个专属线程负责从缓冲区中读取消息执行真正的发送,这部分内存空间的大小就是由buffer-memory参数指定
      # 查看消息大小情况进行设置,可做到直接发送
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      retries: 3
      acks: all
      properties:
        connections:
          # 设置生产者关闭连接空闲时间,默认:9 * 60 * 1000 = 540000;设置为-1表示长连接
          max:
            idle:
              ms: -1

配置解析:
buffer.memory,设置生产者内存缓冲区大小,生产者用它缓冲要发送到服务器的消息
compression.type,默认情况下消息发送不会被压缩;该参数可设置为snappy、gzip或lz4
retries,重发消息次数,遇到临时性问题时,此参数可解决问题
batch.size,指定了同一批次可以使用的内存大小
linger.ms,指定生产者在发送批次之前等待更多消息加入批次的时间,对于延迟要求小的,可将此参数设置小些

分区问题

分区器,可以自定义,根据消息的需要重写逻辑
无分区设置,默认采用轮询的方式提交消息

在可靠的系统中使用消费者

消费者的可靠性配置

group.id,如果是多消费者消费同一主题消息,可设置相同group.id;如果希望消费所有消息,设置不同的group.id
auto.offset.reset,该参数制定了子啊没有偏移量可提交时(消费者第一次启动时)或者请求的偏移量在broker不存在时,消费者在两种参数下的行为:earliest,消费者会从分区的开始位置读取数据,这样消费者会读取大量重复数据,但保证最少消息的丢失;latest,消费者从分区的末尾开始读取数据,减少消息重复处理,但可能错过一些消息
enable.auto.commit,自动提交偏移量或者由消费者手动提交偏移量
auto.commit.interval.ms,自动提交偏移量时,可通过该参数设置提交频度,默认值是5s提交一次

显示提交偏移量

使用回调API消费消息,在确保完成消息消费逻辑后,提交偏移量,注意消费逻辑中需要复合幂等处理的要求(结合业务额外生成唯一标识序列号或者其他)
ps:在处理时,对于幂等处理下产生的错误,可以抛出自己封装的错误,在回调处理时,捕获到幂等封装错误,就直接提交偏移量不做重试处理

原则:
- 总在处理完时间之后再提交偏移量
- 提交频度是性能和重复消息数量之间的权衡
- 确保对提交的偏移量心中有数
- 再均衡(在设计时需考虑消费者再均衡问题)
- 消费者可能需要重试(可重试错误时提交最后一次成功的偏移量或者将错误写入另外一个主题中)
- 消费者可能需要维护状态
- 长时间处理
- 仅仅一次传递(注意保证消费消息的幂等性)

消费者从属于消费者群组,一个群组的消费者所有消费的信息就是这个主题消息的全集

再均衡,消费者群组的消费者变动或者主题分区变动情况下发生,为消费者群组带来了高可用性和伸缩性,但会造成整个群组在一小段时间的不可用
由群组协调器的broker在消费者变动后几秒钟启动再均衡分配
消费者轮询,第一次调用新消费者的poll()方法时,会查找群组协调器,并加入群组接受分配分区,还包括心跳发送

消费者端配置参考:


spring:
  kafka:
    consumer:
      #      auto-commit-interval: 100ms
      enable-auto-commit: false
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      properties:
        session:
          timeout:
            ms: 15000
      max-poll-records: 2000
      auto-offset-reset: earliest

fetch.min.bytes,告诉kafka,足够多的的数据时才返回给消费者
fetch.max.wait.ms,每一个批次等待最长时间
max.partition.fetch.bytes,服务器从每个分区返回给消费者的最大字节数
session.timeout.ms,发送心跳的最大时间间隔
partition.assignment.strategy,分区分配的策略
max.poll.records,控制单次调用call()方法能够返回最大的记录数量

序列化

TODO

Avro
Protocol Buffer

监控

TODO

消息积压

kafka-consumer-groups.sh 脚本可查看对应消费组中消费者的偏移量情况


[root@master bin]# kafka-consumer-groups.sh --zookeeper localhost:2181 --group console-consumer-28542 --describe
GROUP                          TOPIC                          PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             OWNER
console-consumer-28542         test_find1                     0          303094          303094          0               console-consumer-28542_master-1539167387803-268319a0-0
console-consumer-28542         test_find1                     1          303068          303068          0               console-consumer-28542_master-1539167387803-268319a0-0
console-consumer-28542         test_find1                     2          303713          303713          0               console-consumer-28542_master-1539167387803-268319a0-0

另一种方法是基于监控中间件——Eagle

集群状态

测试

测试工具:kafka-producer-perf-test.sh

测试脚本语法及其参数解析

kafka-producer-perf-test.sh 脚本命令的参数解析(以100w写入消息为例):

  • –topic topic名称,本例为test_perf
  • –num-records 总共需要发送的消息数,本例为100000
  • –record-size 每个记录的字节数,本例为1000
  • –throughput 每秒钟发送的记录数,本例为5000
  • –producer-props bootstrap.servers=localhost:9092 (发送端的配置信息,本次测试取集群服务器中的一台作为发送端,可在kafka的config目录,以该项目为例:/usr/local/kafka/config;查看server.properties中配置的zookeeper.connect的值,默认端口:9092)

示例:

测试项压测消息数(单位:W)测试命令
写入MQ消息10./kafka-producer-perf-test.sh --topic test_perf --num-records 100000 --record-size 1000 --throughput 2000 --producer-props bootstrap.servers=localhost:9092
消费MQ消息10./kafka-consumer-perf-test.sh --broker-list localhost:9092 --topic test_perf --fetch-size 1048576 --messages 100000 --threads 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值