浅谈分布式消息系统 Kafka 设计原理
作者:IT科技类资讯 来源:IT科技类资讯 浏览: 【大中小】 发布时间:2025-11-05 14:05:20 评论数:
一、浅谈Kafka简介
Kafka是分布一种高吞吐量、分布式、式消设计基于发布/订阅的息系消息系统,最初由LinkedIn公司开发,原理使用Scala语言编写,浅谈目前是分布Apache的开源项目。
跟RabbitMQ、式消设计RocketMQ等目前流行的息系开源消息中间件相比,Kakfa具有高吞吐、原理低延迟等特点,浅谈在大数据、分布日志收集等应用场景下被广泛使用。式消设计
本文主要简单介绍Kafka的息系设计原理。
二、原理Kafka架构

基本概念:
broker:Kafka服务器,负责消息存储和转发 topic:消息类别,Kafka按照topic来分类消息 partition:topic的分区,一个topic可以包含多个partition,topic消息保存在各个partition上 offset:消息在日志中的位置,可以理解是消息在partition上的偏移量,也是代表该消息的唯一序号 Producer:消息生产者 Consumer:消息消费者 Consumer Group:消费者分组,企商汇每个Consumer必须属于一个group Zookeeper:保存着集群broker、topic、partition等meta数据;另外,还负责broker故障发现,partition leader选举,负载均衡等功能三、Kafka设计原理
3.1 数据存储设计
partition以文件形式存储在文件系统,目录命名规则:<topic_name>-<partition_id>,例如,名为test的topic,其有3个partition,则Kafka数据目录中有3个目录:test-0, test-1, test-2,分别存储相应partition的数据。
partition的数据文件
partition中的每条Message包含了以下三个属性:
offset MessageSize data其中offset表示Message在这个partition中的偏移量,offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了partition中的一条Message,可以认为offset是partition中Message的id;MessageSize表示消息内容data的大小;data为Message的具体内容。
partition的数据文件由以上格式的云南idc服务商Message组成,按offset由小到大排列在一起。
如果一个partition只有一个数据文件:
新数据是添加在文件末尾,不论文件数据文件有多大,这个操作永远都是O(1)的。 查找某个offset的Message是顺序查找的。因此,如果数据文件很大的话,查找的效率就低。Kafka通过分段和索引来提高查找效率。
数据文件分段segment
partition物理上由多个segment文件组成,每个segment大小相等,顺序读写。每个segment数据文件以该段中最小的offset命名,文件扩展名为.log。这样在查找指定offset的Message的时候,用二分查找就可以定位到该Message在哪个segment数据文件中。
数据文件索引
数据文件分段使得可以在一个较小的数据文件中查找对应offset的Message了,但是这依然需要顺序扫描才能找到对应offset的免费信息发布网Message。为了进一步提高查找的效率,Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index。

索引文件中包含若干个索引条目,每个条目表示数据文件中一条Message的索引。索引包含两个部分,分别为相对offset和position。
相对offset:因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条Message相对于其所属数据文件中最小的offset的大小。举例,分段后的一个数据文件的offset是从20开始,那么offset为25的Message在index文件中的相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。 position,表示该条Message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的Message了。index文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。

总结
查找某个offset的消息,先二分法找出消息所在的segment文件(因为每个segment的命名都是以该文件中消息offset最小的值命名);然后,加载对应的.index索引文件到内存,同样二分法找出小于等于给定offset的***的那个offset记录(相对offset,position);***,根据position到.log文件中,顺序查找出offset等于给定offset值的消息。
由于消息在partition的segment数据文件中是顺序读写的,且消息消费后不会删除(删除策略是针对过期的segment文件),这种顺序磁盘IO存储设计是Kafka高性能很重要的原因。
3.2 生产者设计

3.3 消费者设计

