带你了解大数据消息流系统Kafka

Kafka前世今生

随着大数据时代的到来,数据中蕴含的价值日益得到展现,仿佛一座待人挖掘的金矿,引来无数的掘金者。但随着数据量越来越大,如何实时准确地收集并分析如此大的数据成为摆在所有从业人员面前的难题。

为了解决大数据流式处理中面临的巨大数据吞吐量的难题,LinkedIn公司开发了Kafka作为其活动流和运营数据处理的消息管道。作为全球最大的职业社交网站,LinkedIn会员人数在世界范围内已超过3亿,Kafka作为一款消息服务,为其系统数据的稳定运行做出了巨大的贡献,因此Kafka的性能和可靠性也得以验证。

LinkedIn与2011将其开源并捐献给Apache基金会,并与2012年正式成为Apache的顶级项目,目前官方最新版本为2.0。

初识Kafka

首先,Kafka作为一个分布式的流平台,具有三个关键能力:

1. 发布和订阅消息(流),在这方面,它类似于一个消息队列或企业消息系统。

2. 以容错的方式存储消息(流)(多副本存储)。

3. 在消息流发生后及时处理它们(流式处理)。

Kafka对比主流MQ

这里主要与ActiveMQ和RabbitMQ做对比。

吞吐量对比:

Kafka最高,RabbitMq 次之, ActiveMq最差。

Kafka内部采用消息的批量处理,zero-copy机制,数据的存储和获取是本地磁盘顺序批量操作,具有O(1)的复杂度,消息处理的效率很高。

RabbitMQ在吞吐量方面逊于Kafka,他们的出发点不一样,RabbitMQ支持对消息的可靠的传递,支持事务,不支持批量的操作;基于存储的可靠性的要求存储可以采用内存或者硬盘。

在架构模型方面:

RabbitMQ遵循AMQP协议,RabbitMQ的Broker由Exchange、Binding和Queue组成,其中Exchange和Binding组成了消息的路由键;客户端Producer通过连接Channel和Server进行通信,Consumer从Queue获取消息进行消费(长连接,Queue有消息会推送到Consumer端,Consumer循环从输入流读取数据)。RabbitMQ以Broker为中心;有消息的确认机制。

Kafka遵从一般的MQ结构,Producer、Broker和Consumer,以Consumer为中心,消息的消费进度(offset)由Consumer维护(在0.9.0以后的版本中,提交到一个特殊的Topic中),Consumer根据消费的offset,从Broker上批量pull数据;无消息确认机制。

在可用性方面:

RabbitMQ支持mirror的Queue,主Queue失效,mirror Queue接管。

Kafka的Broker支持主备模式。

ActiveMQ也支持主备模式。

在集群负载均衡方面:

Kafka采用Zookeeper对集群中的Broker、Consumer进行管理,可以注册Topic到Zookeeper上;通过Zookeeper的协调机制,Producer保存对应Topic的Broker信息,可以随机或者轮询发送到Broker上;并且Producer可以基于语义指定分片,消息发送到Broker的某分片上。

RabbitMQ的负载均衡需要单独的Loadbalancer进行支持。

下图展示Kafka与主流MQ的同步发送(注:Kafka还支持异步发送模式,性能比同步发送高的多)性能对比:

Kafka核心组件

Kafka的核心组件包括(如下图):

Producer,Kafka消息的生产者,下文介绍。

Kafka Cluster,Kafka服务(Broker,下文介绍)集群。

Consumer,Kafka消息的消费者,下文介绍。

Connector,Kafka的DB连接器,可链接DB作为Kafka消息的输入源和输出端。

Stream Processor,Kafka的流式处理组件。

由于篇幅限制,本文只介绍Kafka基本的的生产者和消费者模式(包含前3个组件,Connector和Stream Processor属于Producer和Consumer的高级应用模式)。

Kafka应用场景

1. 消息流服务。Kafka在很多大数据量的应用场景下能更好的替换传统的消息系统,消息系统被用于各种场景(解耦数据生产者,缓存未处理的消息等),与大多数消息系统比较,Kafka有更好的吞吐量,内置分区,副本和故障转移,这有利于处理大规模的消息。

2. 网站活动追踪。Kafka原本的使用场景:用户的活动追踪,网站的活动(网页游览,搜索或其他用户的操作信息)发布到不同的话题中心,这些消息可实时处理,实时监测,也可加载到Hadoop或离线处理数据仓库。每个用户页面视图都会产生非常高的数据量。

3. 数据指标监控。Kafka也常常用于监测数据。采集分布式应用程序生成的数据,集中数据并统计聚合。

4. 日志聚合。使用Kafka可以作为一种日志聚合的实时解决方案。

