【中间件开发】kafka使用场景与设计原理
文章目录
- 前言
- 一、MessageQueue
- 1.1 定义
- 1.2 使用消息队列的场景
- 1.2.1 异步处理
- 1.2.2 流量控制
- 1.2.3 服务解耦
- 1.2.4 发布订阅
- 1.2.5 高并发缓冲
- 1.3 基本概念和原理
- 1.3.1 点对点消息队列模型 -- 线程池
- 1.3.2 发布订阅消息模型-Topic --上课通知
- 1.3.3 消息的ACK确认机制
- 二、Kafka
- 2.1 Kafka的架构设计及名词解释
- 2.2 工作流程
- 2.2.1 Producer
- 分区策略
- 2.2.2 Consumer
- 消费方式
- 分区分配策略
- 2.3 partition和topic的关系
- 总结
前言
本文首先介绍了消息队列,然后详细阐述了Kafka的架构设计和工作流程。
一、MessageQueue
1.1 定义
消息 + 队列 (Message + Queue) 简称 MQ。消息队列本质就是个队列,FIFO 先入先出,只不过队列中存放的内容是 Message,从而叫消息队列。消息队列的主要用途就是在不同的服务server、进程process、线程thread之间进行通信。
Q:那么是如何进行通信的呢?
1.2 使用消息队列的场景
1.2.1 异步处理
场景:短信通知、终端状态推送、App推送、用户注册等。
很明显,同步处理的性能远不如可并发的异步处理,多线程处理“库存”是趋势。
1.2.2 流量控制
场景:秒杀场景下的下单状态。
这种场景下网关生产的消息远大于服务器处理的速度,使用消息队列隔离网关和后端服务,以达到流量控制和保护后端服务的目的。
1.2.3 服务解耦
可以使用消息队列将各系统进行隔离,以达到修改更少的代码的目的,逻辑层次也更清晰。
使用消息队列:
消息队列在这里充当了一个中介的位置,各系统只需分清生产和消费的位置即可通过消息队列进行通信。
1.2.4 发布订阅
在一些跨服游戏中,当玩家对全服进行喊话,或者系统进行播报的操作。这些展现出来的消息就是通过消息队列,按着先进先出的规则播报出来的。
比如广播用户获得高阶武器的消息。
1.2.5 高并发缓冲
场景:kafka日志服务、监控上报
1.3 基本概念和原理
1.3.1 点对点消息队列模型 – 线程池
消息生产者向一个特定的队列发送消息,消息消费者从该队列中接收消息;一条消息只有一个消费者能收到;
1.3.2 发布订阅消息模型-Topic --上课通知
发布订阅消息模型中,支持向一个特定的主题 Topic 发布消息,0 个或多个订阅者接收来自这个消息主题的消息。 在这种模型下,发布者和订阅者彼此不知道对方。当发布者向这个主题发布消息,然后所有的订阅者会接收这个消息。
1.3.3 消息的ACK确认机制
为了保证消息的不丢失,消息队列中提供了消息的 ACknowledge 机制,即 ACK 机制。当消费者确认这个消息已经消费掉了,那么会向消息队列发送一个 ACK,消息队列收到后会将这个消息进行删除。但是当系统宕机,消息队列并未收到 ACK 的话,消息队列会认为这个消息并未被消费掉,便会将这个消息继续发送给其他的消费者重新处理。这样 ACK 的实时性会牺牲一定的吞吐量。
二、Kafka
解耦:允许我们独立的扩展或修改队列两边的处理过程。
可恢复性:即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
缓冲:有助于解决生产消息和消费消息的处理速度不一致的情况。
灵活性和峰值处理能力:不会因为突发的超负荷的请求而完全崩溃,消息队列能够使关键组件顶住突发的访问压力。
异步通信:消息队列允许用户把消息放入队列但不立即处理它。
典型应用:链接
2.1 Kafka的架构设计及名词解释
Producer:消息生产者,向 Kafka Broker 发消息的客户端。
Consumer:消息消费者,从 Kafka Broker 取消息的客户端。
Consumer Group:消费者组(CG),消费者组内每个消费者负责消费不同分区的数据,提高消费能力。一个分区只能由组内一个消费者消费,消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
Broker:一台 Kafka 机器就是一个 Broker。一个集群 (kafka C1uster) 由多个 Broker 组成。一 个 Broker 可以容纳多个 topic。
topic:可以理解为一个队列,topic 将消息分类,生产者和消费者面向的是同一个 topic。
Partition:为了实现扩展性,提高并发能力,一个非常大的 topic 可以分布到多个 Broker (即服务器)上,一个 topic 可以分为多个 Partition,同一个 topic 在不同的分区的数据是不重复的,每个 Partition 是一个有序的队列,其表现形式就是一个一个的文件夹。
Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候 会选择一个备胎(Follower)上位,成为 Leader。在 kafka 中默认副本的最大数量是 10 个,且副本的数量不能大于 Broker 的数量,follower 和 leader 绝对是在不同的机器,同一机器对同一个分区也 只可能存放一个副本(包括自己)。
Message:消息,每一条发送的消息主体。
Leader:每个分区多个副本的 “主” 副本,生产者发送数据的对象,以及消费者消费数据的对象,都是 Leader。
Follower:每个分区多个副本的 “从” 副本,实时从 Leader 中同步数据,保持和 Leader 数据的同 步。Leader 发生故障时,某个 Follower 还会成为新的 Leader。
Offset:消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从消费位置继续消费。同一主题,不同的分区,他们的 offset 是独立的。
ZooKeeper:Kafka 集群能够正常工作,需要依赖于 ZooKeeper,ZooKeeper 帮助 Kafka 存储和管理集群信息。
在这里还需要注意,一个Partition下有多个segment,segment下由.log和.index文件组成,.index文件用于索引.log中的最终文件位置。
2.2 工作流程
在上述工作图中可以发现副本被存储在不同的Broker中,这些副本同样需要和leader通过ACK机制保持通信。Kafka数据冗余主要是为了系统高可用和高持久性,而fastdfs、mongodb可以通过副本机制提高读的请求量。
2.2.1 Producer
Producer在写入数据时永远只会找leader。
分区策略
决定生产者将消息发送到那个分区的算法。
- 轮询策略:每个分区按顺序每次分配一条消息;
- 随机策略:随意地将消息放置到任意一个分区上;
- 按消息键保序策略:一旦消息被定义了 Key,那么你就可以保证同一个 Key 的所有消息都进入到相同的分区里面;
- 默认分区规则:
- 指明 Partition 的情况下,直接将给定的 Value 作为 Partition 的值。
- 没有指明 Partition 但有 Key 的情况下,将 Key 的 Hash 值与分区数取余得到 Partition 值。
- 既没有 Partition 有没有 Key 的情况下,第一次调用时随机生成一个整数(后面每次调用都在这个整数上自增),将这个值与可用的分区数取余,得到 Partition 值,也就是常说的 Round-Robin轮询算法
2.2.2 Consumer
传统的消息队列模型的缺陷在于消息一旦被消费,就会从队列中被删除(zeromq),而且只能被下游的一个Consumer消费。严格来说,这一点不算是缺陷,只能算是它的一个特性。但是这种模型的可伸缩性差。
Kafka使用Consumer Group机制,实现了两大模型:
- 如果所有实例(消费者)都属于同一个 Group,那么它实现的就是点对点消息队列模型;
- 如果所有实例(消费者)分别属于不同的 Group,那么它实现的就是发布 / 订阅模型。
消费方式
Consumer 采用 **Pull(拉取)**模式从 Broker 中读取数据。Pull 模式则可以根据 Consumer 的消费能力以适当的速率消费消息。Pull 模式不足之处是,如果 Kafka没有数据,消费者可能会陷入循环中,一直返回空数据。
因为消费者从 Broker 主动拉取数据,需要维护一个长轮询,针对这一点, Kafka 的消费者在消费数据时会传入一个时长参数 timeout。如果当前没有数据可供消费,Consumer 会等待一段时间之后再返回,这段时长即为 timeout。
分区分配策略
一个消费者组中有多个 consumer,一个 topic 有多个 partition,所以必然会涉及到 partition 的分配问题。
- Range:针对每个 topic。将 topic 中的分区与消费者排序,通过分区数/消费者数决定每个消费者消费几个分区,若除不尽则前面几个消费者会多消费1个分区。注意,如果有N个 topic,容易产生数据倾斜
- RoundRobin:针对集群中的所有 topic。把所有分区和所有的消费者都列出来,然后按照hashcode 进行排序,最后通过轮询算法来分配分区给到各个消费者
- Sticky:粘性分区从 0.11.x 版本开始引入,首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化
- CooperativeSticky: 在不停止消费的情况下进行增量再平衡。这与 Sticky 的逻辑相同,但具有增量支持。这种策略可能会产生不平衡的分配。
2.3 partition和topic的关系
-
一个分区只能属于一个主题
-
一个主题可以有多个分区
-
同一主题的不同分区内容不一样,每个分区有自己独立的 offset
-
同一主题不同的分区能够被放置到不同节点的 broker
-
分区规则设置得当可以使得同一主题的消息均匀落在不同的分区
为什么会分区? ---- 可以水平扩展
Kafka 的消息组织方式实际上是三级结构:主题 - 分区 - 消息。主题下的每条消息只会保存在某一个分区中,而不会在多个分区中被保存多份。
分区的作用主要提供负载均衡的能力,能够实现系统的高伸缩性(Scalability)。
总结
本文首先介绍了消息队列,然后详细阐述了Kafka的架构设计和工作流程。Kafka通过自身的分区策略、副本冗余机制、ACK确认机制等保证了消息队列的高可用、高性能和高伸缩性。
参考链接:
https://github.com/0voice