文章目录


一、前言

本文主要介绍kafka架构与应用场景。

解开Kafka神秘的面纱(一):kafka架构与应用场景_分布式

二、Kafka简介

2.1 Kafka简介

Kafka 是 linkedin 使用 Scala 编写具有高水平扩展和高吞吐量的分布式消息系统。

Kafka 对消息保存时根据 Topic 进行归类,发送消息者成为 Producer ,消息接受者成为 Consumer ,此外 kafka 集群有多个 kafka 实例组成,每个实例(server)称为 broker。

无论是 Kafka集群,还是 producer 和 consumer 都依赖于 zookeeper 来保证系统可用性,为集群保存一些 meta 信息。

Apache Kafka® 是 一个分布式流处理平台,流处理平台的特性包括:

(1) 可以让你发布和订阅流式的记录。这一方面与消息队列或者企业消息系统类似。

(2) 可以储存流式的记录,并且有较好的容错性。

(3) 可以在流式记录产生时就进行处理。

Kafka 适合什么样的场景?

(1) 构造实时流数据管道,它可以在系统或应用之间可靠地获取数据。 (相当于消息队列)

(2) 构建实时流式应用程序,对这些流数据进行转换或者影响。

Kafka主要特性

(1) kafka作为一个集群运行在一个或多个服务器上

(2) Kafka 通过 topic 对存储的流数据进行分类

(3) 每条记录中包含一个 key ,一个 value 和一个 timestamp(时间戳)

2.2 基于分布式的Kafka

从普通消息队列到分布式消息队列kafka,最大的不同就是支持动态扩容。

解开Kafka神秘的面纱(一):kafka架构与应用场景_java_02

取rabbitmq和kafka做对比,则相同点和不同点如下:

相同点:kafka和rabbitmq都是存储队列

不同点:

(1) 消费单元不同

在rabbitmq中,对于每个消息,一个消费者只能消费这个消息一次,但是这个消息可以同时被发送给多个消费者消费;在kafka中,一个消费者组只能消费一个消息一次,但是这个消息可以同时被发送给多个消费者组消费

(2) 消息消费不同

rabbitmq中,被成功消费ack的消息就自动被删除了,没有了(默认自动ack),kafka中,消息被成功消费后,不会被删除,只是移动消费者的offset(消费者的offset保存在zk上),kafka默认保存7天(消息中有一个timestamp属性,可以用这个属性来控制消息保存时间)。

(3) 消息组成不同

rabbitmq中消息 = routingkey + headers(properties) + payload

kafka中消息 = 配置文件中log位置 (三个文件) = index(key) + value + timestamp

index/key用来排序(顺序消费)或索引,不指定默认index/key为空,value就是消息体,timestamp用来物理删除消息(kafka默认消息存储7天,因为消费的时候只是移动offset,没有删除消息)

(4) 组成不同

rabbitmq由exchange交换机和queue队列构成,exchage是一个抽象概念,不存储消息,队列是真实概念,是真正存储消息的组件

kafka由topic和partition组成,topic类似exchange,不存储数据,但是消费者要消费数据是指定topic的,然后topic从相应的partition取出数据给消费者,partition类似rabbitmq中的queue,是真正存储消息的组件。

问题:为什么kafka是起码要一个消费者组,而rabbitmq最小单元是一个消费者?

回答:因为kafka是天然分布式的,n broker 就有 n 个partition。

问题:kafka如何实现顺序消费?

回答:同一个partition,所有消息的key都为空或者所有消息的key相同。

eg1: 对于kafka,消费者消费消息可以从start开始,也可以从end开始,但是都只能从old到new。

eg2: 对于kafka,如果不指定topic数量,默认三个。

三、Kafka架构

3.1 消息生产与消费

3.1.1 消息生产与消费模型

kafka和其他消息队列一样,消息生产与消费模型非常简单,如下:

解开Kafka神秘的面纱(一):kafka架构与应用场景_kafka_03

服务器端(broker):用来接收生产者发送的消息并将这些消息路由给服务器中的队列

消费者(Consumer):从消息队列中请求消息的客户端应用程序

生产者(Producer):向 broker 发布消息的客户端应用程序

3.1.2 Kafka消费单元是消费者组

在kafka中,最重要的一个组件是是topic,但是这个topic不存储数据,真正存储数据的是partition,topic类似rabbitmq的exchanges。topic既然不存储数据,那么它的作用是什么,topic唯一的作用就是数据主题,是数据记录发布的地方,可以用来区分业务系统(名为test用来测试,名为pro用来生产)。Kafka 中的 Topics 总是多订阅者模式,即一个 topic 可以拥有一个或者多个消费者来订阅它的数据。就是消费者组中每个消费者是找topic要消息,而不是直接partition,topic的作用就是将partition中的消息拿来给消费者。