5. 数据流式处理(Stream Processor)。Kafka数据流式包含多个阶段。其中数据源是从Kafka的某个Topic开始订阅消费,然后汇总、计算,或者以其他的方式处理并将结果输出到其他的Topic。Kafka从0.10.0版本开始提供这个轻量、但功能强大的数据流式处理组件。

6. 分布式日志持久化。Kafka可以作为一种分布式的外部日志的载体,Kafka日志可以支持节点之间数据复制和同步,并作为失败的节点来恢复数据重新同步,Kafka的日志压缩功能很好的支持这种用法,这种用法类似于Apacha BookKeeper项目。另外,Kafka还可以用于分布式事务日志的持久化,以及提供分布式二阶段提交或者三阶段提交中的协调者和参与者之间可靠且持久化的通讯消息。

Hello Kafka

1. Kafka官方下载,2.12表示Scala版本

2. 解压、安装

3. 启动服务

a)启动Zookeeper

> ./bin/zookeeper-server-start.sh config/zookeeper.properties &

b)启动Kafka,如下:

4. 创建Topic(test)

5. 验证Topic是否创建成功

6. 启动Producer发送消息

7. 启动Consumer消费消息

也可以直接连接Borker-list,将--zookeeper localhost:2181替换成--bootstrap-server localhost:9092即可

到这里,我们就已经完成Kafka的单机部署,以及Kafka消息的发送和消费,恭喜,你的Hello Kafka已经完成了!

Kafka架构与分析

Kafka消息流的产生、持久化与消费模式如下图:

注:Kafka的Producer是线程安全的,多个Producer可根据需求定制化配置;Kafka集群包含多个Broker;多个Consumer会按照group id进行分组消费消息。

Kafka相关名词解释:

Broker

Kafka集群包含一个或多个服务器,这种服务器被称为Broker。

Topic

每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个Broker上但用户只需指定消息的Topic即可生产或消费消息流而不必关心数据存于何处)。

Partition

Parition是物理上的概念,每个Topic包含一个或多个Partition。

Producer

负责发布消息到Kafka Broker。

Consumer

消息消费者,向Kafka Broker读取消息的客户端。

Consumer Group

每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group id,若不指定group id则属于默认的group)。

Broker(Topic)端:

Kafka的Broker按照消息的Topic归类,以写日志的方式记录Producer发送的消息,对每个Topic,Kafka 对其进行了分区,如下图所示:

如上图所示,Kafka的Topic引入了Parition的概念,同时引入了zero-copy机制,消息以追加日志的方式写入每一个活动的Parition文件(注:每个Parition会对应这多个日志文件,只有活动的那个才是可写入的),这极大地提升了消息在Broker端的写入性能。

对于每一个Parition,为提高集群系统可用性,Broker一般都会对其进行冗余,一般配置副本数为3,也就是说,每个Topic下的每个Partition实际上会有3份,分别存放在不同的Broker上。但在多个副本中只有一个leader副本,其他副本(follower)只负责从leader上同步数据,若期间出现宕机等导致的leader不可用,则集群会从其他同步副本(follower副本中有同步副本和非同步副本之分,可简单以是否跟leader副本数据一致作为区分标准)中从新选举新的leader,以避免服务不可用和数据的丢失。

注:只有leader副本才会接受Producer的消息以及对Consumer和follower副本提供订阅服务,因此leader副本的负载远大于follower副本。新选举出来的leader会在原leader恢复(能正常工作,且完成数据同步)后,将leader的职能归还给原leader,这么做应该也是为了leader所在的Broker能够尽可能的均衡负载。

Producer端:

在Producer端,每个Topic的Parition都会对应一个Buffer(缓冲区),用来临时存储待发送到对应Parition的消息(经过分区和序列化后的消息流)批次,当达到设定的阈值(批次大小和等待时间达到一个即可)时,当前批量的消息流会转变为可发送批次,并发送到对应的Broker端的Parition,以减少网络请求次数;同时,消息发送超时或者Broker端返回的发送状态失败(判定成功与否的规则受Producer的acks参数影响),重试完毕或者消息发送成功会清除Buffer里发送成功或者因重试次数次数用完而放弃的批次的消息流。另外Kafka的消息流还支持压缩后发送到Broker端,以减小网络IO压力。如下图所示:

Consumer端:

Kafka的Consumer端以Consumer Group的方式订阅Broker中的一个或多个Topic,但值得注意的是,对于每个Topic下的每个Partition,只能被每个Group中的一个Consumer消费。

对于某一个消费组订阅某一个Topic,如果Consumer超过Topic的Partition数量,那么有一部分消费者就会被闲置,不会接收到任何消息,如下图所示:

