消息中间件系列介绍-Kafka

开发
在高并发分布式场景下,合理地利用消息中间件往往能起到突破性能瓶颈与化繁为简的效果。

作者 | 葛贤亮,单位:中国移动智慧家庭运营中心

​Labs 导读

近年来,互联网技术发展迅猛,各行各业的信息量急剧膨胀。随着云计算和算力网络时代的到来,消息中间件在国内许多行业的关键应用中越来越受到重视。在高并发分布式场景下,合理地利用消息中间件往往能起到突破性能瓶颈与化繁为简的效果。

前期分别从“作用”与“协议”、“传输模式”与“消费模式”对消息中间件技术做了简要的介绍。本期从消息中间件产品角度介绍主流方案的设计与实现。

1、概念介绍

Apache Kafka是一种高吞吐量、分布式、多副本、基于发布/订阅的消息系统,最初由LinkedIn公司开发,使用Scala语言编写,目前是Apache的开源项目。Kafka已成为事件流处理动态数据的事实标准。

1.1 主要特性

  • 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒;
  • 可扩展性:kafka集群支持热扩展,数据迁移、扩容对用户透明;
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失;
  • 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败);
  • 高并发:支持数千个客户端同时读写;
  • 分布式架构:Broker、Producer和Consumer都原生自动支持分布式,自动实现负载均衡;
  • 支持同步和异步复制两种高可用机制;
  • 支持数据批量发送和拉取;
  • 零拷贝技术(zero-copy):减少 IO 操作步骤,提高系统吞吐量;
  • 其他特性:丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制。

1.2 Kafka优点

  • 客户端多语言支持:支持Java、.Net、PHP、Ruby、Python、Go等多种语言;
  • 高性能:单机写入TPS约在100万条/秒,消息大小10个字节;
  • 分布式架构,并有replica机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积;
  • 支持批处理操作;
  • 消费者采用Pull方式获取消息。单分区内消息有序,通过控制能够保证所有消息被消费且仅被消费一次;
  • 在日志领域比较成熟,被多家公司和多个开源项目使用。

1.3 Kafka缺点

  • Kafka单机超过64个分区时,load时会发生明显的飙高现象。队列越多,负载越高,发送消息响应时间变长;
  • 使用短轮询方式,实时性取决于轮询间隔时间,对于不能批处理的消息,需考虑消费线程执行效率;
  • 需要引入ZooKeeper,部署成本相比其他MQ较高;
  • 不能保证消息100%到达,不支持事务消息。

1.4 主要应用场景

  • 消息系统:分布式消息系统,解耦生产者和消费者;
  • 日志收集:Kafka常与ELK(Logstash、ElasticSearch、Kibana)一起作为业务系统日志收集方案;
  • 业务埋点:对于可靠性要求不那么高的埋点数据(如浏览网页、点击、跳转等),可使用Kafka进行传输,消费者端收到消息后可根据需求做实时监控分析或装载到hadoop、数据仓库中做离线分析和挖掘;
  • 运营指标:Kafka可用来传输运营监控数据,以便统一归集分析、集中反馈;
  • 流式处理:Kafka提供了完整的流式处理类库,可以很方便的被集成至应用程序中,为流式处理框架(Flink、Spark、Storm等)提供可靠的数据来源。

1.5 Kafka为什么这么快?

Kafka可以轻松支持每秒百万级的写入请求,超过了大部分的消息中间件,这种特性使得Kafka在日志处理等海量数据场景得到广泛应用。

  • 并行处理:Kafka引入了Partition(分区)的概念,每个Topic 可包含一个或多个Partition,不同Partition可位于不同节点中,从而实现多磁盘并发读写;
  • 顺序读写:Kafka中每个Partition是一个有序、不可变的消息序列,新的消息只会被追加到Partition的末尾,而一个Partition又被分为多个Segment,清除旧数据时可直接删除Segment文件,避免随机写;
  • 页缓存(Page Cache):Kafka使用页缓存技术减少I/O操作次数,即使Kafka进程重启数据也不会丢失(机器宕机时,页缓存内的数据未及时写入磁盘会导致数据丢失,同步刷盘可以规避该问题,但会影响性能,默认使用异步刷盘机制);
  • 零拷贝技术:Kafka使用零拷贝技术,避免数据在内核空间的缓冲区和用户空间的缓冲区之间进行拷贝;
  • 批处理:Kafka支持批处理操作,以减少网络I/O操作;
  • 数据压缩:Kafka支持Snappy、Gzip、LZ4等算法对数据进行压缩传输。