队列模式
队列模式,指每条消息只会有一个Consumer消费到。Kafka保证同一Consumer Group中只有一个Consumer会消费某条消息。
在Consumer Group稳定状态下,每一个Consumer实例只会消费某一个或多个特定partition的数据,而某个partition的数据只会被某一个特定的Consumer实例所消费,也就是说Kafka对消息的分配是以partition为单位分配的,而非以每一条消息作为分配单元; 同一Consumer Group中,如果Consumer实例数量少于partition数量,则至少有一个Consumer会消费多个partition的数据;如果Consumer的数量与partition数量相同,则正好一个Consumer消费一个partition的数据;而如果Consumer的数量多于partition的数量时,会有部分Consumer无法消费该Topic下任何一条消息; 设计的优势是:每个Consumer不用都跟大量的broker通信,减少通信开销,同时也降低了分配难度,实现也更简单;可以保证每个partition里的数据可以被Consumer有序消费。 设计的劣势是:无法保证同一个Consumer Group里的Consumer均匀消费数据,且在Consumer实例多于partition个数时导致有些Consumer会饿死。如果有partition或者Consumer的增减,为了保证均衡消费,需要实现Consumer Rebalance,分配算法如下:

broker对Consumer设计原理:
对于每个Consumer Group,选举出一个Broker作为Coordinator(0.9版本以上),由它Watch Zookeeper,从而监控判断是否有partition或者Consumer的增减,然后生成Rebalance命令,按照以上算法重新分配。 当Consumer Group***次被初始化时,Consumer通常会读取每个partition的最早或最近的offset(Zookeeper记录),然后顺序地读取每个partition log的消息,在Consumer读取过程中,它会提交已经成功处理的消息的offsets(由Zookeeper记录)。 当一个partition被重新分配给Consumer Group中的其他Consumer,新的Consumer消费的初始位置会设置为(原来Consumer)最近提交的offset。
如图,Last Commited Offset指Consumer最近一次提交的消费记录offset,Current Position是当前消费的位置,High Watermark是成功拷贝到log的所有副本节点(partition的所有ISR节点,下文介绍)的最近消息的offset,Log End Offset是写入log中***一条消息的offset+1。
从Consumer的角度来看,最多只能读取到High watermark的位置,后面的消息对消费者不可见,因为未完全复制的数据还没可靠存储,有丢失可能。
发布订阅模式
发布订阅模式,又指广播模式,Kafka保证topic的每条消息会被所有Consumer Group消费到,而对于同一个Consumer Group,还是保证只有一个Consumer实例消费到这条消息。
3.4 Replication设计
作为消息中间件,数据的可靠性以及系统的可用性,必然依赖数据副本的设计。
Kafka的replica副本单元是topic的partition,一个partition的replica数量不能超过broker的数量,因为一个broker最多只会存储这个partition的一个副本。所有消息生产、消费请求都是由partition的leader replica来处理,其他follower replica负责从leader复制数据进行备份。
Replica均匀分布到整个集群,Replica的算法如下:
将所有Broker(假设共n个Broker)和待分配的Partition排序 将第i个Partition分配到第(i mod n)个Broker上 将第i个Partition的第j个Replica分配到第((i + j) mode n)个Broker上
如图,TopicA有三个partition:part0、part1、part2,每个partition的replica数等于2(一个是leader,另一个是follower),按照以上算法会均匀落到三个broker上。
broker对replica管理:
选举出一个broker作为controller,由它Watch Zookeeper,负责partition的replica的集群分配,以及leader切换选举等流程。
In-Sync-Replica(ISR)
分布式系统在处理节点故障时,需要预先明确节点的”failure”和”alive”的定义。对于Kafka节点,判断是”alive”有以下两个条件:
节点必须和Zookeeper保持心跳连接 如果节点是follower,必须从leader节点上复制数据来备份,而且备份的数据相比leader而言,不能落后太多。Kafka将满足以上条件的replica节点认为是”in sync”(同步中),称为In-Sync-Replica(ISR)。
Kafka的Zookeeper维护了每个partition的ISR信息,理想情况下,ISR包含了partition的所有replica所在的broker节点信息,而当某些节点不满足以上条件时,ISR可能只包含部分replica。例如,上图中的TopicA-part0的ISR列表可能是[broker1,broker2,broker3],也可能是[broker1,broker3]和[broker1]。
数据可靠性
Kafka如何保证数据可靠性?首先看下,Producer生产一条消息,该消息被认为是”committed”(即broker认为消息已经可靠存储)的过程:
消息所在partition的ISR replicas会定时异步从leader上批量复制数据log 当所有ISR replica都返回ack,告诉leader该消息已经写log成功后,leader认为该消息committed,并告诉Producer生产成功。这里和以上”alive”条件的第二点是不矛盾的,因为leader有超时机制,leader等ISR的follower复制数据,如果一定时间不返回ack(可能数据复制进度落后太多),则leader将该follower replica从ISR中剔除。 消息committed之后,Consumer才能消费到。