对于消费群组里Consumer少于Partition数量的情况,会有一个Consumer消费多个Partition数据的情况。如果往群组里加入新的Consumer,此时会触发消费群组的分区再均衡(分区的所有权从一个消费者转移到另一个消费者,这样的行为被称为再均衡)。

开发初体验(Java版)

构建Java Maven项目,引入kafka-clients依赖:

Kafka Producer

Producer重要参数介绍:

bootstrap.servers:连接的broker-list,可以指定多个。

key/value.serializer:任何消息的key和value都会依据该参数指定的类转序列化成字节数组用于后续的发送。

acks:判断消息发送成功需要消息副本(也就是Partition副本,下文有介绍)的确认数;0表示立即返回成功,性能最高;1表示只需要得到leader副本的成功响应就返回成功,all表示需要得到所有的同步副本成功响应才能返回成功,性能最低。

Producer:消息生产者接口,K/V表示Kafka消息key/value类型,KafkaProducer是其具体实现类。Future send(ProducerRecord record[, Callback callback]),send()方法会返回一个包含RecordMetadata(包含消息的发送结果、Topic、Partition和offset等信息)的 Future对象, 如果不关心发送结果,此消息便是异步发送(该应用线程不需要同步发送线程的执行结果),或者消息的处理结果在callback中异步处理;反之,调用Future的get()方法,同步发送线程的执行结果,此消息便采用了同步发送方式。

ProducerRecord:Kafka消息的数据结构类,代表着具体的一条待发送的Kafka消息。K、V与Producer需要保持一致,ProducerRecord有几种重载的构造函数,这里不做赘述。其数据结构如下图:

Kafka Consumer

Consumer主要参数介绍:

bootstrap.servers:连接的broker-list,可以指定多个。

key/value.deserializer:将字节数组消息的key和value根据改参数指定的类反序列化成相应类型返回给消费端。

group.id:消费组id,组id相同的Consumer会加入到同一个group,在同一个group中,Producer发送的消息,只能被某一个Consumer消费一次,即point-to-point的传递方式;对于组id不同的consumer组成不同的group,一条消息会被广播到每一个Consumer Group,既publish/subscribe模式。

enable.auto.commit:true表示自动提交该Consumer Group的消费偏移量;false则由消费程序中自己维护分区偏移量,可在消费完成之后调用consumer.commitSync()或者consumer.commitAsync()提交本批次消息的最大偏移量,两个方法区别在于提交的方式是同步还是异步。

KafkaConsumer:Kafka消息的消费者,实现了Consumer接口,K/V分别表示消息的key/value类型。consumer.subscribe(Arrays.asList("test"))表示订阅一组Topic。consumer.poll(100)表示消费者从Broker获取消息,100表示一次轮询的超时时间,也就是说每次poll时,如果拉取到的消息没有达到指定的期望数,达到轮询的超时时间则立刻返回。

ConsumerRecord:代表着Consumer端获取到的Kafka消息的数据结构类,其数据结构如下图:

结语

可以看出Kafka在处理大数据的消息流方面,在高性能、高吞吐量和高系统可靠性上较传统MQ具有很大的优势,从设计上处处能看出Kafka在这方面的野心,这也是如今在大数据消息流处理中,Kafka如此火热的主要原因。

但是,不得不说,Kafka也有很多不尽人意的地方。比如:

1. Kafka并不保证每条消息的精确处理(不丢失且不重复消费)。Kafka消息丢失主要在两种情况下存在可能性:a) Producer端重试次数用完后放弃该批次消息(也可选择抛出异常),b) Broker端Partition的leader副本崩溃,其他follower副本与leader的数据不是完全一致的。Kafka消息产生重复消息也主要在在两种情况下会出现:a) Producer端发送完批次消息,消息写入成功,但响应超时,造成该批次消息被重发,Broker端不会对相同消息进行合并;b) Consumer端对消息偏移量的维护与实际消息消费进度不一致,这也是实际应用中最常见的。

2. 0.11(这其实是一个较新的版本,只是Kafka最近的版本号迭代跨度比较大)之前的版本不支持事务消息。

3. 只能保证消息的分区有序性(如果在Producer端buffer中的批次是异步发送,在遇到超时和重试的时候,也会乱序),如果需要保证特定类型消息的有序性,需要开发自定义的分区器,将特定类型消息分布到同一个分区(Partition)中。

4. 在 2.0 以前,Kafka 自身的访问控制机制还是粗粒度的。比如对“创建Topic”这一权限的控制,只有“全集群”这一种范围。也就是说,对于任何一个用户来说,我们只能给或者不给这种权限。而且Kafka对消息访问的权限控制也不够好,在数据安全性方面有待提升。

罗天然

大数据工程师

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180920G1BFGN00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券