Kafka作为分布式消息系统的系统解析
Kafka概述
Apache Kafka由Scala和Java编写,基于生产者和消费者模型作为开源的分布式发布订阅消息系统。它提供了类似于JMS的特性,但设计上又有很大区别,它不是JMS规范的实现,如Kafka允许多个消费者主动拉取数据,而在JMS中只有点对点模式消费者才会主动拉取数据。
Kafka对消息保存时根据topic进行归类,发送消息者称为producer,消息接收者称为consumer。Kafka集群由多个Kafka实例组成,每个实例称为broker。并且Kafka集群基于zookeeper保存一些meta信息,来保证系统的高可用性。
生产者可以直接把数据传递给broker,broker通过zookeeper进行leader和follower的选举管理;消费者可以通过zookeeper保存读取的位置offset以及读取的topic的分区信息。这样做有以下几个好处:
1. 生产者和消费者的负载解耦
2. 消费者可以按照自己的“能力”拉取数据
3. 消费者可以自定义消费数量
Kafka与传统消息系统相比,有以下不同:
1. Kafka是分布式的,易于水平扩展
2. 同时为发布和订阅提供高吞吐量
3. 支持多订阅者,当失败时能自动对消费者进行rebalance
4. 将消息持久化到磁盘,因此可用于批量消费,例如ETL以及实时应用程序
Kafka中的重要概念
名称 |
解释 |
broker |
Kafka集群中的实例进程,负责数据存储。在Kafka集群中每个broker都有一个唯一的brokerId。通过broker来接受producer和consumer的请求,并把消息持久化到磁盘。每个Kafka集群中会选举出一个broker来担任Controller,负责处理分区的leader选举,协调分区迁移等工作 |
topic |
Kafka根据topic对消息进行归类(逻辑划分),发布到Kafka集群的每条消息都需要指定一个topic。落到磁盘上对应的是partition目录,partition目录中有多个segement组合(后缀为index、log的文件)。一个topic对应一个或多个partition,一个partition对应多个segment组合 |
producer |
向broker发送消息的生产者。负责数据生产和数据分发。生产者代码可以集成到任务系统中。 数据分发策略默认为defaultPartition Utils.abs(key.hashCode)%numPartitions |
consumer |
从broker读取消息的消费者【实际上consumer是通过与zookeeper通信获取broker地址进行消息消费】 |
ConsumerGroup |
数据消费者组,ConsumerGroup(以下简称CG)可以有多个。可以把多个consumer线程划分为一个组,组里面所有成员共同消费一个topic的数据(一个topic的多个partition),组员之间不能重复消费 |
partition |
分区,一个topic可以分为多个partition(分布在多个broker上,实现扩展性),每个partition可以设置多个副本,会从多个副本中选取出一个leader负责读写操作。每个partition是一个有序的队列(partition中的每条消息都会被分配一个有序的id(offset)),Kafka只保证按每个partition内部有序并且被顺序消费,不保证一个topic的整体(多个partition间)的顺序 |
offset |
每条消息在文件中的偏移量。Kafka的存储文件都是按照offset.index来命名,方便查找 |
zookeeper |
保存meta信息,管理集群配置,以及在CG发生变化时进行rebalance |
Replication |
Kafka支持以partition为单位对消息进行冗余备份,每个partition都可以配置至少1个Replication(副本数包括本身) |
leader partition |
每个Replication集合中的partition都会选出一个唯一的leader,所有的读写请求都由leader处理。其他Replicas从leader处把数据更新同步到本地,过程类似MySQL中的Binlog同步。 |
ISR(In-Sync Replica) |
Replicas的一个子集,表示目前"活着的"且与leader能够保持"联系"的Replicas集合。由于读写都是首先落到leader上,所以一般来说通过同步机制从leader上拉取数据的Replica都会和leader有一些延迟(包括了延迟时间和延迟条数两个维度),任意一个超过阈值都会把该Replica踢出ISR。每个partition都有它自己独立的ISR。 |
Kafka中的广播和单播
每个consumer属于一个特定的CG,一条消息可以发送到多个不同的CG,但是一个CG中只能有一个consumer能够消费该消息。目的:实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)。一个topic可以有多个CG。topic的消息会复制(逻辑概念)到所有的CG,但每个partition只会把消息发给该CG中的一个consumer。
【实现广播】每个consumer有一个独立的CG
【实现单播】所有的consumer在同一个CG
用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。
Kafka消息的分发
1. producer客户端负责消息的分发
1)Kafka集群中的任何一个broker都可以向producer提供metadata信息,这些metadata中包含集群中存活的servers列表、partitions leader列表等信息
2)当producer获取到metadata信息之后,producer将会和topic下所有partition leader保持socket连接
3)消息由producer直接通过socket发送到broker,中间不会经过任何"路由层",消息被路由到哪个partition上由producer通过一些策略如随机、轮巡等决定
如果一个topic中有多个partition,那么在producer端实现"消息均衡分发"是非常必要的。在producer端的配置文件中,开发者可以指定partition路由的方式,具体流程:
1)连接broker-list中任意一台broker服务器
2)发送数据时,需要知道topic对应的partition个数及leader partition所在节点。这些信息由broker提供,每一个broker都能提供一份元数据信息(如哪些broker是存活的,哪个topic有多少分区,哪个分区是leader)
3)数据生产,数据发送到哪个partition的leader由producer代码决定
4)数据通过socket连接,直接发送到partition所在的broker
2. producer消息发送的应答机制
设置发送数据是否需要服务端的反馈,由参数request.required.acks的值决定:
0: producer不会等待broker发送ack。最低延迟,持久化保证弱,当server挂掉时会丢失数据
1: 当leader接收到消息之后发送ack。当前leader接收到数据后,producer会得到一个ack,更好的持久性,因为在server确认请求成功后,client才会返回。如果数据刚写到leader还没来得及复制leader就挂了,消息可能会丢失
-1: 当所有的follower都同步消息成功后发送ack。最好的持久性,只要有一个replica存活,数据就不会丢失。但相对延迟高
3. 分发策略
默认分发策略:def partition(key: T, numPartitions: Int): Int = {
Utils.abs(key.hashCode) % numPartitions}。
其他策略:轮询、随机等。
consumer与topic关系
通常情况下,一个消费者组有多个consumer,并且一个consumer只会属于一个消费者组。这样不仅可以提高topic中消息的并发消费能力,还能提高"故障容错"。如消费组中的某个consumer挂掉,那么它消费的partition将会由同组内其他的consumer自动接管。
一个CG中所有的consumer将会交错的消费整个topic,每个消费组中consumer消息消费互相独立,可以认为一个消费者组就是一个"订阅"者。
注意:对于topic中的一条特定的消息,只会被订阅此topic的每个消费者组中的其中一个consumer消费。同时,Kafka的设计原理决定,对于一个topic,同一个消费者组中如果有多于partition个数的consumer,则意味着某些consumer将无法消费消息。
consumer负载均衡
最好是一个partition对应一个consumer。如果consumer数量过多,必然有空闲的consumer。
当一个消费者组中,有consumer加入或者离开时,会触发partition消费的rebalance。均衡的最终目的为了提升topic的并发消费能力,步骤如下:
比如一个topic有4个分区:P0、P1、P2、P3,一个CG中有C1、C2两个consumer。
首先根据partition索引号对partition排序:P0、P1、P2、P3,再根据consumer的id排序:C0、C1
计算倍数: M = [P0,P1,P2,P3].size / [C0,C1].size,本例值M=2(向上取整)
然后依次分配partition: C0 = [P0,P1],C1=[P2,P3],即Ci = [P(i * M),P((i + 1) * M -1)]
Kafka文件存储机制
1. 文件存储基本结构
在Kafka文件存储中,同一个topic下有一个或多个不同partition,每个partition为一个目录,partition命名规则为topic名称+有序序号,第一个partition序号从0开始,序号最大值为partition数量减1。
每个partition相当于一个巨型文件被平均分配到多个大小相等segment段数据文件中。但每个段segment file消息数量不一定相等,这种特性方便老的segment file快速被删除即方便已被消费的消息的清理,提高磁盘利用率。
segment文件生命周期由服务端配置参数(log.segment.bytes:当segment文件达到多大时滚动生成一个新的segment文件,log.roll.{ms,hours}:滚动生成新的segment的时间即使没有达到设置的segment文件最大值等若干参数)决定。
segment的意义:当Kafka producer不断发送消息,必然会引起partition文件的无限扩张,这样对于消息文件的维护以及已经被消费的消息的清理带来严重影响。通过参数设置segment可以指定保留多长时间的数据,及时清理已经被消费的消息,提高磁盘利用率,目前默认保存7天数据。
2. partition segment
1)segment file组成
由2大部分组成,分别为index file和data file,两个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件。数值大小为64位,20位数字字符长度,没有数字用0填充,如下:
2)segment 文件命名规则
partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充
3)索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址
4)segment data file由许多message组成,物理结构如下:
关键字 |
解释说明 |
8 byte offset |
在partition内每条消息都有一个有序的id号:offset,它可以唯一确定每条消息在partition内的位置。即offset表示partition的第多少个message |
4 byte message size |
message大小 |
4 byte CRC32 |
用crc32校验message |
1 byte "magic" |
表示本次发布Kafka服务程序协议版本号 |
1 byte "attributes" |
表示为独立版本、或标识压缩类型、或编码类型 |
4 byte key length |
表示key的长度,当key为-1时,K byte key字段不填 |
value bytes payload |
表示实际消息数据 |
Kafka查找message
已知offset查找相应的message,需要通过下面2个步骤查找:
1. 查找segment file
00000000000000000000.index表示最开始的文件,起始偏移量为0
00000000000000000099.index的消息量起始偏移量为100=99+1
00000000000000000999.index的起始偏移量为1000=999+1
其他后续文件依次类推。以起始偏移量命名并排序这些文件,只要根据offset按照"二分查找"文件列表,就可以快速定位到具体文件。
2. 通过segment file查找message
根据offset,依次定位index元数据物理位置和log的物理偏移位置,然后再在log中顺序查找直至找到对应offset位置即可。
关注微信公众号:大数据学习与分享,获取更对技术干货
Kafka作为分布式消息系统的系统解析的更多相关文章
- 深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题.实现高性能,高可用,可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件. 本场 Chat 主要内容: Kafk ...
- Kafka 和 ZooKeeper 的分布式消息队列分析
1. Kafka 总体架构 基于 Kafka-ZooKeeper 的分布式消息队列系统总体架构如下: 如上图所示,一个典型的 Kafka 体系架构包括若干 Producer(消息生产者),若干 bro ...
- 分布式消息服务DMS与开源Kafka对比
分布式消息服务(简称DMS)是一项基于高可用分布式集群技术的消息中间件服务,提供了可靠且可扩展的托管消息队列,用于收发消息和存储消息.那么,比起自建开源的Kafka,分布式消息服务DMS有哪些好处呢? ...
- RabbitMQ,RocketMQ,Kafka 几种消息队列的对比
常用的几款消息队列的对比 前言 RabbitMQ 优点 缺点 RocketMQ 优点 缺点 Kafka 优点 缺点 如何选择合适的消息队列 参考 常用的几款消息队列的对比 前言 消息队列的作用: 1. ...
- Apache Kafka:下一代分布式消息系统
[http://www.infoq.com/cn/articles/apache-kafka/]分布式发布-订阅消息系统. Kafka是一种快速.可扩展的.设计内在就是分布式的,分区的和可复制的提交日 ...
- 【转载】Apache Kafka:下一代分布式消息系统
http://www.infoq.com/cn/articles/kafka-analysis-part-1 Kafka是由LinkedIn开发的一个分布式的消息系统,使用Scala编写,它以可水平扩 ...
- 转 Apache Kafka:下一代分布式消息系统
简介 Apache Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apache项目的一部分.Kafka是一种快速.可扩展的.设计内在就是分布式的,分区的和可复制的提交 ...
- [kfaka] Apache Kafka:下一代分布式消息系统
简介 Apache Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apache项目的一部分.Kafka是一种快速.可扩展的.设计内在就是分布式的,分区的和可复制的提交 ...
- Apache Kafka:下一代分布式消息系统【转载】
简介 Apache Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apache项目的一部分.Kafka是一种快速.可扩展的.设计内在就是分布式的,分区的和可复制的提交 ...
随机推荐
- python中的递归
python中的递归 关注公众号"轻松学编程"了解更多. 文章更改后地址:传送门 间接或直接调用自身的函数被称为递归函数. 间接: def func(): otherfunc() ...
- P4683 [IOI2008] Type Printer 打印机
题意描述 [IOI2008] Type Printer 打印机 几百年前的 IOI 的题目还是很好的呀. 给你一个 诡异的 打印机,它只能用已有的字符来打印,而且必须每一个都用到.(这岂不是活字印刷术 ...
- 力扣 - 19. 删除链表的倒数第N个节点
目录 题目 思路1 代码实现 思路2 代码实现 题目 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: 1->2->3->4->5, ...
- vim-配置教程+源码
目录 概念 前言 参考链接 vim 优点 vim 配置 vim 配置方法一 vim 配置方法二 自动添加文件头 一般设置 插件 ** 映射 YouCompleteMe 插件 其它配置 概念 前言 放弃 ...
- 解决windows下Chrome78以上跨域失效问题
1. 为什么需要解决chrome浏览器跨域的问题? 基于Hybird App的H5部分,可以直接打包进apk或者ipa包中,在开发过程中也不需要放置到临时搭建的服务器上,直接在本地打开html静态页面 ...
- 解决无法访问 Github
可以正常使用Google,但无法打开Github. 查阅了一些资料,发现需要在hosts文件中添加映射. 在hosts文件中加入两行 140.82.113.4 github.com 140.82.11 ...
- Find Any File for Mac(文件搜索软件)v2.1.2b6
Find Any File for Mac是应用在Mac上的一款文件搜索工具,Find Any File Mac可以通过名称.创建或修改日期,大小或类型和创建者代码(而不是内容)在本地磁盘上搜索文件. ...
- Spider_基础总结2_Requests异常
# 1: BeautifulSoup的基本使用: import requests from bs4 import BeautifulSoup html=requests.get('https://ww ...
- 你知道MySQL的LRU链表吗?
相信大家对LRU链表是不陌生的,算是一种基础的数据结构! LRU:Least Recently Used 一.简述传统的LRU链表 LRU:Least Recently Used 相信大家对LRU链表 ...
- [MIT6.006] 12. Square Roots, Newton's Method 平方根,牛顿法
首先让我们回顾下上节课讲的,用牛顿法计算√2的内容: 简单来说,牛顿法从x0=1不断向后计算逼近√2的值,而刚开始计算的精度是1,随着牛顿法的逼近(共log2d个循环),就能使得√2逼近值的精度达到d ...