ISR机制下的数据复制,既不是完全的同步复制,也不是单纯的异步复制,这是Kafka高吞吐很重要的机制。同步复制要求所有能工作的follower都复制完,这条消息才会被认为committed,这种复制方式极大的影响了吞吐量。而异步复制方式下,follower异步的从leader复制数据,数据只要被leader写入log就被认为已经committed,这种情况下如果follower都复制完都落后于leader,而如果leader突然宕机,则会丢失数据。而Kafka的这种使用ISR的方式则很好的均衡了确保数据不丢失以及吞吐量,follower可以批量的从leader复制数据,数据复制到内存即返回ack,这样极大的提高复制性能,当然数据仍然是有丢失风险的。
Kafka本身定位于高性能的MQ,更多注重消息吞吐量,在此基础上结合ISR的机制去尽量保证消息的可靠性,但不是绝对可靠的。
服务可用性
Kafka所有收发消息请求都由leader节点处理,由以上数据可靠性设计可知,当ISR的follower replica故障后,leader会及时地从ISR列表中把它剔除掉,并不影响服务可用性,那么当leader故障后会怎样呢?如何选举新的leader?
leader选举
Kafka在Zookeeper存储partition的ISR信息,并且能动态调整ISR列表的成员,只有ISR里的成员replica才会被选为leader,并且ISR所有的replica都有可能成为leader; leader节点宕机后,Zookeeper能监控发现,并由broker的controller节点从ISR中选举出新的leader,并通知ISR内的所有broker节点。
因此,可以看出,只要ISR中至少有一个replica,Kafka就能保证服务的可用性(但不保证网络分区下的可用性)。
容灾和数据一致性
分布式系统的容灾能力,跟其本身针对数据一致性考虑所选择的算法有关,例如,Zookeeper的Zab算法,raft算法等。Kafka的ISR机制和这些Majority Vote算法对比如下:
ISR机制能容忍更多的节点失败。假如replica节点有2f+1个,每个partition最多能容忍2f个失败,且不丢失消息数据;但相对Majority Vote选举算法,只能最多容忍f个失败。
在消息committed持久化上,ISR需要等2f个节点返回ack,但Majority Vote只需等f+1个节点返回ack,且不依赖处理最慢的follower节点,因此Majority Vote有优势
ISR机制能节省更多replica节点数。例如,要保证f个节点可用,ISR方式至少要f个节点,而Majority Vote至少需要2f+1个节点。
如果所有replica都宕机了,有两种方式恢复服务:
等ISR任一节点恢复,并选举为leader; 选择***个恢复的节点(不一定是ISR中的节点)为leader***种方式消息不会丢失(只能说这种方式最有可能不丢而已),第二种方式可能会丢消息,但能尽快恢复服务可用。这是可用性和一致性场景的两种考虑,Kafka默认选择第二种,用户也可以自主配置。
大部分考虑CP的分布式系统(假设2f+1个节点),为了保证数据一致性,最多只能容忍f个节点的失败,而Kafka为了兼顾可用性,允许最多2f个节点失败,因此是无法保证数据强一致的。