对于kafka来说,producer生产数据存放到kafka是比较简单的,主要是看消息消费,因为kafka是天然分布式的,这里涉及到一个消费者组的概念。

比方说,生产了 message1 message2 message3,分别存储到partition1 partition2 partition3,partition相当于队列,有三个消费者,如何消费?这里涉及到一个消费者组的概率,消费者组诞生的意义在于一个消费者组消费所有partition(这里是三个partition)上存储的所有消息,一个消息只能被一个消费组消费一次,至于拿个消费者能消费到哪些消息,就看消费者组怎么分配。

如果consumer1自己属于一个消费者组,consumer2和consumer3属于另一个消费者组,则consumer1可以消费到message1 message2 message3这三个消息,consumer2和consumer3共同消费message1 message2 message3,一个消息只能被一个消费组消费一次,具体这个消息被消费者组中的哪个消费者消费,就不是程序员可以决定的了。

如果consumer1属于一个消费者组,consumer2属于另一个消费者组,consumer3属于第三个消费者组,则每个消费者都可以消费三个消息。

还可以交叉覆盖,如果consumer1 consumer2属于一个消费者组,consumer2 consumer3属于另一个消费者组,则consumer1和consumer2可以消费三个消息,consumer2和consumer3又可以消费这三个消息。

这就是Kafka消费者组的概念,王道在于一个消费者组消费所有partition上存储的所有消息,一个消息只能被一个消费组消费一次。消费者与partion的关系:

情况1:正常情况,一个消费者组中三个消费者对应三个partition,那么每个消费者消费一个partition;

情况2:如果消费者组中的某个消费者挂了,则一个消费者组中,两个消费者对应三个partition,那么其中一个消费者可能就要消费两个partition了;

情况3:如果只有三个partition,而消费者组有4个消费者,则一个消费者组中,四个消费者对应三个partition,那么一个消费者会空闲;

情况4:如果多加入一个消费者组,则两个消费者组对应三个partition,无论是新增的消费者组还是原本的消费者组,都能消费topic的全部数据(理由:消费者组之间从逻辑上它们是独立的)。

消费者组本质:消费者组是一个多线程的概念,就是多个线程来消费所有消息(各个partition分区消息总和),消费者实例可以分布在多个进程中或者多个机器上。

(1)消费者组这个概念是必须的,只有一个消费者那么消费者组就只有一个消费者,有N个消费者那么消费者组就只有N个消费者;

(2)消费者组必须消费所有partition主分区的消息:组中消费者不够,一个消费者就要消费多个partition,因为消费者组有义务将Partition上的message消费完;如果组中消费者多了,就有人可以不用干事了;

(3)同一个消费组中,一个消息message只被消费一次。

消费者使用一个消费组名称来进行标识,发布到 topic 中的每条记录被分配给订阅消费组中的一个消费者实例。消费者实例可以分布在多个进程中或者多个机器上。如果所有的消费者实例在同一消费组中,消息记录会负载平衡到每一个消费者实例。如果所有的消费者实例在不同的消费组中,每条消息记录会广播到所有的消费者进程。如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_kafka_04

3.1.3 Kafka只消费Partition主分区的消息

Kafka的消息时存储在partition上,但是每个partition有个备份,包括一个主partition和n个从partition,但是生产者在写数据到kafka的时候,主partition和从partition的存在消息不一致的情况,如下图

解开Kafka神秘的面纱(一):kafka架构与应用场景_java_05

上图表示的是 0 1 2 3 4 5 6 7 8 9 10 11 12 ,这个图想要告诉读者的是,消息存放在三个partition的时候,数据不是一致的,那么消费者如果从不同的partition拿数据,会不会拿到遗漏或者重复的数据,答案是不会,因为当消费者是找topic要消息,而不是直接找partion要消息,topic每次从主分区找数据给消费者,即消费者只消费主分区的消息,从分区只是同步主分区的消息。


kafka这个分区数据存在不一致的情况和rabbitmq的cluster的普通模式相同,数据存在不一致,rabbitmq镜像模式数据是一致的。


小结:消费者组只消费主分区的,因为kafka中,生产者消费者读写的是主分区,从分区不做生产消费,而且,kakfa对于机械硬盘使用顺序读写,就不需要寻址时间,速度接近随机读写。从分区只要按固定的策略同步主分区的数据就好了,待主分区宕机,在zookeeper的协调下选出新的partition,供生产和消费。