2、架构设计

图片

图1 架构设计

3、核心概念

  • Producer:生产者,用来向Kafka Broker中发送数据(Record);
  • Kafka Cluster:Kafka集群,由一台或多台服务器组成;
  • Broker:Broker是指部署了Kafka实例的服务器节点,每个服务器上可安装一个或多个Kafka实例。每个Kafka集群内的Broker都有一个不重复的编号(如broker-0、broker-1等);
  • Topic:消息主题,用来区分不同类型信息。在每个Broker上可以创建多个Topic;
  • Partition:Topic的分区,每个Topic可以有一个或多个 Partition(分区),分区可实现负载均衡,支持并发写入读取,提高Kafka的吞吐量。一个分区内的数据只能被一个线程消费;
  • Replication:每一个分区可以有多个副本。当主分区(Leader)故障的时候会选择一个副本(Follower)成为新的Leader。在Kafka中副本的默认最大数量是10个,且副本的数量不能大于Broker的数量,Follower和Leader必须分布在不同的机器上,同一机器上同一分区只能存放一个副本(包括自己)。
  • Record:消息记录。每个Record包含了key、value和 timestamp;
  • Consumer:消费者,用来读取Kafka中的数据(Record)进行消费;
  • Consumer Group:消费者组,一个消费者组可以包含一个或多个消费者。在Kafka的设计中一个分区内的数据只能被消费者组中的某一个消费者消费,同一个消费者组的消费者可以消费某个Topic不同分区的数据;
  • Segment:实际存储消息的片段;一个Partition在物理上由一个或者多个Segment构成,每个Segment中保存真实的消息数据。

4、工作流程

图片

图2 工作流程

Kafka一般工作流程如下(根据ACK应答策略会存在部分差异):

  • 生产者与Leader直接交互,先从集群获取Topic对应分区的Leader元数据;
  • 获取到Leader分区元数据后进行消息发送;
  • Kafka Broker对应的Leader分区收到消息后写入文件进行持久化;
  • Follower拉取Leader消息,进行数据同步;
  • Follower完成消息拉取后给Leader回复ACK确认;
  • Leader和Follower分区完成数据同步后,Leader分区给生产者回复ACK确认。

👇 ACK应答机制

通过配置request.required.acks属性来配置ACK策略:

  • 0代表生产者往集群发送数据不需要等待集群的返回,不确保消息是否发送成功。安全性最低但是效率最高。
  • 1(默认)代表生产者往集群发送数据只要Leader应答就可以发送下一条,只确保Leader发送成功(Leader不需要等待Follower完成数据同步即返回生产者ACK确认)。
  • all代表生产者往集群发送数据需要所有的Follower都完成数据同步才会发送下一条,确保Leader发送成功和所有的副本都完成备份。安全性最高,但是效率最低。

5、Kafka数据存储设计

5.1 Topic和数据日志

Topic是同一类别的消息记录(Record)的集合。在Kafka中,一个Topic又可以被划分成多个Partition,分区数据日志文件结构如下:

图片

图3 Topic和数据日志-分区数据日志文件结构

每个Partition是一个有序、不可变的消息序列,新的消息只会被追加到Partition的末尾。在每个Partition中,通过offset(偏移量)标识消息。由此可见,在同一个Partition内消息是有序的,在不同Partition之间,不能保证消息被有序消费。

Kafka可以通过log.retention配置项设定消息日志在集群内的留存时间,默认为168小时(即7天)。

5.2 Partition结构

Partition在服务器上是以文件夹形式存在的,每个Partition文件夹内会有多组Segment文件,每组Segment文件又包含.index、.log、.timeindex三个文件,其中.log是实际存储消息日志的地方,而.index和.timeindex为索引文件,用于检索消息。

