Kafka作为一个分布式的流平台,这到底意味着什么?
我们认为,一个流处理平台具有三个关键能力:
- 发布和订阅消息(流),在这方面,它类似于一个消息队列。
- 以容错(故障转移)的方式存储消息(流)。
- 在消息流发生时处理它们。
什么是kafka的优势?它主要应用于2大类应用:
- 构建实时的流数据管道,可靠地获取系统和应用程序之间的数据。
- 构建实时流的应用程序,对数据流进行转换或反应。
要了解kafka是如何做这些事情的,让我们从下到上深入探讨kafka的能力。
首先几个概念:
- kafka作为一个集群运行在一个或多个服务器上。
- kafka集群存储的消息是以topic为类别记录的。
- 每个消息(也叫记录record,我习惯叫消息)是由一个key,一个value和时间戳构成。
kafka有四个核心API:
- 应用程序使用 Producer API 发布消息到1个或多个topic(主题)中。
- 应用程序使用 Consumer API 来订阅一个或多个topic,并处理产生的消息。
- 应用程序使用 Streams API 充当一个流处理器,从1个或多个topic消费输入流,并生产一个输出流到1个或多个输出topic,有效地将输入流转换到输出流。
- Connector API 可构建或运行可重用的生产者或消费者,将topic连接到现有的应用程序或数据系统。例如,连接到关系数据库的连接器可以捕获表的每个变更。
Client和Server之间的通讯,是通过一条简单、高性能并且和开发语言无关的TCP协议。并且该协议保持与老版本的兼容。Kafka提供了Java Client(客户端)。除了Java客户端外,还有非常多的其它编程语言的客户端。
首先来了解一下Kafka所使用的基本术语:
Topic
Kafka将消息分门别类,每一类的消息称之为一个主题(Topic)。
Producer
发布消息的对象称之为主题生产者(Kafka topic producer)
Consumer
订阅消息并处理发布的消息的对象称之为主题消费者(consumers)
Broker
已发布的消息保存在一组服务器中,称之为Kafka集群。集群中的每一个服务器都是一个代理(Broker)。 消费者可以订阅一个或多个主题(topic),并从Broker拉数据,从而消费这些已发布的消息。
主题和日志 (Topic和Log)
让我们更深入的了解Kafka中的Topic。
Topic是发布的消息的类别名,一个topic可以有零个,一个或多个消费者订阅该主题的消息。
对于每个topic,Kafka集群都会维护一个分区log,就像下图中所示:
每一个分区都是一个顺序的、不可变的消息队列, 并且可以持续的添加。分区中的消息都被分了一个序列号,称之为偏移量(offset),在每个分区中此偏移量都是唯一的。
Kafka集群保持所有的消息,直到它们过期(无论消息是否被消费)。实际上消费者所持有的仅有的元数据就是这个offset(偏移量),也就是说offset由消费者来控制:正常情况当消费者消费消息的时候,偏移量也线性的的增加。但是实际偏移量由消费者控制,消费者可以将偏移量重置为更早的位置,重新读取消息。可以看到这种设计对消费者来说操作自如,一个消费者的操作不会影响其它消费者对此log的处理。
再说说分区。Kafka中采用分区的设计有几个目的。一是可以处理更多的消息,不受单台服务器的限制。Topic拥有多个分区意味着它可以不受限的处理更多的数据。第二,分区可以作为并行处理的单元,稍后会谈到这一点。
分布式(Distribution)
Log的分区被分布到集群中的多个服务器上。每个服务器处理它分到的分区。 根据配置每个分区还可以复制到其它服务器作为备份容错。 每个分区有一个leader,零或多个follower。Leader处理此分区的所有的读写请求,而follower被动的复制数据。如果leader宕机,其它的一个follower会被推举为新的leader。 一台服务器可能同时是一个分区的leader,另一个分区的follower。 这样可以平衡负载,避免所有的请求都只让一台或者某几台服务器处理。
Geo-Replication(异地数据同步技术)
Kafka MirrorMaker为群集提供geo-replication
支持。借助MirrorMaker
,消息可以跨多个数据中心或云区域进行复制。 您可以在active/passive场景中用于备份和恢复; 或者在active/passive方案中将数据置于更接近用户的位置,或数据本地化。
生产者(Producers)
生产者往某个Topic上发布消息。生产者也负责选择发布到Topic上的哪一个分区。最简单的方式从分区列表中轮流选择。也可以根据某种算法依照权重选择分区。开发者负责如何选择分区的算法。
消费者(Consumers)
通常来说,消息模型可以分为两种,队列
和发布-订阅式
。 队列的处理方式是:一组消费者从服务器读取消息,一条消息只有其中的一个消费者来处理。在发布-订阅模型中,消息被广播给所有的消费者,接收到消息的消费者都可以处理此消息。
Kafka为这两种模型提供了单一的消费者抽象模型:消费者组(consumer group)。消费者用一个消费者组名标记自己。一个发布在Topic上消息被分发给此消费者组中的一个消费者。假如所有的消费者都在一个组中,那么这就变成了队列模型。假如所有的消费者都在不同的组中,那么就完全变成了发布-订阅模型。更通用的,我们可以创建一些消费者组作为逻辑上的订阅者。每个组包含数目不等的消费者,一个组内多个消费者可以用来扩展性能和容错。正如下图所示:
2个kafka集群托管4个分区(P0-P3),2个消费者组,消费组A有2个消费者实例,消费组B有4个。
正像传统的消息系统一样,Kafka保证消息的顺序不变。再详细扯几句。传统的队列模型保持消息,并且保证它们的先后顺序不变。但是,尽管服务器保证了消息的顺序,消息还是异步的发送给各个消费者,消费者收到消息的先后顺序不能保证了。这也意味着并行消费将不能保证消息的先后顺序。用过传统的消息系统的同学肯定清楚,消息的顺序处理很让人头痛。如果只让一个消费者处理消息,又违背了并行处理的初衷。 在这一点上Kafka做的更好,尽管并没有完全解决上述问题。Kafka采用了一种分而治之的策略:分区。因为Topic分区中消息只能由消费者组中的唯一一个消费者处理,所以消息肯定是按照先后顺序进行处理的。但是它也仅仅是保证Topic的一个分区顺序处理,不能保证跨分区的消息先后处理顺序。 所以,如果你想要顺序的处理Topic的所有消息,那就只提供一个分区。
Kafka的保证(Guarantees)
- 生产者发送到一个特定的Topic的分区上,消息将会按照它们发送的顺序依次加入,也就是说,如果一个消息M1和M2使用相同的producer发送,M1先发送,那么M1将比M2的offset低,并且优先的出现在日志中。
- 消费者收到的消息也是此顺序。
- 如果一个Topic配置了复制因子(replication factor)为N, 那么可以允许N-1服务器宕机而不丢失任何已经提交(committed)的消息。
有关这些保证的更多详细信息,请参见文档的设计部分。
kafka作为一个消息系统
Kafka的流与传统企业消息系统相比的概念如何?
传统的消息有两种模式:队列
和发布订阅
。 在队列模式中,消费者池从服务器读取消息(每个消息只被其中一个读取); 发布订阅模式:消息广播给所有的消费者。这两种模式都有优缺点,队列的优点是允许多个消费者瓜分处理数据,这样可以扩展处理。但是,队列不像多个订阅者,一旦消息者进程读取后故障了,那么消息就丢了。而发布和订阅
允许你广播数据到多个消费者,由于每个订阅者都订阅了消息,所以没办法缩放处理。
kafka中消费者组有两个概念:队列
:消费者组(consumer group)允许同名的消费者组成员瓜分处理。发布订阅
:允许你广播消息给多个消费者组(不同名)。
kafka的每个topic都具有这两种模式。
kafka有比传统的消息系统更强的顺序保证。
传统的消息系统按顺序保存数据,如果多个消费者从队列消费,则服务器按存储的顺序发送消息,但是,尽管服务器按顺序发送,消息异步传递到消费者,因此消息可能乱序到达消费者。这意味着消息存在并行消费的情况,顺序就无法保证。消息系统常常通过仅设1个消费者来解决这个问题,但是这意味着没用到并行处理。
kafka做的更好。通过并行topic的partition —— kafka提供了顺序保证和负载均衡。每个partition仅由同一个消费者组中的一个消费者消费到。并确保消费者是该partition的唯一消费者,并按顺序消费数据。每个topic有多个分区,则需要对多个消费者做负载均衡,但请注意,相同的消费者组中不能有比分区更多的消费者,否则多出的消费者一直处于空等待,不会收到消息
。
kafka作为一个存储系统
所有发布消息到消息队列
和消费分离的系统,实际上都充当了一个存储系统(发布的消息先存储起来)。Kafka比别的系统的优势是它是一个非常高性能的存储系统
。
写入到kafka的数据将写到磁盘并复制到集群中保证容错性。并允许生产者等待消息应答,直到消息完全写入。
kafka的磁盘结构 - 无论你服务器上有50KB或50TB,执行是相同的。
client来控制读取数据的位置。你还可以认为kafka是一种专用于高性能,低延迟,提交日志存储,复制,和传播特殊用途的分布式文件系统
。
kafka的流处理
仅仅读,写和存储是不够的,kafka的目标是实时的流处理。
在kafka中,流处理持续获取输入topic
的数据,进行处理加工,然后写入输出topic
。例如,一个零售APP,接收销售和出货的输入流
,统计数量或调整价格后输出。
可以直接使用producer和consumer API进行简单的处理。对于复杂的转换,Kafka提供了更强大的Streams API。可构建聚合计算
或连接流到一起
的复杂应用程序。
助于解决此类应用面临的硬性问题:处理无序的数据,代码更改的再处理,执行状态计算等。
Streams API在Kafka中的核心:使用producer和consumer API作为输入,利用Kafka做状态存储,使用相同的组机制在stream处理器实例之间进行容错保障。
拼在一起
消息传递,存储和流处理的组合看似反常,但对于Kafka作为流式处理平台的作用至关重要。
像HDFS这样的分布式文件系统允许存储静态文件来进行批处理。这样系统可以有效地存储和处理来自过去的历史数据。
传统企业的消息系统允许在你订阅之后处理未来的消息:在未来数据到达时处理它。
Kafka结合了这两种能力,这种组合对于kafka作为流处理应用和流数据管道平台是至关重要的。
批处理以及消息驱动应用程序的流处理的概念:通过组合存储和低延迟订阅,流处理应用可以用相同的方式对待过去和未来的数据。它是一个单一的应用程序,它可以处理历史的存储数据,当它处理到最后一个消息时,它进入等待未来的数据到达,而不是结束。
同样,对于流数据管道(pipeline),订阅实时事件的组合使得可以将Kafka用于非常低延迟的管道;但是,可靠地存储数据的能力使得它可以将其用于必须保证传递的关键数据,或与仅定期加载数据或长时间维护的离线系统集成在一起。流处理可以在数据到达时转换它。
有关Kafka提供的保证,api和功能的更多信息,可继续查阅本网。
厉害,终于找到一往篇讲的很透的文章,所有疑问基本都能评论区找到答案
“kafka做的更好。通过并行topic的parition ”这里的parition 是不是写漏了个t?
是的,已经更新 ღ( ´・ᴗ・` )
我有个疑问,是一个分区的消息只能由同组的一个消费者消费,还是一个分区的一条消息同一时刻只能由同组的消费者消费,大佬们求教
是前者的话,那为什么在每个topic有多个分区时,需要对多个消费者做负载均衡,是后者的话,就能说通这句话了
你觉得哪个合理?
前者应该合理些,后者的话会降低效率,但是我不太懂前者的负载均衡的具体情况
一条消息,到底应该被消费几次才合理?我从你的评论里感觉你没理解。
消费一次,那就是后者了,这样就能理解了,是那一句确保消费者是该partition的唯一消费者,让我先入为主以为是整个分区消息的唯一消费者。是后者的话,如果同组消费者大于分区数量,那每次多余的消费者是不确定的吧,就是会有多余的消费者,但具体多的是那些消费者不确定,这样理解对不呐
对,谁先抢到是谁的。
一个分区只能有一个消费者消费,即分区A只能由消费者A1消费;
一个消费者可以消费多个分区,即消费者A1可以消费分区A、B,前提是分区个数大于消费者个数的情况;
一个分区只能由一个指定的消费者消费是为了保证消息的顺序消费
看你们俩解释的更乱了,真费心。
看你们俩解释的更乱了,真费心。所以归根结底就是 分区 不管在任何时刻 消费者 是固定的,不能上午是张三的 ,下午就成了李四的了。
说法不对,限定词有问题。应该是一个分区在一个消费者组中只能有一个消费者消费。你说的情况都该基于在一个消费者组中。
实际偏移量由消费者控制,消费者可以将偏移量重置为更早的位置,重新读取消息! 如果consumerA offset=3, consumerB offset=11, 那他们提交后offset该是多少呢?
A是A的,B是B的,组名不同,offset互不干扰。
感觉楼主的那个鸡蛋标签篮子的抽象很有意思,我这里做个拓展。
某一天举办了一次吃鸡蛋大赛,可以以小组的形式参加,报名结束后,一共有两个小组参加:
在他们面前各自放着三条流水线,鸡蛋从流水线滚下来(3 个 分区),厨师(生产者)在后台不停的往流水线里加鸡蛋,防止参赛选手(消费者)不够吃。
A小组正巧有三个人,就一人负责一条流水线,按照流水线的上鸡蛋的顺序吃。
B小组只有两个人,其中 B1 饭量比较大,独自负责两条流水线,这条流水线吃一个,那条流水线吃一个。
这里就对应了一个参赛选择按鸡蛋的生产顺序进行吃鸡蛋,比如
A小组
,那么每个人吃的鸡蛋的顺序都是按照厨师放入流水线的顺序。而B小组
的B1
这个人,就一会儿这吃个一会儿吃那个,没有一个顺序性保证了。比赛的结局就是谁最后吃的鸡蛋最多,谁就获胜。
nice,特别喜欢你这种边学习,边思考,还边总结分享的猿友。
你的例子不完整,如果吃鸡蛋人数大于流水线数量的那
不是说了这句话嘛:相同的消费者组中不能有比分区更多的消费者,否则多出的消费者一直处于空等待,不会收到消息
以我的理解,用鸡蛋和篮子并不合适哎。
厨师做好的鸡蛋,如果A小组先拿到,那B小组就那不到了。
对于kafka中的消息,一个消费者组只能有一个消费者去消费一个消息,多个消费者组可以重复消费
可以把吃鸡蛋改成数鸡蛋个数,这样消费者组A的A1把流水线1上的鸡蛋汇总后,不耽误消费者组B的B1去再次累计个数
但是「鸡蛋」是唯一呀,消费者组A或者消费者组B吃的鸡蛋应该是同一个。
对啊,「鸡蛋」是唯一的,那么消费组A的成员吃了之后,消费组B就就味都闻不到了。
对于kafka,应该是消费者组A吃了鸡蛋1后,这个鸡蛋1还是完整的,由消费组B再吃一次。
所以我感觉用厨师炒鸡蛋,有两个组去吃的这个例子不是那么合适。
1、Kafka如何保证写入的数据能被精确的读取到
2、Kafka生产者生产一条数据后消费者如何知道该去读取数据了和读取那一条数据
1、kafka生产者和消费者之间是解耦的,消息也是无状态的(默认7天被自动删除),根本不关心消息被谁读取的。
2、消费者根据订阅topic来读取消息的,按顺序读取。
这些在文章里都有,先看文章吧。
我觉着你这个问题应该这么回答你, kafka中的消息按照topic进行分类, 比如我们把订单消息作为订单topic, 由订单处理相关的系统来作为消费者订阅这个主题进行处理, 把评价消息作为评价topic, 交由评价处理相关的系统进行处理, 至于具体读取哪一条数据(好像消费数据都是顺序的消费, 随机消费这种还真没了解过)这种问题我还真不太了解, 等我精进了再回来想想这个问题哈哈
我来说说的我的理解,有什么不对尽管指教。
1、Kafka中采用分区的设计有几个目的。一是可以处理更多的消息,不受单台服务器的限制。Topic拥有多个分区意味着它可以不受限的处理更多的数据。
2、Log的分区被分布到集群中的多个服务器上。每个服务器处理它分到的分区。 根据配置每个分区还可以复制到其它服务器作为备份容错。 每个分区有一个leader,零或多个follower。Leader处理此分区的所有的读写请求,而follower被动的复制数据。
我想问一下,这两句话所指的分区是一样的吗?,如果一样的话第一句创建的多个分区不是在同一台机器吗,他是如何到多台机器上了呀
比如你的kafka集群有3台broker节点,A、B、C,分区P有3个副本,它会平均分布在这3个服务器上,其中有个是leader,这样你挂2台kafka节点也没有问题。
备份是需要配置的吧?还是说3个partition被平均分配的时候,就区分leader和follower了?
你创建topic的时候,需要指定几个分区和对应的副本,如果不指定,默认只有1个副本,也就是这个副本就是leader
“如果一个Topic配置了复制因子(replication factor)为N, 那么可以允许N-1服务器宕机而不丢失任何已经提交(committed)的消息。” 这句话强调了已经提交的消息没有问题。我的问题是在Apache Kafka实战一书中,1.4.6 ISR 介绍里面的,“一个partition可以配置N个replica,那么这是否就意味着该partition可以容忍N-1个replica失效而不丢失数据呢?答案是“否”!”,原因大概是说不能将消息置为已提交?“只有该集合中所有replica都接收到了同一条消息,Kafka才会将该消息置为已提交状态”。书上的这个要怎么理解?在此谢过!
已提交意味着所有的副本都收到了,而是否为“已提交”是生产者的
acks
决定的。可参考:https://www.orchome.com/511
搜索:acks
谢谢,我明白了。是指默认配置下,消息发送给leader,leader立即异步返回,返回后此时follower还未复制,leader就挂掉了,消息也就丢失了。此时消息状态并不是已提交。如果配置acks=all ,是不是可以保证消息不丢失,但是会影响kafka效率。
是的,可以保证。
请问一下,kafka是只能在patition上面做到有序,而topic如果有多个partition是不是就不能保证topic是有序的。
是的
“还有非常多的其它编程语言的客户端。”这个链接协议https多了一个s
都存在5年了.... 感谢指正。
大佬,但是这连接中多余的一个“s”,还是没有去掉。
果然...已修复。
嗯嗯 可以跳转了, 给大佬的效率点赞 ^v^