3.1.4 消费者组中的每个消费者的offset

关于offset

(1)每个消费者都一个offset,相互独立不影响

(2)每个消费者的offset存储在zookeeper上面去,这样即使consumer宕机下一次也可以接着消费,因为上一次的消费位置offset,zookeeper帮他记录着,consumer重新启动后,按照自己的offset接着消费就好了

即使是同一个消费者组,consumer1和consumer2也各有一个offset,每个消费者接入进来,都需要从zookeeper取下offset,如果消费者第一次进来,zookeeper建立从0开始,如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_数据_06

一般来说,n broker 就有 n partition,除非所有broker都宕机,否则不会丢数据,而且还是磁盘持久化保证。


eg: offset只对消费者有用,对生产者没用


3.1.5 小结

问题:N个消费者如何消费M个Partition如何消费?

回答:消费者组的引入解决如何消费的问题,核心是一个消费者组必须完全消费所有partion消息,一个消息也只能被一个消费者组中某个消费者消费,在同一个消费者组中,一个消息永远不会消费两次,不会重复消费。

情况1:正常情况,一个消费者组中三个消费者对应三个partition,那么每个消费者消费一个partition;

情况2:如果消费者组中的某个消费者挂了,则一个消费者组中,两个消费者对应三个partition,那么其中一个消费者可能就要消费两个partition了;

情况3:如果只有三个partition,而消费者组有4个消费者,则一个消费者组中,四个消费者对应三个partition,那么一个消费者会空闲;

情况4:如果多加入一个消费者组,则两个消费者组对应三个partition,无论是新增的消费者组还是原本的消费者组,都能消费topic的全部数据(理由:消费者组之间从逻辑上它们是独立的)。

问题:被消费的消息删除了吗?

回答:被消费的消息只是移动offset,不删除,默认存储7天(用消息体中的timestamp可以知道消息存了多久)。offset存储在zookeeper,因为zookeeper可以保证数据一致性,offset不可能存某个服务器的磁盘。

问题:kafka读写消息、删除消息需要扫描整个磁盘吗?

回答:不需要,每个都一个timestamp字段,kafka采用顺序读写可以保证删除消息很方便。

问题:partition有多个,生产者写数据到kafka,存在数据不一致情况,如何保证消息消费不重不漏?

回答:只消费主分区保证消费消息不重不漏,partion有主从之分,只消费主分区,从分区只同步作用,从而保证消费消息不重不漏。

问题:kafka消费顺序有哪些?

回答:kakfa可以从当前head消费,也可以从当前tail消费,但是只能从old到new消费,即old->new消费,不能反过来。

3.2 Partition备份与选主

每个partition还会被复制到其它服务器作为replication,这是一种冗余备份策略,如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_java_07

Partition备份四个特性

(1) 同一个partition的多个replication不允许在同一broker上

(2) 每个partition的replication中,有一个leader ,零或多个follower

(3) leader处理此分区的所有的读写请求, follower仅仅被动的复制数据

(4) leader宕机后,会从follower中选举出新的leader

Partition分区被分布到集群中的多个服务器上,每个服务器处理它分到的分区, 根据配置每个分区还可以复制到其它服务器作为备份容错。 每个分区有一个 leader,零或多个 follower。Leader 处理此分区的所有的读写请求,而 follower 被动的复制数据。

如果 leader 宕机,其它的一个 follower 会被推举为新的 leader。选主需要依赖的是zookeeper中间件来完成,zk选主的时候,哪个从分区的数据与宕机的主分区的数据最相近,同步时间与主分区最接近的,被选为主分区。一台服务器可能同时是一个分区的 leader,另一个分区的 follower。 这样可以平衡负载,避免所有的请求都只让一台或者某几台服务器处理。

3.3 高级特性

3.3.1 四个核心API

Producer API

允许一个应用程序发布一串流式的数据到一个或者多个 Kafka topic。

Consumer API

允许一个应用程序订阅一个或多个 topic ,并且对发布给他们的流式数据进行处理。

Streams API

允许一个应用程序作为一个流处理器,消费一个或者多个 topic 产生的输入流,然后生产一个输出流到一个或多个 topic 中去,在输入输出流中进行有效的转换。

Connector API

允许构建并运行可重用的生产者或者消费者,将Kafka topics连接到已存在的应用程序或者数据系统。比如,连接到一个关系型数据库,捕捉表(table)的所有变更内容。

Kafka四个核心API关系如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_应用场景_08

