本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
名词解释
- Broker:消息中间件处理结点,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群。
- Topic:一类消息,例如page view日志、click日志等都可以以topic的形式存在,Kafka集群能够同时负责多个topic的分发。
- Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。
- Segment:partition物理上由多个segment组成,下面2.2和2.3有详细说明。
- offset:每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息.
Segment的Index <--> DataFile对应关系物理结构

Consumer Offset确认
-
自动确认Offset(enable.auto.commit=true)
-
手工确认Offset(enable.auto.commit=false)
-
分区订阅
向特定Partition订阅消息。会失去partition的负载分担
-
外部存储
消费者自己定义offset存储位置,目的是让消费者将数据和offset进行原子性的存储。避免重复消费问题。(enable.auto.commit=false; seek(TopicPartition, long);//重置)
-
控制消费位置(跳跃性数据消费)
如时效性数据,只需要获取最近一段时间内的数据
-
控制消费流Consumption Flow Control
一个consumer同时消费多个分区,默认情况这多个分区的优先级是一样的。
-
多线程处理模型 Multi-threaded Processing
Kafka的Consumer的接口为非线程安全的。多线程共用IO,Consumer线程需要自己做好线程同步。
Consumer单线程、多线程模型优缺点
Consumer单线程模型
- 优点:
- 实现容易;
- 没有线程之间的协作。通常比下面的那种更快;
- 单分区数据的顺序处理;
- 缺点:
- 多个TCP连接,但是关系不大,kafka对自己的server自信满满;
- 太多的Request可能导致server的吞吐降低一丢丢;
- consumer数量受到分区数量限制,一个consumer一个分区;
Consumer多线程模型
- 优点:
- 一个consumer任意多的线程,线程数不用受到分区数限制;
- 缺点:
- 如果有保序需求,自己要加控制逻辑;
- 该模型中如果手动offset,自己要加控制逻辑;
- 一种可行的解决办法:为每个分区分配独立的存储,获取的数据根据数据所在分区进行hash存储。这样可以解决顺序消费,和offset的确认问题。
server.properties
apache kafka系列之server.properties配置文件参数说明
| 参数 | 说明 |
|---|---|
| broker.id=0 | 每个Broker在集群的唯一标识,要求正数。服务器ip变化时,broker.id没有变化不影响consumers的消息情况 |
| log.dirs=/data/kafka-logs | kafka数据的存放地址,多个地址用逗号隔开,分配到不同磁盘上可提高读写性能 |
| port=9092 | broker server服务端口 |
| message.max.bytes=6525000 | 表示消息体的最大大小,单位是字节 |
| num.network.threads=4 | broker处理消息的最大线程数,一般情况下数量为cpu核数 |
| num.io.threads=8 | broker处理磁盘IO的线程数,数值为cpu核数2倍 |
| background.threads=4 | 一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改 |
| queued.max.requests=500 | 等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,应该是一种自我保护机制 |
| host.name | broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置 |
| socket.send.buffer.bytes=100*1024 | socket的发送缓冲区,socket的调优参数SO_SNDBUFF |
| socket.receive.buffer.bytes=100*1024 | socket的接受缓冲区,socket的调优参数SO_RCVBUFF |
| socket.request.max.bytes=10010241024 | socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖 |
| log.segment.bytes=102410241024 | topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,会被topic创建时的指定参数覆盖 |
| log.roll.hours=24*7 | 这个参数会在日志segment没有达到log.segment.bytes设置的大小,也会强制新建一个segment会被 topic创建时的指定参数覆盖 |
| log.cleanup.policy=delete | 日志清理策略选择有:delete和compact主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖 |
| log.retention.minutes=300 / log.retention.hours=24 | 数据文件保留多长时间,存储的最大时间超过这个时间会根据log.cleanup.policy设置数据清除策略log.retention.bytes和log.retention.minutes或log.retention.hours任意一个达到要求,都会执行删除。有2删除数据文件方式:1)按照文件大小删除:log.retention.bytes 2)按照2中不同时间粒度删除:分别为分钟,小时 |
| log.retention.bytes=-1 | topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes。-1没有大小限log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖 |
| log.retention.check.interval.ms=5minutes | 文件大小检查的周期时间,是否触发log.cleanup.policy中设置的策略 |
| log.cleaner.enable=false | 是否开启日志压缩 |
| log.cleaner.threads=2 | 日志压缩运行的线程数 |
| ... | ... |
| ... | ... |
| ... | ... |
| ... | ... |
Producer端配置
| 参数 | 说明 |
|---|---|
| compression.type=none | gzip。默认发送不进行压缩,推荐配置一种适合的压缩算法,可以大幅度的减缓网络压力和Broker的存储压力 |
| buffer.memory=33554432 | Producer用来存放尚未发送出去的Message的缓冲区大小。缓冲区满了之后可以选择阻塞发送或抛出异常,由block.on.buffer.full的配置来决定。 |
| linger.ms=0 | Producer默认会把两次发送时间间隔内收集到的所有Requests进行一次聚合然后再发送,以此提高吞吐量,而linger.ms则更进一步,这个参数为每次发送增加一些delay,以此来聚合更多的Message。 |
| batch.size=16384 | Producer会尝试去把发往同一个Partition的多个Requests进行合并,batch.size指明了一次Batch合并后Requests总大小的上限。如果这个值设置的太小,可能会导致所有的Request都不进行Batch。 |
| acks=1 | 这个配置可以设定发送消息后是否需要Broker端返回确认。0—不需要进行确认,速度最快。存在丢失数据的风险。1-仅需要Leader进行确认,不需要ISR进行确认。是一种效率和安全折中的方式。 all-需要ISR中所有的Replica给予接收确认,速度最慢,安全性最高,但是由于ISR可能会缩小到仅包含一个Replica,所以设置参数为all并不能一定避免数据丢失。 |
| ... | ... |
Consumer端配置
| 参数 | 说明 |
|---|---|
| num.consumer.fetchers=1 | 启动Consumer的个数,适当增加可以提高并发度。 |
| fetch.min.bytes=1 | 每次Fetch Request至少要拿到多少字节的数据才可以返回。 |
| fetch.wait.max.ms:100 | 在Fetch Request获取的数据至少达到fetch.min.bytes之前,允许等待的最大时长。对应上面说到的Purgatory中请求的超时时间。 |
| ... | ... |
其他
- Kafka Consumer手动执行commit时实际上是对这个Consumer进程所占用的Partition进行commit
- Kafka 0.9.*版本中重写了Consumer API,Consumer维护了消费者当前状态,不是线程安全的
- 新的Consumer基于单线程模型,offset自动提交在poll方法中进行,0.9~0.10.0.1客户端的心跳也是在poll方法中进行,在0.10.1.0版本中,客户端心跳在后台异步发送
- 0.10.0.1版本中可以通过在consumer的props中设置max.poll.records来限制每回返回的最大数据条数
- 广播模式,相同的topic不同的group。如果相同的topic多个监听里相同的group,会造成只有一个监听能处理其中的消息
Command
- 增加分区
./bin/kafka-topics.sh --alter --topic eng_gds --zookeeper 127.0.0.1:2181 --partitions 8
- 分区迁移
一些问题
- session.timeout.ms时间内没有处理完,broker会认为Consumer挂掉
解决方案:1.提高partition数量,从而提高consumer的并行能力。2.降低一次获取数据量