Q:为什么有了Partition还要有Segment?

Segment对应一个文件(实现上对应两个文件,一个数据文件,一个索引文件),一个Partition对应一个文件夹,一个Partition内理论上可以包含任意多个Segment。

如果不引入Segment ,所有消息日志都直接写在Partition文件内,会导致Partition文件一直增大。同时,在做data purge时,需要把文件的前面部分给删除,不符合Kafka文件的顺序写优化设计方案。引入Segment后,消息日志被分散在多个Segment中, 每次做data purge,只需要把旧的Segment整个文件删除即可,保证了每个Segment的顺序写。

5.3 Partition的数据文件(offset、MessageSize、data)

Partition中的每条消息包含三个属性:offset、MessageSize、data,其中offset表示消息在这个Partition中的偏移量,offset 不是实际存储位置,而是逻辑上一个值,用来唯一标识Partition内的一条消息,相当于消息id;MessageSize表示消息内容data的大小;data为消息的具体内容。 

5.4 数据文件分段Segment(顺序读写、分段命令、二分查找)

Partition物理上由多个Segment文件组成,每个Segment大小相等(大致)。每个Segment数据文件以该段中最小的offset命名,文件扩展名为.log。这样在查找指定offset消息的时候,用二分查找法可以快速定位到该消息在哪个Segment数据文件中。 

5.5 数据文件索引(分段索引、稀疏存储)

Kafka为每个Segment建立了索引文件(文件名与数据文件一致,扩展名为.index)。具体是采用稀疏索引方式,每隔一定字节的数据建立一条索引。这样做的好处是减少了索引体积,以便保留在内存中;坏处是查询时命中需要消耗更多时间(相对)。

图片

图4 数据文件索引-稀疏索引

6、生产者&消费者设计

6.1 负载均衡(Partition会均衡分布到不同Broker上) 

由于一个Topic可以有多个Partition,不同Partition均衡分布在不同的Broker上。基于该特性,生产者可通过随机、轮询或hash等策略,将消息平均发送至多个Broker中,实现负载均衡。

6.2 批量发送

生产者在本地内存中进行消息聚合,以单次请求发送批量数据的方式,减少网络I/O操作(副作用是一定程度上会影响消息实时性,以时延换取吞吐量)。

6.3 压缩(Snappy、Gzip、LZ4)

生产者通过Snappy、Gzip、LZ4等算法对数据进行压缩传输,减少传输数据量,减轻网络压力(以CPU资源换取网络时延的降低)。

7、总结

Kafka凭借其架构与性能优势,愈来愈受到众厂商的青睐。依托其完善的社区环境,Kafka构建了庞大而成熟的生态,已成为大数据及流计算领域中至关重要的一环。

责任编辑:未丽燕 来源: 移动Labs
相关推荐

2022-10-21 10:48:17

消息中间件互联网应用协议

2022-10-28 13:33:05

Push模式互联网高并发

2022-02-13 23:04:28

RedisRabbitMQKafka

2022-08-09 08:31:29

RocketMQ消息中间件

2023-06-29 10:10:06

Rocket MQ消息中间件

2023-10-24 07:50:18

消息中间件MQ

2021-12-14 10:39:12

中间件ActiveMQRabbitMQ

2015-08-11 11:16:36

淘宝中间件

2021-10-06 19:03:35

Go中间件Middleware

2020-11-13 07:14:55

Kafka消息中间件

2019-01-04 09:59:14

KafkaRabbitMQMQ

2023-05-08 08:09:26

路由元信息谓词

2020-10-10 08:04:09

RabbitMQ消息中间件

2024-01-24 08:19:02

Stream应用场景注解

2022-11-24 15:19:16

Connect架构消息中间件

2019-07-19 07:56:13

消息队列消息代理消息中间件

2023-09-08 10:02:14

Linux系统

2023-10-16 12:25:48

2022-09-21 16:09:28

消息中间件

2019-11-18 09:58:11

中间件投递模式
点赞
收藏

51CTO技术栈公众号