上图告诉我们,在Kafka中,客户端和服务器之间的通信是通过简单,高性能,语言无关的TCP协议完成的,即kafka可以使用java scala混编,通过 tcp/ip 网络通信实现语言无关性,此协议已版本化并保持与旧版本的向后兼容性。Kafka提供多种语言客户端。


上图中的 Connector 表示kafka和其他连接,例如 mysql redis 等。


3.3.2 攒一波再发

Producer向kafka发送消息的时候,Producer会为每个partition维护一个缓冲,用来记录还没有发送的数据,每个缓冲区大小用 batch.size指定,默认值为16k,就是数据一定要等到满16K才发送,就是攒一波再发,减少网络性能消耗,如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_java_09


eg: 还有一个参数 linger.ms,这个参数表示buffer中的数据在达到batch.size前,需要等待的时间。


3.3.3 普通消费模式和高级消费模式

普通消费模式:Kafka Simple Consumer

Simple Cnsumer 位于kafka.javaapi.consumer包中,不提供负载均衡、容错的特性每次获取数据都要指定topic、partition、offset、fetchSize。

高级消费模式:High-level Consumer

该客户端透明地处理kafka broker异常,透明地切换consumer的partition,通过和broker交互来实现consumer group级别的负载均衡,如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_应用场景_10


辅助理解: 普通消费模式类似汇编语言 C语言,啥都没有,需要程序员自己指定;高级消费模式类似Java语言 Python语言,很多都配好了,程序员直接用就好了。
一般都是都是使用高级消费模式。


3.4 Kafka整体架构

对于kafka整体架构,只要知道topic和partition的这个两个最关键的组件,可以了,如下图:

解开Kafka神秘的面纱(一):kafka架构与应用场景_kafka_11

eg: 上图中zookeeper,用来保存每个消费者的offset。

四、Kafka应用场景

4.1 Kafka应用场景:消息

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

根据官方的经验,通常消息传递使用较低的吞吐量,但可能要求较低的端到端延迟,kafka 提供强大的持久性来满足这一要求。在这方面,Kafka 可以与传统的消息传递系统(ActiveMQ 和 RabbitMQ)相媲美。

4.2 Kafka应用场景:跟踪网站活动

kafka 的最初始作用就是是将用户活动跟踪管道重建为一组实时发布-订阅源。 把网站活动(浏览网页、搜索或其他的用户操作)发布到中心 topic,其中每个活动类型有一个 topic。 这些订阅源提供一系列用例,包括实时处理、实时监视、对加载到Hadoop或离线数据仓库系统的数据进行离线处理和报告等。

每个用户浏览网页时都生成了许多活动信息,因此活动跟踪的数据量通常非常大。这就非常使用使用 kafka。

4.3 Kafka应用场景:日志聚合

许多人使用 kafka来替代日志聚合解决方案。日志聚合系统通常从服务器收集物理日志文件,并将其置于一个中心系统(可能是文件服务器或HDFS)进行处理。

kafka 从这些日志文件中提取信息,并将其抽象为一个更加清晰的消息流。 这样可以实现更低的延迟处理且易于支持多个数据源及分布式数据的消耗。

与 Scribe 或 Flume 等以日志为中心的系统相比,Kafka具备同样出色的性能、更强的耐用性(因为复制功能)和更低的端到端延迟。

4.4 Kafka应用场景:流处理

从0.10.0.0开始,kafka 支持轻量,但功能强大的流处理。

kafka消息处理包含多个阶段。其中原始输入数据是从kafka主题消费的,然后汇总,丰富,或者以其他的方式处理转化为新主题以供进一步消费或后续处理。

例如,一个推荐新闻文章,文章内容可能从“articles”主题获取;然后进一步处理内容,得到一个处理后的新内容,最后推荐给用户。这种处理是基于单个主题的实时数据流。

除了Kafka Streams,还有 Apache Storm 和 Apache Samza 也是不错的流处理框架。

4.5 Kafka应用场景:事件采集

Event sourcing是一种应用程序设计风格,按时间来记录状态的更改。 Kafka 可以存储非常多的日志数据,为基于 event sourcing 的应用程序提供强有力的支持。

4.6 Kafka应用场景:提交日志

kafka 可以从外部为分布式系统提供日志提交功能。 日志有助于记录节点和行为间的数据,采用重新同步机制可以从失败节点恢复数据。 Kafka的日志压缩 功能支持这一用法。 这一点与Apache BookKeeper 项目类似。

五、尾声

本文主要介绍了kafka架构与应用场景。

天天打码,天天进步!!