如图所示,一开始ISR数量等于3,正常同步数据,红色部分开始,leader发现其他两个follower复制进度太慢或者其他原因(网络分区、节点故障等),将其从ISR剔除后,leader单节点存储数据;然后,leader宕机,触发重新选举第二节点为leader,重新开始同步数据,但红色部分的数据在新leader上是没有的;***原leader节点恢复服务后,重新从新leader上复制数据,而红色部分的数据已经消费不到了。
因此,为了减少数据丢失的概率,可以设置Kafka的ISR最小replica数,低于该值后直接返回不可用,当然是以牺牲一定可用性和吞吐量为前提了。
重复消息
消息传输有三种方式:
At most once:消息可能会丢失,但不会重复传输
At least once:消息不会丢失,但可能重复传输
Exactly once:消息保证会被传输一次且仅传输一次
Kafka实现了第二种方式,即,可能存在重复消息,需要业务自己保证消息幂等性处理。
3.5 高吞吐设计
对于partition,顺序读写磁盘数据,以时间复杂度O(1)方式提供消息持久化能力。 Producer批量向broker写数据 Consumer批量从broker拉数据 日志压缩 Topic分多个partition,提高并发 broker零拷贝(Zero Copy),使用sendfile系统调用,将数据直接从page cache发送到socket上 Producer可配置是否等待消息committed。如果Producer生产消息,每次都必须等ISR存储后才返回,时延会很高,进而影响整体消息的吞吐量。为了解决这个问题,一方面Producer可以配置减少partition的副本数,例如,ISR大小为1;另一方面,在不太关注消息可靠存储的场景下,Producer可以通过配置选择是否等待消息committed,如下:
这是用户在消息吞吐量和持久化之间做的权衡选择,持久化等级越高,生产消息吞吐量越小,反之,持久化等级越低,吞吐量越高。
3.6 HA基本原理
broker HA
broker集群信息由Zookeeper维护,并选举出一个controller。所有partition的leader选举都由controller决定,将leader的变更直接通过rpc方式通知需要为此做出响应的brokers;controller也负责增删topic以及partition replica的重新分配。
controller在Zookeeper上注册watch,一旦有broker宕机,其对应在Zookeeper的临时节点自动被删除,controller对宕机broker上的所有partition重新分配新leader;如果controller宕机,其他broker通过Zookeeper选举出新的controller,然后同样对宕机broker上的所有partition重新分配新leader。
partition HA
partition leader所在的broker宕机,如上所述,broker controller根据动态维护的ISR,会重新在剩下的broker机器中选出ISR里面的一个成员成为新的leader。如果ISR中至少有一个follower,则可以确保已经committed的数据不丢失;否则选择任意一个replica作为leader,该场景可能会有潜在的数据丢失;如果partition所有的replica都宕机了,就无法保证数据不丢失了,有两种恢复方案,上文已介绍过。
四、推广
腾讯云即将推出高性能的消息队列服务Ckafka,完全兼容开源Kafka API(0.9版本)。Ckafka服务端完全托管在腾讯云上,用户无需自己维护和搭建,使用开源Kafka API客户端即可访问实例,大大降低了用户使用Kafka的门槛,欢迎体验:)
原文链接:https://cloud.tencent.com/community/article/369570
【本文是专栏作者“腾讯云技术社区”的原创稿件,转载请通过联系原作者获取授权】
戳这里,看该作者更多好文
Ubuntu是一个流行的Linux操作系统,基于Debian发行版和GNOME桌面环境,和其他Linux发行版相比,Ubuntu非常易用,和Windows相容性很好,非常适合Windows用户的迁移,预装了大量常用软件,中文版的功能也较全,支持拼音输入法,预装了Firefox、Open Office、多媒体播放、图像处理等大多数常用软件,一般会自动安装网卡、音效卡等设备的驱动,对于不打游戏不用网银的用户来说,基本上能用的功能都有了,在Windows操作系统下不用分区即可安装使用,就如同安装一个应用软件那么容易,整个Ubuntu操作系统在Windows下就如同一个大文件一样,很容易卸载掉。下面我就介绍一下Ubuntu操作系统安装使用的方法,供Ubuntu新手参考,希望能起到Linux扫盲的作用。 下载Ubuntu Ubuntu有三个版本,分别是桌面版(Desktop Edition),服务器版(Server Edition),上网本版(Netbook Remix),普通桌面电脑使用桌面版即可,下载地址请点这里,32位CPU请选择32bit version,上网本则可下载Netbook Remix,目前Ubuntu已经占据三分之一的上网本市场,仅次于Windows XP系统。Google的Chrome操作系统强有力的对手就是Ubuntu Netbook Remix。 目前最新的版本是9.04版,下载后的文件名是ubuntu-9.04-desktop-i386.iso,大小是698M,通过迅雷下载非常快,大约半个小时左右可以下载完毕。 安装Ubuntu 在Windows下可以不用重新分区,直接像安装一个应用程序那样安装Ubuntu,安装方法是,先使用一个虚拟光驱(例如微软的Windows虚拟光驱)装载ubuntu-9.04-desktop-i386.iso文件,然后运行根目录下的wubi.exe,运行前要将本地磁盘的名字都修改为英文名,否则会出现错误信息“UnicodeEncodeError: ascii codec cant encode characters in position 0-3: ordinal not in range(128)”而无法运行。 运行之后,会出现如下界面,选择“Install inside Windows”即可在Windows下直接安装而无需分区。 接着出现下面的安装界面,选择一个磁盘,然后将语言选择为“Chinese(Simplified)简体中文”,Installation size为Ubuntu环境的总共磁盘大小,然后是登录用户名和密码,设置好了以后就点安装继续。 后面的安装操作很简单,不需要手动干预就可以直接安装好整个操作系统,大部分的硬件驱动都可以自动安装好。提示安装完毕后,重启系统,就可以使用Ubuntu了。 自动登录Ubuntu Ubuntu默认是每次登录都是要输入用户名和密码的,这是基于安全方面的考虑,不过对于桌面版,大家都习惯自己的电脑能自动登录,类似Windows XP系统那样,通过一些设置可以实现Ubuntu自动登录。设置的方法是:点击“系统”—“系统管理”—“登录窗口” (需要输入管理员密码),然后在“安全”选项页—勾选(启用自动登录),然后在下拉列表里选择自己的用户名。之后Ubuntu就能够自动登录了。 开机自动运行程序 类似Windows的启动菜单,在Linux也可以实现开机自动运行一些命令,比较简单的方法是修改 /etc/rc.local 文件,将需要执行的命令添加进去。 桌面设置 Ubuntu的桌面,默认有两个任务栏,一个在上面,一个在下面,通常习惯Windows的用户喜欢将上面的移到下面,Ubuntu的面板无法拖动,在上面点右键后,可以让其显示在屏幕下端。 桌面背景设置和Windows很类似,在“桌面”上点右键,点更改桌面背景,就可以进行修改设置。 修改root密码 Ubuntu默认的用户并不是root,我们可以通过操作来使用root这个超级管理员帐号,以获得更大的权限。先打开终端,然后执行下面的语句 sudo passwd root 就可以修改超级管理员root的密码,之后就可以使用su命令切换到root用户来执行某些更高权限的操作。 Hosts修改 在Windows下,我们上Twitter等网站都需要修改hosts文件,在Linux下也有hosts文件,文件位于/etc/hosts,使用root用户可以编辑修改这个文件,主机名和IP的格式与Windows的完全相同,例如: 127.0.0.1 localhost 在Ubuntu下安装软件 Ubuntu下的软件安装有几种方式,常用的是deb包的安装方式,deb是debian系列的Linux包管理方式,ubuntu属于debian的派生,也默认支持这种软件安装方式,当下载到一个deb格式的软件后,直接在界面上就可以安装。 另一种常见的安装方式是源代码编译安装,很多软件会提供了源代码给最终用户,用户需要自行编译安装,先使用tar将源代码解压缩到一个目录下,然后进入这个目录,执行以下三条命令: ./configure make sudo make install 执行完成后,即可完成软件的编译和安装。 还有一种方式是apt-get的安装方法,APT是Debian及其衍生发行版的软件包管理器,APT可以自动下载,配置,安装二进制或者源代码格式的软件包,因此简化了Unix系统上管理软件的过程。常用的安装命令是: sudo apt-get install 软件名 sudo apt-get remove 软件名 Firefox浏览器的更新 Ubuntu安装完成后会自动安装一个Firefox浏览器,遗憾的是这个Firefox版本通常较低,例如Ubuntu 9.04会安装Firefox 3.0,不过我们可以想办法下载最新的Firefox覆盖掉老版本Firefox,具体方法是,先上Firefox官方网站下载最新的Linux版本Firefox,然后将其解压缩到某一个目录下,例如firefox目录,进入终端,到这个目录的父目录,执行下面的语句: sudo cp -r firefox /usr/lib/firefox-3.5.2 sudo mv /usr/bin/firefox /usr/bin/firefox.old sudo ln -s /usr/lib/firefox-3.5.2/firefox /usr/bin/firefox-3.5.2 sudo ln -s /usr/bin/firefox-3.5.2 /usr/bin/firefox 之后就可以将Firefox成功替换为最新的Firefox 3.52版本,未来的Firefox更新也可以使用这种方法。 Firefox的Flash问题 经过我的实际测试,Ubuntu自动安装的Flash插件swfdec存在很多问题,在Firefox中,很多网页的Flash无法显示,包括Google音乐和开心网等,因此建议使用下面两条语句将其卸载。 sudo apt-get remove swfdec-mozilla sudo apt-get remove swfdec-gnome 之后可安装官方的Adobe Flash Player的Linux版,下载地址是: http://get.adobe.com/flashplayer/ 安装完成后,还要解决中文乱码问题,解决方法是执行下面语句: sudo cp /etc/fonts/conf.d/49-sansserif.conf /etc/fonts/conf.d/49-sansserif.conf.bak sudo rm /etc/fonts/conf.d/49-sansserif.conf 之后,Firefox的Flash就完全正常了,在Firefox中访问开心网等Flash网站,显示都正常。 安装常用软件 介绍完了安装的方法和Firefox,下面就可以去各个网站下载一些常用的Linux软件来安装了,下面是我整理的一些常用的Linux软件列表: Linux QQ:访问这个地址,下载deb文件安装,可以在Linux下玩腾讯QQ。 防火墙 firestarter: 使用 sudo apt-get install firestarter 安装。 杀毒软件 AntiVir: 虽然Linux下的病毒很少,但对于新手还是有必要安装一个杀毒软件,访问这个地址可以下载免费版的AntiVir杀毒软件,这个软件我曾经在《五个最佳的防病毒软件》中介绍过。 rpm 转 deb 工具: 使用 sudo apt-get install alien 安装 JAVA环境安装: JRE的安装 sudo apt-get install sun-java6-jre ,JDK的安装 sudo apt-get install sun-java6-jdk eclipse安装: 先到这个地址下载最新的eclipse,然后使用tar xvfz eclipse-php-galileo-linux-gtk.tar.gz -C /opt 解压缩后就可以使用。 Picasa 3 for Linux安装: 访问这个地址,下载后直接安装。 Google Earth安装: 在这里下载最新版本的Google Earth,下载下来是个BIN文件,在图形界面上右击 GoogleEarthLinux.bin,在“权限”选项卡中勾选“允许以程序执行文件”,如下图。 之后在终端上执行 ./GoogleEarthLinux.bin 即可安装。 安装LAMP环境 Ubuntu的桌面版也可以安装LAMP(Linux + Apache + MySQL + PHP)环境,这里我介绍一个最简单的方法,就是使用XAMPP,这个项目我曾经在《常见的WAMP集成环境》中介绍过,XAMPP不但支持Windows,还支持Linux,在其网站下载之后,运行下面两条命令: tar xvfz xampp-linux-1.7.2.tar.gz -C /opt /opt/lampp/lampp start 就可以启动LAMP环境,XAMPP是功能全面的集成环境,软件包中包含Apache、MySQL、SQLite、PHP、Perl、FileZilla FTP Server、Tomcat等等,很适合开发环境使用。 安装程序添加程序菜单和桌面 有些程序是直接解压缩安装的,因此不会添加“应用程序”的菜单项,我们可以手动将其添加菜单项,具体方法是,打开“系统”—“首选项”—“主菜单”,新增即可。 添加桌面快捷方式是,在桌面上点右键,创建启动器。这个“启动器”就是Windows里面的“快捷方式”。 将“应用程序”的菜单项创建到桌面快捷方式的方法是,在“应用程序”的菜单项上单击鼠标右键,选择“将此启动器添加到桌面”或“将此启动器添加到面板”,就可以了。
