目录
在大数据数据收集环节,需要构建数据流水线,其中一种方式可以通过消息队列实现。在实际应用中,不同服务器产生的日志,比如指标监控数据,用户搜索日志,用户点击日志等,需要同时传送到多个系统中以便进行相应的逻辑处理和挖掘。
消息队列位于生产者和消费者之间的“中间件”,解除了生产者和消费者的直接依赖关系,使得软件架构更容易扩展和伸缩;能够缓冲生产者产生的数据,防止消费者无法及时处理生产者产生的数据。
Kafka设计动机
随着业务复杂度和产生数据量不断增加,逐渐演化成上图数据收集模式,并将收集到数据直接写入后端,则会产生以下几个潜在问题:
- 数据生产者和消费者耦合度过高
- 生产者和消费者间数据处理速率不对等
- 大量并发的网络连接对后端消费者不够友好
为了解决以上问题,引入“中间件”,从这个角度理解Kafka 如下图所示(Kafka在数据流中扮演的角色):
- 消息中间件:避免生产者和消费者直接相互产生的彼此高度依赖,使得两者中任何一个由变化,都不会影响另一方。
- 消息队列:缓存生产者产生的数据,使得消费者可以重复消费历史数据;平滑生产者产生数据速度和消费处理数据速度的不对等。
- 发布订阅系统:消费者订阅某类主题的数据,当生产者产生对应主题的数据后,所有订阅者会快速获取到数据。随时增加新的消费者而无需进行任何系统层面的修改。
- 消息总线:所有收集到的数据会流经 Kafka,之后由Kafka分流后,进入各个消费者系统。
Kafka特点
Kafka是在大数据背景下产生,能应对海量数据的处理系统。具有以下特点:
- 高性能:相比RabbitMQ等消息队列,Kafka 具有更高的性能和吞吐率。
- 良好扩展性:采用分布式设计架构,数据经分片后写入多个节点,既可以突破单节点数据存储和处理的瓶颈,也可以实现容错等功能。
- 数据持久化:数据消息均会持久化到磁盘上,并通过多副本策略避免数据丢失,采用顺序写,顺序读,和批量写等机制,提升磁盘操作的效率。
Kafka设计架构
1. 基本架构
Kafka 架构由 Producer,Broker 和 Consumer 三类组件构成。
- Producer 将数据写入Broker,Consumer 从 Broker上读取数据进行处理,Broker构成了连接 Producer 和 Consumer 的“缓冲区”。
- Broker 和 Cunsumer 通过 Zookeeper 做协调和服务发现。
- 多个 Broker 构成一个可靠的分布式消息存储系统,避免数据丢失。
- Broker 中的消息被划分成若干个 topic ,同属于一个 topic 的所有数据按照某种策略被分成多个 partition 以实现负载分摊和数据并行处理。
- 采用 push-pull,即 Producer 将数据直接 “push” 给 Broker, 而 Consumer 从Broker “pull”数据,这种架构优势带来以下两点:
- Consumer 可以根据自己的实际负载和需求获取数据(自己主动去拉取消息进行消费)
-
Consumer 自己维护已读取消息的 offset 而不是由Broker 端维护,大大缓解 Broker 的压力,使得它更加轻量级。
2. Kafka各组件详解
1.producer
由用户使用 Kafka 提供的 SDK 开发的,Producer 将数据转化成 “消息”,并通过网络发送给 Broker。每条消息表示为一个三元组: <topic, key, message>
- topic:表示该条消息所属的 topic,划分消息的逻辑概念。一个 topic 可以分布到多个不同的 Broker 上。
- key:表示该消息的主键。根据主键将同一个 topic 下消息划分成不同的分区 ( partition ),默认基于哈希取模算法,用户可以自行设置分区算法。如下图演示 Producer 写消息的过程,假设 topic A 共分为 4 个 partition (创建 topic 时静态制定)。
- message:消息值,其类型为字节数组,可以是普通字符串,JSON对象,或者 JSON, Acro,Thrift或Protobuf等序列化框架序列化后的对象。
2. Broker
主要职责是接受 Producer 和 Consumer 的请求,并把消息持久化到本地磁盘。Broker一般有多个,组成一个分布式高容错的集群。
- Broker 以 topic 为单位将消息分成不同的分区 (partition),每个分区可以有多个副本,通过数据冗余的方式实现容错。当 partition 存在多个副本时,其中一个是 leader,对外提供读写请求,其余均是 follower,仅仅同步 leader 中的数据,并在 leader 出现问题时,通过选举算法将其中的某一个提升为 leader。
- Broker 能够保证同一个 topic 在同一个 partition 内部的消息是有序的,但无法保证 partition 之间的消息全局有序。在实际应用中,合理利用分区内部有序这一特征即可完成时序相关的需求。
- Broker 中保存数据是有有效期,一旦超过了有效期,对应的数据被移除以释放磁盘空间。在有效期期内,消息可以重复读取而不受限制。
- Broker 以追加的方式将消息写到磁盘文件中,且每个分区中消息被赋予了唯一整数标识,称之为 “offset” (偏移量)。
- Broker 仅提供基于 offset 的读取方式 ,不会维护各个 Consumer 当前已消费的 offset 值,而是由 Consumer 各自维护当前读取的进度。Consumer 读取数据时告诉 Broker 请求消息的起始 offset 值,Broker将之后的消息流式发送过去。如下图所示:
3.Consumer
- 主动从 Broker 拉取(pull)消息进行处理,这个机制大大降低了 Broker 的压力,使得 Broker 吞吐率很高。
- 每个Consumer 自己维护最后一个已读取消息的 offset,并在下次请求从这个 offset 开始的消息。
- 多个Consumer 构成一个Consumer Group,共同读取同一个 topic 中的数据,提高数据读取效率。Kafka 可自动为同一个Group 中的 Consumer 分摊负载,从而实现消息的并发读取,在某个Consumer发生故障时,自动将它处理的 partition 转移给同 Group 中的 Consumer 处理。
4.Zookeeper
在 Kafka 集群中,Zookeeper 担任分布式服务协调作用,Broker 和 Consumer 直接依赖于 Zookeeper 才能工作。
- Broker 与 Zookeeper:所有 Broker 会向 Zookeeper 注册,将自己的位置,健康状况,维护的 topic,partition等信息写入 Zookeeper ,以便其他 Consumer 可以发现和获取这些数据。
- Consumer 与 Zookeeper:Consumer Group 通过Zookeeper 保证内部各个 Consumer 的负载均衡,在某个 Consumer 或 Broker 出现故障时,重新分摊负载。Consumer (仅限于 height-level API,如果是 low-level API,用户需要自己保存和恢复offset)会将最近获取消息的 offset 写入 Zookeeper,以便出现故障重启后,能够接着故障前的断点继续读取数据。
3. Kafka关键技术点
1. 可控的可靠性级别
Producer 通过两种方式向 Broker 发送数据:同步 与 异步。异步方式通过批处理的方式,可大大提高数据写入效率。这两种方式均支持通过控制消息应答方式,在写性能与可靠性之间做一个较好的权衡。目前支持三种消息应答方式,通过request.required.ack 控制:
- 0:无需对消息进行确认,性能最高,但不能确保消息被成功接收并写入磁盘。
- 1:需要等到 leader partition 写成功后才会返回,但对应的 follower partition 不一定写成功。在性能与可靠性之间进行折中。
- -1:需要等到所有 partition 均写成功后才会返回。性能较低,但可靠性最高。
2. 数据多副本
Broker 允许为每个 topic 中的数据存放多个副本,以达到容错的目的。Kafka 采用强一致的数据复制策略。
3. 高效的持久化机制
直接将消息持久化到磁盘而不是内存中,这要求必须采用高效的数据写入和存储方式。Kafka Broker 将收到的数据顺序写入磁盘,并结合基于 offset 的数据组织方式,能达到高效的读速度和写速度。
4. 数据传输优化:批处理与zero-copy技术
为了优化 Broker 与 Consumer 之间的网络数据传输效率,Kafka引入了大量优化技术,典型的两个代表:
- 批处理:发送是将多条消息进行组装,同时对数据格式统一设计,避免数据格式转换带来开销。
- zero-copy技术:一般情况下,一条存储在磁盘的数据从读取到发送出去需要经过四次拷贝(内核状态 read buffer > 用户态应用程序 buffer > 内核态 socket buffer > 网卡NIC buffer)和两次系统调用。经过zero-copy技术优化后,数据只需三次拷贝(少了 用户态应用程序 buffer),且无需使用任何系统调用,大大提高数据发送效率。如下图所示:
5. 可控的消息传递语义
根据接受者受到重复消息的次数,将消息传递语义分为三种:
- at most once:不需要等待确认,消息可能被消费者成功接收,也可能丢失。
- at least once:需要等待确认,如未收到确认则会重发;保障消费者收到消息,但可能会收到多次。
- exactly once:消费者会且只会处理一次同一条消息。
实现第三种语义,常用技术手段有:
- 两段锁协议:分布式中常用的一致性协议。
- 在支持幂等操作下,使用 at least once 语义。Producer 与 Broker, Broker 与 Consumer 之间,均存在消息传递语义问题。
典型应用场景
1. 消息队列
与 RabbitMQ 和 ZeroMQ 等开源消息队列相比,Kafka 具有高吞吐率,自动分区,多副本以及良好容错性等特点,使得它非常适合大数据应用场景。
2. 流式计算框架的数据源
在流式计算框架,为了保证数据不丢失,具备“at least once”数据发送语意,通常在数据源中使用具有高性能的消息队列。
3. 分布式日志收集系统中Source或Sink
可与日志收集组件 Flume 或 Logstash 组合使用,担任 Source 或 Sink 的角色。
4. Lambda Architecture 中的 Source
同时为批处理和流式处理两条流水线提供数据源
注:内容主要整理自书本《大数据技术体系详解 原理、架构与实践》 董西成