使用场景
Kafka通常被用于诸如搜索服务、直播服务、订单服务、支付服务中来
- 处理一些日志信息;
- 对程序状态进行一个采集,获取Metrics数据,比如程序的QPS,查询写入的一些耗时;
- 对用户行为,如搜索、点赞、评论、收藏,产生的数据进行一个存放
如何使用Kafka
- 首先我们要创建一个集群
- 然后新增Topic,并且设置好分区的数量
- 引入Kafka的sdk,编写生产者逻辑,将消息发送给Kafka
- 编写消费者逻辑,将发送的消息从Kafka中拉取出来,并进行业务的处理
基本概念
- 其中 Topic 为逻辑队列,每一个不同的业务场景就是一个Topic,对于业务来说,所有的数据都存储在这个Topic中
- Cluster是物理集群,每个集群可以建立多个不同的Topic
- Producer是生产者,负责将业务消息发送给Topic
- Consumer是消费者,则是负责处理Topic中的消息
- Partition是 Topic 的一个分区,通常Topic会有多个分区,不同分区的消息可以 并发 处理,以此来提高单个Topic的吞吐能力
- Offset 每一个 Partition 内部会存储不同消息,对于每一个消息都会有一个唯一的Offset,其为消息在Partition内的相对位置信息,同时在Partiton内部严格递增
- Replica 每一个Partition会有多个副本,并遍布在集群中。其中分为不同的角色, Leader 负责进行对外的写入或是读取,而 Follower 则是会不断从Leader上把消息拉取下来,尽量去保持一致的状态,从而达到一个容灾的效果。而在Kafka中有一个特性ISR,即Leader和Follower之间的差距是有个限度的,如果两者之间差距过大,则该Follower会被踢出分区副本。如果Leader副本所在机器发生宕机,则会在剩余的Follower中选取一个作为新的Leader,保证高可用性
数据复制
其中的Broker:Controller负责对整个集群的Broker及副本进行分配
Kafka架构
集群的上层会有一个ZooKeeper组件,它会和前面提到的Controller进行配合,负责存储集群元信息,包括分区分配信息等
一条消息的流程
接下来从一条消息的视角,看看为什么Kafka能支持这么高的吞吐
Producer
先来看看Producer这边
如果发送一条消息,等其成功后再发送一条会导致在有大量消息需要发送时,吞吐量远远不足
因而我们可以选择进行批量发送,减少IO次数,从而加强发送能力
但如果消息量很大,网络带宽可能会不够用,这时该怎么办
Kafka中有着压缩机制,通过压缩,可以减少消息大小
Broker
Broker写入消息
随后Broker将消息存储到本地磁盘
Broker会采用顺序写的方式写入,以提高写入效率。对于每一个消息都会采用末尾添加的方式,不会去动磁头,以此来减少寻道的成本
现在我们已经将消息发送到Broker,并通过Broker写入到磁盘中,那么要如何读取到消息呢
Consumer 会向 Broker 发送一个 FetchRequest 来请求消息数据,然后Broker会将 指定Offset 处的消息返回给Consumer
Broker找到指定消息
那么Broker又是如何寻找到指定的消息呢
比如说我们要在下方文件中找到 Offset=28的消息
Broker会通过二分找到小于目标 Offset即28的 最大文件,也就是最终会找到6.log文件
随后进入到文件,同样是通过二分找到小于目标 Offset即28 的最大索引位置
最终找到firstOffset=26的索引
在数据找到后,Broker要将消息发送给Consumer,在Kafka中应用到了零拷贝技术
零拷贝技术调用了系统架构里sendFile的系统调用,这样从磁盘空间读取数据到内核空间,内核空间可以把整个数据发送给网卡,网卡就可以直接发送给消费者进程,就不需要再经过 应用空间 以及 SocketBuffer 的数据拷贝。从而降低了三次内存拷贝的次数
Consumer
Consumer分配Partition方式
Consumer首先需要解决Partition在Consumer Group在的分配问题
通常会有两种解决方案,一种是手动分配,一种是自动分配
- 手动分配比较简单,通常在Producer启动时就将其要消费的Partition在代码中配置好,哪一个Consumer消费哪一个Partition完全由业务决定。缺点在于如果其中摸一个Consumer挂掉,其对应的Partition顺序流也直接就断掉了。无法自动去容灾。再者如果要新增Consumer,为了能够拉取到Partition,需要将之前的Consumer也停掉,重新启动,中间有一个机器的启停,就会有数据中断的问题
- 自动分配会对不同的Consumer Group会选取其中一个作为Coordinator,即协调者,可以帮助某一个Group中的Consumer去进行自动分配,如果其中某一个Consumer宕机,Coordinator会自动感知到,会进行重新分配。以此达到高可用目的
Consumer Rebalance
- 初始的Consumer会向Broker集群发送一个FindCoordinatorRequest来找到Coordinator
- 随后Consumer会发起第二轮请求,此时会向Coordinator发送JoinGroupRequest来请求加入到Group
- Coordinator会返回响应,并选取一个Consumer作为Leader
- Consumer发起第三轮请求,向Coordinator发送SyncGroupRequest并由Leader携带分配方案发送,Coordinator最终会把分配方案告诉每一个Consumer
- 每个Consumer以一定间隔向Coordinator发送一个HeartTestRequest即心跳检测请求,如果在一定时间间隔内某一个Consumer没有发送请求,则Coordinator认为该Consumer宕机,将其剔除,并重新走一遍Rebalance流程
总结
总共有哪些可以帮助Kafka提高吞吐或者稳定性的功能
- Producer:批量发送(减少IO次数)、数据压缩(降低带宽流量)
- Broker:顺序写、消息索引、零拷贝
- Consumer:Rebalance
Kafka的问题
- 运维成本高
- 对于负载不均衡的场景,解决方案复杂
- 没有自己的缓存,完全依赖 Page Cache
- Controller 和 Coordinator 以及 Broker 在同一进程中,大量IO会造成其性能下降