当前位置: 首页 > news >正文

深入浅出理解kafka ---- 万字总结

1.Kafka简介


Kafka 本质上是一个 MQ(Message Queue),使用消息队列的优点:

解耦:允许独立的扩展或修改队列两边的处理过程。
可恢复性:即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
缓冲:有助于解决生产消息和消费消息的处理速度不一致的情况。
灵活性和峰值处理能力:不会因为突发的超负荷的请求而完全崩溃,消息队列能够使关键组件顶住突发的访问压力。
异步通信:消息队列允许用户把消息放入队列但不立即处理它。

先介绍消息队列的优点: 

消息队列:

消息队列的异步处理

主要应用于短信通知、终端状态推送、App推送、用户注册等。

同步处理:

 我们同步处理的话,我们执行下一个步骤需要等上个步骤完成才能执行下一步,这样网速就很慢了,但是我们是不是可以将库存作为生产者,在库存和订单,短信,统计之间加一个消息队列的话,并且开启订单,短信,统计三个(消费者)线程,分别处理自己的任务,那么我们在库存里拿出以后(生产者生产后立马返回再去生产放到消息队列里,然后通知消费者线程去消费)这样我们就不需要一步一步

的等待

异步处理:

 异步处理的优势:更快速返回结果;减少等待,实现并发处理,提升系统总体性能。
消息队列的模式可以有:
(1)1对1:读取之后立刻从队列中移除消息。
(2)1对多:这种模型是发布订阅模型,消息队列的元素可以被重复消费。至于何时删除消息,可以设置消息的存活周期;比如kafka可以设置24H后删除消息。

消息队列的流量控制(削峰)

在秒杀场景下的下单状态,使用消息队列隔离网关和后端服务,以达到流量控制和保护后端服务的目的。

设置消息队列的最大限制数量,在达到最大数量时网关不再生产消息到消息队列中。

可以这么想:有一个商品秒杀活动,商品数量为100,当消息队列的数量达到100时不再生产秒杀成功消息,直接返回秒杀失败给用户,只有1到100的用户秒杀成功 获得商品。

那么我们以服务器设计的角度去思考,在某一时刻,我们是为秒杀服务提供服务的生产者,我们生产到消息队列的数量达到100的时候就不再生产,而我们的客户端再去网上秒杀购物的时候,从消息队列里拉取,当看到消息队列为空(也就是货物为0),那么就返回秒杀失败

消息队列的服务解耦

A系统负责数据分发,其他系统调用A系统提供的接口处理数据;当新增一个系统时,A系统需要改代码调用新的系统,并实现新的接口给新的系统去调用。这种方式是系统间高度耦合。

  

使用消息队列,A系统负责将数据分发到MQ,消费端根据需要从MQ获取消息即可,不需要就取消MQ的消费。

消息队列的发布订阅

用户需要先去注册,才能收到相关消息。

 

 比如游戏里面跨服:
(1) 广播今天整体还剩多少把屠龙刀可以暴。
(2) 广播用户暴的屠龙刀的消息。

消息队列的高并发缓冲


这个和消息队列的流量控制(削峰)有些类似。区别在于,这里没有大小限流,可能在某个时间点会出现超过后端处理能力的访问;比如后端处理能力是50000每秒,在某个时间点出现每秒80000的访问,这就可能造成击穿。

针对此情况,消息全部放入消息队列,消息队列提供可以把数据固化到磁盘的能力,降低高峰数据对后端的短暂冲击。

比如,后端处理能力50000,某个短暂时间点(比如一秒的时间)数据访问达到80000,消息队列将多的数据缓存到磁盘,后端仍然处理50000数据;冲击点退去后,访问数据降到了30000,那么消息队列将把缓存的数据放到后端处理。

比如kafka 日志服务、监控上报。

2.Kafka的架构 

Kafka只写数据到leader副本,也只从leader副本获取数据。如果leader失效,会重新选择出leader(从Follower副本中选出,并且得是在同一个topic中的)。
优点类似MySQL的主从关系,写数据都是到主机里面,但是读数据不一样,Kafka读数据只能从主机里面读。

1.Kafka 存储的消息来自任意多被称为 Producer 生产者的进程。数据从而可以被发布到不同的 2.Topic 主题下的不同 Partition 分区。
在一个分区内,这些消息被索引并连同时间戳存储在一起。其它被称为 Consumer 消费者的进程可以从分区订阅消息。
3.Kafka 运行在一个由一台或多台服务器组成的集群上,并且分区可以跨集群结点分布。
 

2.1 Kafka 一些重要概念


Producer:消息生产者,向 Kafka Broker 发消息的客户端。
Consumer:消息消费者,从 Kafka Broker 获取消息的客户端。
Consumer Group:消费者组(CG),消费者组内每个消费者负责消费不同分区的数据,提高消费能力。一个分区只能由组内一个消费者消费,消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。

(注意!!!当同组消费组订阅了同一个topic的话,那么同组消费者在消费消息队列的内容的话,那么每次只能由组内任意一个消费者进行消费,不能同时消费组内的所有消费者同时消费)
Broker:一台 Kafka 机器就是一个 Broker。一个集群(kafka cluster)由多个 Broker 组成。一个Broker 可以容纳多个 Topic。
Topic:可以理解为一个队列,Topic 将消息分类,生产者和消费者面向的是同一个Topic。

(其实这么设计主要是理解为一个逻辑上的存储,比如某个公众号的内容都存在topic上面,在物理上是存在每个partition)
Partition:为了实现扩展性,提高并发能力,一个非常大的 Topic 可以分布到多个 Broker (即服务器)上,一个 Topic 可以分为多个 Partition,同一个topic在不同的分区的数据是不重复的,每个 Partition 是一个有序的队列,其表现形式就是一个一个的文件夹。

(物理上表示的存储单元)
Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)。
Message:消息,每一条发送的消息主体。
Leader:每个分区多个副本的“主”副本,生产者发送数据的对象,以及消费者消费数据的对象,都是 Leader。
Follower:每个分区多个副本的“从”副本,实时从 Leader 中同步数据,保持和 Leader 数据的同步。Leader 发生故障时,某个 Follower 还会成为新的 Leader。
Offset:消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从消费位置继续消费。同一主题,不同的分区,它们的offset是独立的。
ZooKeeper:Kafka 集群能够正常工作,需要依赖于 ZooKeeper,ZooKeeper 帮助 Kafka 存储和管理集群信息。

!!!注意在我们启动kafka集群的时候,需要先启动zookeeper才能启动kafka

2.2 工作流程

不同的partition的offerset 是独立的。
Kafka 中消息是以 Topic 进行分类的,生产者生产消息,消费者消费消息,面向的都是同一个 Topic。(就是我们前面提到的topic是逻辑上的概率)
Topic 是逻辑上的概念,而 Partition 是物理上的概念,每个 Partition 对应于一个 log 文件,该 log 文件中存储的就是 Producer 生产的数据。Producer 生产的数据会不断追加到该 log 文件末端(顺序写),且每条数据都有自己的 Offset。
消费者组中的每个消费者,都会实时记录自己消费到了哪个 Offset,以便出错恢复时,从上次的位置继续消费。

我们的partition上会记录offset,这个offest代表消费者消费的位置,以便出错恢复时,从上次的位置继续消费
日志默认在:/tmp/kafka-logs

2.3 文件存储

Kafka文件存储也是通过本地落盘的方式存储的,主要是通过相应的log与index等文件保存具体的消息文件。

生产者不断的向log文件追加消息文件,为了防止log文件过大导致定位效率低下,Kafka的log文件以1G为一个分界点,当.log文件大小超过1G的时候,此时会创建一个新的.log文件,同时为了快速定位大文件中消息位置,Kafka采取了分片和索引的机制来加速定位。

在kafka的存储log的地方,即文件的地方,会存在消费的偏移量以及具体的分区信息,分区信息的话主要包括.index和.log文件组成,
所以总结一句话,就是一个topic有多个partition,一个partition对应一个log文件,我们的生产者生产的数据写到topic的时候就是写入到该topic的partition中也就是写到log文件中,log文件又分为多个segment,一个segment对应两个文件.index和.log文件

2.4 副本原理


副本机制(Replication),也可以称之为备份机制,通常是指分布式系统在多台网络互联的机器上保存有相同的数据拷贝。副本机制的好处在于:

提供数据冗余(即提高可用性)。
提供高伸缩性(支撑更高的读请求量)。
改善数据局部性(降低系统延时)。
目前Kafka只实现了副本机制带来的第 1 个好处,即是提供数据冗余实现高可用性和高持久性。

在kafka生产环境中,每台 Broker 都可能保存有各个主题下不同分区的不同副本,因此,单个 Broker上存有成百上千个副本的现象是非常正常的。

比如了一个有 3 台 Broker 的 Kafka 集群上的副本分布情况。主题 1 分区 0 的 3 个副本分散在 3 台 Broker 上,其他主题分区的副本也都散落在不同的 Broker 上,从而实现数据冗余。
 

上述的话总结一下就是每个broker对应一个kafka服务器,一个broker下保存多个topic的partition,那么我们如果有多个broker的话,并且每个类型的topic的分区保存在多个kafka服务器上(broker上),可能比如topic1的follower副本保存在broker2,topic1的leader副本保存在broker3下,这样冗余的把数据分布在多个kafka服务器上的话,那么如果某一个服务器宕机的话

并且某个topic的leader副本下线了,那么我们就可以用其他kafka服务器(broker)中选出follower作为leader副本,当之前下线的副本上线之后,作为follower加入到kafka集群当中

 

Kafka是基于领导者(Leader-based)的副本机制: 

1. 在 Kafka 中,副本分成两类:领导者副本和追随者副本。每个分区在创建时都要选举一个副本,称为领导者副本,其余的副本自动称为追随者副本。


2.Kafka 副本机制中的追随者副本是不对外提供服务的。


3.当领导者副本挂掉了,或领导者副本所在的 Broker 宕机时,Kafka 依托于 ZooKeeper 提供的监控功能能够实时感知到,并立即开启新一轮的领导者选举,从追随者副本中选一个作为新的领导者。老 Leader 副本重启回来后,只能作为追随者副本加入到集群中。
 

2.5 分区和主题的关系

  • 一个分区只属于一个主题。
  • 一个主题可以有多个分区。
  • 同一主题的不同分区内容不一样,每个分区有自己独立的offset。
  • 同一主题不同的分区能够放置到不同节点的broker。
  • 分区规则设置得当可以使得同一主题的消息均匀落在不同的分区。

2.6 生产者

生产者是数据的入口。Producer在写入数据的时候永远是找leader,不会直接将数据写入follower。

2.6.1 分区可以水平扩展

Kafka 的消息组织方式实际上是三级结构:主题 - 分区 - 消息。主题下的每条消息只会保存在某一个分区中,而不会在多个分区中被保存多份。

分区的作用主要提供负载均衡的能力,能够实现系统的高伸缩性(Scalability)。不同的分区能够被放置到不同节点的机器上,而数据的读写操作也都是针对分区这个粒度而进行的,这样每个节点的机器都能独立地执行各自分区的读写请求处理。这样,当性能不足的时候可以通过添加新的节点机器来增加整体系统的吞吐量。

分区原则:需要将 Producer 发送的数据封装成一个 ProducerRecord 对象。
该对象需要指定一些参数:

topic:string 类型,NotNull。
partition:int 类型,可选。
timestamp:long 类型,可选。
key:string 类型,可选。
value:string 类型,可选。
headers:array 类型,Nullable。

2.6.2 分区策略

所谓分区策略是决定生产者将消息发送到哪个分区的算法。Kafka 为我们提供了默认的分区策略,同时它也支持自定义分区策略。

分区的原因

  • 方便在集群中扩展:每个partition通过调整以适应它所在的机器,而一个Topic又可以有多个partition组成,因此整个集群可以适应适合的数据
  • 可以提高并发:以Partition为单位进行读写。类似于多路。

2.6.2.1 轮询策略


Round-robin 策略,即顺序分配。比如一个主题下有 3 个分区,那么第一条消息被发送到分区 0,第二条被发送到分区 1,第三条被发送到分区 2,以此类推。当生产第 4 条消息时又会重新开始,即将其分配到分区 0

轮询策略有非常优秀的负载均衡表现,它总是能保证消息最大限度地被平均分配到所有分区上,故默认
情况下它是最合理的分区策略
,也是我们最常用的分区策略之一。

2.6.2.2 随机策略-Randomness 策略


随机就是我们随意地将消息放置到任意一个分区上,如下面这张图所示。 

 随机策略是老版本生产者使用的分区策略,在新版本中已经改为轮询了。

2.6.2.3 按消息键保序策略-Key-ordering 策略


Kafka 允许为每条消息定义消息键,简称为 Key。这个 Key 的作用非常大,它可以是一个有着明确业务
含义的字符串,比如客户代码、部门编号或是业务 ID 等;也可以用来表征消息元数据。特别是在 Kafka
不支持时间戳的年代,在一些场景中,工程师们都是直接将消息创建时间封装进 Key 里面的。一旦消息被定义了 Key,那么你就可以保证同一个 Key 的所有消息都进入到相同的分区里面,由于每个分区下的消息处理都是有顺序的,故这个策略被称为按消息键保序策略。
key1 — 落在同一分区
key2 — 落在同一个分区
 

2.6.2.4 默认分区规则。
  1. 如果指定的partition,那么直接进入该partition。
  2. 如果没有指定partition,但是指定了key,使用key的 hash一选择partition。
  3. 如果既没有指定partition,也没有指定key,使用轮询一的方式进入partition。

2.7 消费者

传统的消息队列模型的消息一旦被消费,就会从队列中被删除,而且只能被下游的一个Consumer 消费。这种模型的伸缩性(scalability)很差,因为下游的多个 Consumer 都要“抢”这个共享消息队列的消息。发布 / 订阅模型倒是允许消息被多个 Consumer 消费,但它的问题也是伸缩性不高,因为每个订阅者都必须要订阅主题的所有分区。这种全量订阅的方式既不灵活,也会影响消息的真实投递效果。

当 Consumer Group 订阅了多个主题后,组内的每个实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息。Consumer Group 之间彼此独立,互不影响,它们能够订阅相同的一组主题而互不干涉。再加上 Broker 端的消息留存机制,Kafka 的 Consumer Group 完美地规避了上面提到的伸缩性差的问题。可以这么说,Kafka 仅仅使用 Consumer Group 这一种机制,却同时实现了传统消息引擎
系统的两大模型:

如果所有实例(消费者)都属于同一个 Group,那么它实现的就是点对点消息队列模型;
如果所有实例(消费者)分别属于不同的 Group,那么它实现的就是发布 / 订阅模型。
 

2.7.1 消费方式


Consumer 采用 Pull(拉取)模式从 Broker 中读取数据。

Pull 模式则可以根据 Consumer 的消费能力以适当的速率消费消息。Pull 模式不足之处是,如果 Kafka没有数据,消费者可能会陷入循环中,一直返回空数据。
因为消费者从 Broker 主动拉取数据,需要维护一个长轮询,针对这一点, Kafka 的消费者在消费数据时会传入一个时长参数 timeout。如果当前没有数据可供消费,Consumer 会等待一段时间之后再返回,这段时长即为 timeout。

2.7.2 分区分配策略


一个消费者可以订阅多个主题,可以去消费多个分区,一个分区不支持多个消费者(同一个消费组)读
取。

一个消费者组中有多个 consumer,一个 topic 有多个 partition,所以必然会涉及到 partition 的分
配问题,即确定那个 partition 由哪个 consumer 来消费。当消费者组里面的消费者个数发生改变的时
候,也会触发再平衡。

Kafka 有四种分配策略,可以通过参数 partition.assignment.strategy 来配置,默认 Range + CooperativeSticky。

RoundRobin:针对集群中的所有topic;轮询的方式依次将分区分配给消费者。
Range,默认为Range:针对每个topic;通过 分区数 / 消费者数 决定每个消费者消费几个分区。如果除不尽则前面几个消费者会多消费1个分区(最好要保证分区数量可以整除消费者数量,不然会随着topic数量的增多,数据倾斜会越严重)。
Sticky:首先会尽量均衡放置分区到消费者上面,出现同一消费组内消费者出现问题的时候,会尽量保持原有分配的分区不变化。
CooperativeSticky:在不停止消费的情况下进行增量再平衡。


(1)RangeAssignor分配策略


RangeAssignor 分配策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度, 然后将分
区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。

每一个主题,RangeAssignor策略会将消费组内所有订阅这个主题的消费者按照名称的字典序排序,然
后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分
区。

假设n= 分区数/消费者数量,m= 分区数%消费者数量,那么前m个消费者每个分配n+1个分区,后面
的(消费者数量-m)个消费者每个分配n个分区。

假设消费组内有2个消费者C0和C1都订阅了主题t0和t1, 并且每个主题都有4个分区,那 么订阅的所有分
区可以标识为: t0p0、t0p1、t0p2、t0p3、t1p0、t1p1、t1p2、t1p3。最终的分配结果为:

消费者C0: t0p0、t0p1、t1p0、t1p1
消费者C1: t0p2、t0p3、t1p2、t1p3
这样分配得很均匀,那么这个分配策略能够一直保持这种良好的特性吗?我们不妨再来看 另一种情况。
假设上面例子中2个主题都只有3个分区,那么订阅的所有分区可以标识为:t0p0、 t0p1、t0p2、
t1p0、t1p1、t1p2最终的分配结果为:

消费者C0: t0p0、t0p1、t1p0、t1p1
消费者C1: t0p2、t1p2
可以明显地看到这样的分配并不均匀,如果将类似的情形扩大,则有可能出现部分消费者过载的情况。
对此我们再来看另一种RoundRobinAssignor策略的分配效果如何。

总结:
最好要保证分区数量可以整除消费者数量,不然会随着topic数量的增多,数据倾斜会越严重

(2)RoundRobinAssignor分配策略


RoundRobinAssi gnor 分配策略的原理是将消费组内所有消费者及消费者订阅的所有主题的分 区按照字
典序排序,然后通过轮询方式逐个将分区依次分配给每个消费者。RoundRobinAssignor 分配策略对应
的Partition.assignment.strategy参数值为org.apache.kafka.C1ients.Consumer.RoundRobinAssignor。

如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor分配策略的分区
分配会是均匀的。

比如:假设消费组中有2个消费者C0 和C1都订阅了主题 t0和t1, 并且每个主题都有3个分区,那么订阅的
所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、 t1p1、t1p2。最终的分配结果为:

消费者C0: t0p0、t0p2、t1p1
消费者C1: t0p1、t1p0、t1p2
如果同一个消费组内的消费者订阅的信息是不相同的, 那么在执行分区分配的时候就不是完全的轮询分
配,有可能导致分区分配得不均匀。 如果某个消费者没有订阅消费组内的某个主题, 那么在分配分区的
时候此消费者将分配不到这个主题的任何分区。

比如:假设消费组内有3个消费者(C0、 C1和C2), 它们共订阅了3个主题(t0、t1、 t2) , 这 3个主题分别有
1、2、3个分区, 即整个消费组订阅了t0p0、 t1p0、 t1p1、 t2p0、 t2p1、 t2p2这6个分区。 具体而
言, 消费者 C0 订阅的是主题t0, 消费者C1 订阅的是主题t0和t1, 消费者C2 订阅的是主题t0、t1和t2, 那
么最终的分配结果为:

消费者C0: t0p0
消费者C1: t1p0
消费者C2: t1p1、t2p0、 t2p1、t2p2
可以看到RoundRobinAssignor策略也不是十分完美, 这样分配其实并不是最优解, 因为完全可以将分
区t1p1 分配给消费者C1。

所以需要注意:如果使用RoundRobinAssignor策略,则消费者应该订阅相同的主题。

(3)StickyAssignor分配策略


我们再来看一下StickyAssignor分配策略, “sticky"这个单词可以翻译为“ 黏性的”, Kafka 从0.11.x版本
开始引入这种分配策略, 它主要有两个目的:
(1)分区的分配要尽可能均匀。
(2)分区的分配尽可能与上次分配的保待相同。

当两者发生冲突时, 第一个目标优先于第二个目标。

鉴于这两个目标, StickyAssignor分配策略的具体实现要比RangeAssignor和RoundRobinAssignor这
两种分配策略要复杂得多。 我们举 例来看一下StickyAssignor分配策略的实际效果。

假设消费组内有3个消费者(C0、C1和C2),它们都订阅了4个主题(t0、t1、t2、t3),并且每个主题有2个
分区。 也就是说,整个消费组订阅了t0p0、 t0p1、 t1p0、 t1p1、 t2p0、 t2p1、 t3p0、 t3p1这8个分区。 最终的分配结果如下:

消费者C0: t0p0、t1p1、t3p0
消费者C1: t0p1、t2p0、t3p1
消费者C2: t1p0、t2p1
这样初看上去似乎与采用RoundRobinAssignor分配策略所分配的结果相同, 但事实是否真的如此呢?
再假设此时消费者 C1脱离了消费组, 那么消费组就会执行再均衡操作,进而消费分区会重新分配。 如
果采用RoundRobinAssignor 分配策略, 那么此时的分配结果如下:

消费者C0: t0p0、t1p0、t2p0、t3p0
消费者C2: t0p1、t1p1、t2p1、t3p1
如分配结果所示,RoundRobinAssignor分配策略会按照消费者C0 和C2进行重新轮询分配。 如果此时
使用的是StickyAssignor分配策略,那么分配结果为:

消费者C0: t0p0、t1p1、t3p0、t2p0
消费者C2: t1p0、t2p1、t0p1、t3p1
可以看到分配结果中保留了上一次分配中对消费者 C0 和C2的所有分配结果,并将原来消费者C1的 “ 负
担 “ 分配给了剩余的两个消费者 C0 和C2, 最终 C0 和C2的分配还保持了均衡。

如果发生分区重分配,那么对于同一个分区而言,有可能之前的消费者和新指派的消费者不是同一个,
之前消费者进行到一半的处理还要在新指派的消费者中再次复现一遍,这显然很浪费系统资源。

StickyAssignor 分配策略如同其名称中的"st1cky" 一样,让分配策略具备一定 的 “ 黏性 ” ,尽可能地让
前后两次分配相同,进而减少系统资源的损耗及其他异常情况的发生。

到目前为止,我们分析的都是消费者的订阅信息都是相同的情况,我们来看一下订阅信息不同的清况下
的处理。

举个例子,同样消费组内有3个消费者(C0、C1和C2) , 集群中有3个主题(t0、t1和 t2) , 这3个主题分别有
1、2、3个分区。也就是说,集群中有t0p0、 t1p0、 t1p1、 t2p0、 t2p1、 t2p2这6个分区。消费者
C0 订阅了主题t0,消费者C1订阅了主题t0和t1, 消费者C2订阅了主题t0、t1和t2。 如果此时采用
RoundRobinAssignor分配策略,那么最终的分配结果如RoundRobinAssignor分配策略时的一样

RoundRobinAssignor分配策略的分配结果

消费者C0: t0p0
消费者C1: t1p0
消费者C2: t1p1、t2p0、t2p1、t2p2
如果此时采用的是StickyAssignor分配策略,那么最终的分配结果如下所示。
StickyAssignor分配策略的分配结果

消费者C0: t0p0
消费者C1: t1p0、t1p1
消费者C2: t2p0、t2p1、t2p2
可以看到这才是一个最优解(消费者C0 没有订阅主题t1和t2, 所以不能分配主题t1和t2 中的任何分区给它, 对于消费者C1也可同理推断)。

假如此时消费者C0 脱离了消费组, 那么RoundRobinAssignor分配策略的分配结果为:

消费者C1: t0p0、t1p1
消费者C2: t1p0、t2p0 、t2p1、 t2p2
可以看到RoundRobinAssignor策略保留了消费者C1和C2中原有的3个分区的分配:t2p0、 t2p I和t2p2。

如果采用的是StickyAssignor分配策略, 那么分配结果为:

消费者C1: t1p0、t1p1、t0p0
消费者C2: t2p0、t2p1、 t2p2
可以看到StickyAssignor分配策略保留了消费者C1和C2中原有的5个分区的分配:t1p0、t1p1、t2p0、
t2p1、t2p2。

使用StickyAssignor分配策略的一个优点就是可以使分区重分配具备 “ 黏性"’ 减少不必要的分区移动(即
一个分区剥离之前的消费者,转而分配给另一个新的消费者)。
StickyAssignor分配策略比另外两者分配策略而言显得更加优异,但这个策略的代码实现也异常复杂,就不介绍了

2.8 数据可靠性保证


为保证 Producer 发送的数据,能可靠地发送到指定的 Topic,Topic 的每个 Partition 收到 Producer发送的数据后,都需要向 Producer 发送 ACK(ACKnowledge 确认收到)。如果 Producer 收到 ACK,就会进行下一轮的发送,否则重新发送数据。
 

2.8.1 副本数据同步策略

确保有 Follower 与 Leader 同步完成,Leader 再发送 ACK,这样才能保证 Leader 挂掉之后,能在 Follower 中选举出新的 Leader 而不丢数据。

 

2.8.1.1 ISR(同步副本集)


猜想???

采用了第二种方案进行同步ack之后,如果leader收到数据,所有的follower开始同步数据,但有一个follower因为某种故障,迟迟不能够与leader进行同步,那么leader就要一直等待下去,直到它同步完成,才可以发送ack,此时需要如何解决这个问题呢?

解决!!!

leader中维护了一个动态的ISR(in-sync replica set),即与leader保持同步的follower集合,当ISR中的follower完成数据的同步之后,给leader发送ack,如果follower长时间没有向leader同步数据,则该follower将从ISR中被踢出,该之间阈值由replica.lag.time.max.ms参数设定。当leader发生故障之后,会从ISR中选举出新的leader。
 

2.8.2 ACK 应答机制

Kafka 为用户提供了三种可靠性级别,用户根据可靠性和延迟的要求进行权衡,选择以下的配置。

ACK 参数配置:

0:Producer 不等待 Broker 的 ACK,这提供了最低延迟,Broker 一收到数据还没有写入磁盘就已经返回,当 Broker 故障时有可能丢失数据。


1:Producer 等待 Broker 的 ACK,Partition 的 Leader 落盘成功后返回 ACK,如果在 Follower同步成功之前 Leader 故障,那么将会丢失数据。


-1(all):Producer 等待 Broker 的 ACK,Partition 的 Leader 和 Follower 全部落盘成功后才返回 ACK。但是在 Broker 发送 ACK 时,Leader 发生故障,则会造成数据重复。
 

 2.8.3 可靠性指标


1. 分区副本,你可以创建更多的分区来提升可靠性,但是分区数过多也会带来性能上的开销,一般来说,3个副本就能满足对大部分场景的可靠性要求。

(举个例子如果分区过多的话,那么我们kafka需要维护这些分区,并且还要创建大量副本来保证,虽然分区多了,一个坏了丢失的数据会少很多,但是同样的性能就下降很多了,因为分区副本需要同步进行维护)


2. ACKS,生产者发送消息的可靠性,也就是我要保证我这个消息一定是到了broker并且完成了多副本的持久化。

(但是如果ack如果要确保全部副本的持久化和同步的话其实也会带来性能下降问题,不理解的可以去ack应答机制那里反复看看)


3. 保障消息到了broker之后,消费者也需要有一定的保证,因为消费者也可能出现某些问题导致消息没有消费到。


4.enable.auto.commit默认为true,也就是自动提交offset,自动提交是批量执行的,有一个时间窗口,这种方式会带来重复提交或者消息丢失的问题,所以对于高可靠性要求的程序,要使用手动提交。 对于高可靠要求的应用来说,宁愿重复消费也不应该因为消费异常而导致消息丢失。


总结


一个主题多个分区的场景下,kafka只能保证同一个分区的消息顺序性,不能保证不同分区间的消息顺序性。
一般,配置三个副本就可以满足绝大部分需求。
一个消费者可以订阅多个主题,可以去消费多个分区,但一个分区不支持多个消费者(同一个消费组)读取。

 

        如果所有的Consumer都属于一个group,那么就是一对一、点对点的消费,如果每个consumer属于不同的group,那么消息就是广播给所有的消费者。这个实际上是根据partition来分的,一个partition,只能被消费组里的一个消费者消费,但是可以同时被多个消费组消费,消费组里的每个消费者拥有与之对应的一个partition,因此,对于一个topic,同一个group中中不能有多于partition个数的consumer同时消费,否则某些consumer将无法消费到数据。
 

相关文章:

深入浅出理解kafka ---- 万字总结

1.Kafka简介 Kafka 本质上是一个 MQ(Message Queue),使用消息队列的优点: 解耦:允许独立的扩展或修改队列两边的处理过程。可恢复性:即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系…...

一对一聊天

服务端 package 一对一用户;import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector;…...

IDEA版SSM入门到实战(Maven+MyBatis+Spring+SpringMVC) -Mybatis查询中返回值四种情况

第一章 Mybatis查询中返回值四种情况 1.1 查询单行数据返回单个对象 /*** 通过id获取员工信息*/ public Employee selectEmpById(int empId);<select id"selectEmpById" resultType"employee">SELECTid,last_name,email,salaryFROMtbl_employeeWHE…...

华为云安全组规则

初始发布cce,快被安全组搞死了。现在把自己的研究成果综合一下,在这里给自己留痕,希望对迷惑的朋友有帮助。 先搞懂安全组是个啥东东: 安全组规则 操作场景 安全组实际是网络流量访问策略,通过访问策略可以控制流量入方向规则和出方向规则,通过这些规则可以为加入安全组…...

MySQL之binlog文件过多处理方法

背景 MySQL由于大量读写&#xff0c;导致binlog文件特别的多。从而导致服务器disk空间不足问题。 先备份binlog文件 tar -zcvf mysql.tar.gz mysql/data/mysql-bin.00* 修改MySQL配置 binlog过期时间 show variables like expire_logs_days; 这里 0 表示 永不过期 如果为 n…...

力扣面试150题 | 88.合并两个有序数组

力扣面试150题 &#xff5c; 88.合并两个有序数组 题目描述解题思路代码实现复杂度分析 题目描述 88.合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并…...

Spring Cache快速入门教程及案例

1. Spring Cache介绍 Spring Cache提供了一组注解&#xff0c;使开发者能够轻松地在方法上定义缓存行为 Spring Cache抽象了缓存的底层实现&#xff0c;允许开发者选择使用不同的缓存提供者&#xff08;如 Ehcache、Redis、Caffeine 等&#xff09;。通过配置相应的缓存管理器…...

pycharm中debug,py文件

1、先把需要的实参传入 2、在合适位置打上断点 3、在小三角旁边右键调用调试 4.步进/步出查看 5.选择单步执行&#xff0c;走的更慢...

虚拟化之指令的Trap和仿真

有时,虚拟机监控程序需要在虚拟机(VM)中模拟操作。例如,VM内的软件可能尝试配置与功耗管理或缓存一致性相关的低级处理器控件。通常,您不希望将VM直接访问这些控件,因为它们可能被用于突破隔离,或影响系统中的其他VM。 trap在执行给定操作(例如读取寄存器)时引发异常…...

Python函数默认参数设置

在某些情况下&#xff0c;程序需要在定义函数时为一个或多个形参指定默认值&#xff0c;这样在调用函数时就可以省略为该形参传入参数值&#xff0c;而是直接使用该形参的默认值。 为形参指定默认值的语法格式如下&#xff1a; 形参名 默认值 从上面的语法格式可以看出&…...

js moment计算当前时间到24:00:00的剩余时间

2023.12.7今天我学习了如何计算当前的时间到24:00:00剩下的时间&#xff0c;https://momentjs.cn/ const now moment(); // 获取当前时间const endOfDay moment().endOf(day); // 设置当天的 23:59:59const duration moment.duration(endOfDay.diff(now)); // 计算剩余时间的…...

【UE5】瞬移+马赛克过渡效果

效果 步骤 1. 新建一个工程&#xff0c;创建一个Basic关卡 2. 添加第三人称游戏资源到内容浏览器 3. 新建一个材质&#xff0c;这里命名为“M_Pixel” 打开“M_Pixel”&#xff0c;设置材质域为“后期处理” 在材质图表中添加如下节点 此时效果如下&#xff0c;已经有马赛克的…...

【Skynet 入门实战练习】分布式 ID | 雪花算法 | 缓存设计 | LRU算法 | 数据库

文章目录 前言雪花算法LRU 算法缓存模块数据库测试逻辑 前言 本节实现了 分布式 ID 生成系统&#xff0c;采用雪花算法实现唯一 ID&#xff1b;实现缓存架构&#xff0c;采用 LRU &#xff08;最近最少使用&#xff09;算法。 雪花算法 分布式 ID 生成算法的有很多种&#x…...

ArcGIS Pro中怎么设置标注换行

在ArcGIS Pro中进行文字标注的时候&#xff0c;如果标注的字段内容太长&#xff0c;直接标注的话会不美观&#xff0c;而且还会影响旁边的标注显示&#xff0c;这里为大家介绍一下在ArcGIS Pro中设置文字换行的方法&#xff0c;希望能对你有所帮助。 数据来源 本教程所使用的…...

MAX26——快速人物毛发插片工具 Hair cards tool

一提到毛发插件&#xff0c;我们一般想起的就是maya的 xgrn 或者max的ox。但是这些都是我们做影视级数字人用的。比较费性能也比较费面 下面分享一个干货 Hair cards tool 这个插件操作不像xgen与ox那么复杂。基本上0基础上手5分钟不到。就能插片出不错的效果。比较适用于&…...

一天一个设计模式---原型模式

基本概念 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;其主要目的是通过复制现有对象来创建新对象&#xff0c;而不是通过实例化类。原型模式允许在运行时动态创建对象&#xff0c;同时避免了耦合与子类化。 在原型模式中&#xff0…...

<习题集><LeetCode><链表><2/19/21/23/24>

目录 2. 两数相加 19. 删除链表的倒数第 N 个结点 21. 合并两个有序链表 23. 合并 K 个升序链表 24. 两两交换链表中的节点 2. 两数相加 https://leetcode.cn/problems/add-two-numbers/ public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//head是cur链表头节点…...

C++实现DFS、BFS、Kruskal算法和Prim算法、拓扑排序、Dijkstra算法

背景&#xff1a; 实现要求&#xff1a; 根据图的抽象数据类型的定义&#xff0c;请采用邻接矩阵来存储图1&#xff0c;采用邻接表来存储图2&#xff0c;并完成如下操作&#xff1a;对图1无向图进行深度优先遍历和广度优先遍历。对图1无向图采用Kruskal算法和Prim算法得出最小…...

Spring 依赖注入的三种方式优缺点

小王学习录 前言属性注入1. 属性注入的优点2. 属性注入的缺点 Setter注入Setter注入的优点Setter注入的缺点 构造方法注入1. 构造方法的优点 总结补充Aurowired注解和Resource注解的区别 前言 在前面的文章中介绍了基于注解的方式将Bean存储到Spring中, 接下来介绍如何基于注解…...

代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理 &#xff08;一&#xff09;定义 1、定义 为其他对象提供一种代理以控制对这个对象的访问&#xff1b; 2、涉及到的角色 &#xff08;1&#xff09;抽象主题角色&#xff1a;真实主题和代理主题的共同接口&#xff0c;便于在使用真实主题的地方都可以使用代理…...

设计模式基础——工厂模式剖析(2/2)

目录 一、工厂模式 1.1 工厂模式的定义 1.2 工厂模式的设计意图 1.3 工厂模式主要解决的问题 1.4 工厂模式的缺点 1.5 实际的应用案例 1. 数据库连接池 2. 图形用户界面&#xff08;GUI&#xff09;组件 3. 文件操作 二、各种工厂模式的变形 1.1 简单工厂模式&#…...

spark3.x 读取hudi报错

报错信息如下: Exception in thread "main" org.apache.hudi.exception.HoodieUpsertException: Failed to upsert for commit time 20231201203145254 at org.apache.hudi.table.action.commit.BaseWriteHelper.write(BaseWriteHelper.java:64) at org.apa…...

微信小程序中block和View组件的使用区别

block和View组件都是用于布局的组件: 1. Block组件&#xff1a; Block组件是一个无实际显示效果的组件&#xff0c;它主要用于包裹一组组件&#xff0c;并提供了类似于div的作用。使用Block组件可以将一组组件进行分组&#xff0c;便于样式的管理和控制。Block组件不会在页面…...

代码混淆技术探究与工具选择

代码混淆技术探究与工具选择 引言 在软件开发中&#xff0c;保护程序代码的安全性是至关重要的一环。代码混淆&#xff08;Obfuscated code&#xff09;作为一种常见的保护手段&#xff0c;通过将代码转换成难以理解的形式来提升应用被逆向破解的难度。本文将介绍代码混淆的概…...

selenium 解决 id定位、class定位中,属性值带空格的解决办法

一、前置说明 selenium遇到下面这种元素&#xff1a; <th id"demo id" class"value1 value2 value3 ">1、虽然id一般不会有空格&#xff0c;但是前端错误的这种写法(如下图)&#xff0c;会造成使用id定位不到元素&#xff0c;如&#xff1a; find…...

gma 空间绘图实战(1):绘制多个子图,连接并展示局部放大区域

安装 gma&#xff1a;pip install gma 本文基于&#xff1a;gma 2.0.3&#xff0c;Python 3.10 本文用到的矢量数据为&#xff1a;CTAmap 1.12。来源于 https://www.shengshixian.com/ 。&#xff08;感谢锐多宝&#xff09; 绘图目标 参考代码 import matplotlib.pyplot as p…...

Unity中C#使用协程控制Shader材质变化

文章目录 前言一、协程是什么二、在Unity中使用协程1、我们在 Start 中测试一下协程的执行顺序2、我们实现一个点击按钮实现角色受击效果 三、协程中的动画过渡1、首先&#xff0c;在协程内实现中毒并且消散的效果2、在 OnGUI 内&#xff0c;给一个新按钮使用刚刚定义的协程 四…...

WordPress禁止显示指定类别的文章

使用wordpress禁止输出指定类别的文章可以给get_posts()函数传个数组参数&#xff0c;如下&#xff1a; <div class"widget" id"diary1"> <h3>随机呈现</h3> <ul> <?php $argsarray( numberposts>16, category>-9,-12, …...

C#里面的泛型(T),泛型类,泛型方法,泛型接口等简单解释

https://blog.csdn.net/dap769815768/article/details/81946506 只是比较简单的解释&#xff0c;在实际使用中&#xff0c;如果遇到需要深入研究的场景&#xff0c;再翻阅相关资料深入研究下。 一、泛型T 这个T在实际使用中很常见&#xff0c;比如List<T>。其实我们还…...

C语言——指针(五)

&#x1f4dd;前言&#xff1a; 上篇文章C语言——指针&#xff08;四&#xff09;更加深入的介绍了不同类型指针的特点&#xff0c;这篇文章主要想记录一下函数与指针的结合运用以及const和assert关于指针的用法&#xff1a; 1&#xff0c;函数与指针 2&#xff0c;const 3&am…...

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑气电联合需求响应的气电综合能源配网系统协调优化运行》

这个标题涉及到一个涉及气体&#xff08;天然气&#xff09;和电力的综合能源配网系统&#xff0c;并且强调了考虑气电联合需求响应的协调优化运行。让我们逐步解读&#xff1a; 气电综合能源配网系统&#xff1a; 这指的是一个结合了气体&#xff08;通常是天然气&#xff09;…...

PostgreSQL 主键和唯一键的区别

主键和唯一键的区别 主键&#xff08;Primary Key&#xff09;&#xff1a; 主键是用于唯一标识表中的每一条记录的键。主键必须是唯一的&#xff0c;不允许为空。一个表只能有一个主键。主键可以由一个或多个字段组成。主键的值在整个表中必须是唯一的&#xff0c;用于确保数据…...

删除表格中的所有绘图

Ctrl G 调出定位的对话框再点击定位条件 按Delete键&#xff0c;删除...

Linux卸载Nginx

1、停止Nginx软件 #/usr/local/nginx/sbin/nginx-sstop 或者kill进程 #ps -ef|grep nginx #kill -9 PID 2、查找根下所有名子包含nginx的文件 #sudofind/-namenginx* 3、执行命令删掉nignx安装的相关文件 # rm -rf /usr/local/sbin/nginx # rm -rf /usr/local/nginx # r…...

Qt之QGraphicsView —— 笔记1:绘制简单图元(附完整源码)

效果 相关类介绍 QGraphicsView类提供了一个小部件,用于显示QGraphicsScene的内容。QGraphicsView在可滚动视口中可视化。QGraphicsView将滚动其视口,以确保该点在视图中居中。 QGraphicsScene类 提供了一个用于管理大量二维图形项的场景。请注意,QGraphicsScene没有自己的视…...

SpringIoC原理

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…...

如何对售后服务的全流程进行精细化的管理?

——“如何对售后服务的全流程进行精细化的管理&#xff1f;” ——“售后又是一个十分复杂的过程&#xff0c;仅靠手工或者电子表格记录这些内容&#xff0c;肯定是低效率、易出错的。最好的办法是借助合适的管理工具进行精细化的过程管理。” 假设你购买了一台新的家用电器…...

SAP UI5 walkthrough step2 Bootstrap

我的理解&#xff0c;这就是一个引导指令 1.我们右键打开命令行--执行 ui5 use OpenUI5 2.执行命令&#xff1a;ui5 add sap.ui.core sap.m themelib_sap_horizon 执行完之后&#xff0c;会更新 yaml 文件 3.修改index.html <!DOCTYPE html> <html> <head&…...

Gemini:定义下一代人工智能的里程碑

Google最近发布号称世界最强的大模型"Gemini"&#xff0c;其强大多模态LLM&#xff0c;标志着AI技术的一个新时代。 Gemini作为"迄今为止最强大的AI模型"之一&#xff0c;其独特之处在于它融合了多种模式的处理能力&#xff0c;能够同时理解和生成文本、代…...

一些系统日常运维命令和语句

一、前言 记录一些日常系统运维的命令和语句 二、linux命令与语句 1、linux查看各目录使用磁盘情况 du -h /home home为目录 du -h /home 2.查看内存使用情况 free -h 3、查看进程和CPU使用情况 top top 三、数据库语句 1、统计mysql数据库表数量 SELECT COUNT(*) A…...

微信小程序uni.chooseImage()无效解决方案

Bug场景&#xff1a; 微信小程序在上传图片时可以通过 uni.chooseImage()方案进行上传&#xff0c;这里不再赘述具体参数。一直项目都可以正常使用&#xff0c;突然有一天发现无法使用该方法&#xff0c;于是查了一下&#xff0c;发现是用户隐私协议问题。故记录一下解决方案。…...

Rust深入浅出:编程的深邃大海中的奇妙冒险

第一章&#xff1a;前言 欢迎来到Rust的深邃大海&#xff0c;这里是一片充满挑战和奇妙冒险的领域。在这篇文章中&#xff0c;我们将深入浅出&#xff0c;探索Rust编程语言的深层次特性&#xff0c;并通过诙谐而深刻的方式&#xff0c;带你走进这个奇妙的编程世界。 第二章&a…...

go-zero开发入门-API网关开发示例

开发一个 API 网关&#xff0c;代理 https://blog.csdn.net/Aquester/article/details/134856271 中的 RPC 服务。 网关完整源代码 // file: main.go package mainimport ("flag""fmt""github.com/zeromicro/go-zero/core/conf""github.c…...

TCP一对一通信

package 二十一章; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;/*** Socket服务端**/ public class SocketServer {public static void main(String[] args) {ServerSocket ss null;Socket s null;try {// 创建监听…...

laravel DB::connection 报错 Database connection [{$name}] not configured

DB::connection(mysql2);//不是连接数据库名...

快捷支付是什么?快捷支付好申请吗?

快捷支付是指用户在购买商品时&#xff0c;不需要打开网上银行&#xff0c;只需提供银行卡号码、户名、手机号码等信息&#xff0c;银行验证手机号码的正确性&#xff0c;输入动态密码即可完成支付&#xff0c;无需打开网上银行。持卡人将银行卡绑定到第三方支付应用程序&#…...

如何在Spring Boot中集成RabbitMQ

如何在Spring Boot中集成RabbitMQ 在现代微服务架构中&#xff0c;消息队列&#xff08;如RabbitMQ&#xff09;扮演了关键的角色&#xff0c;它不仅能够提供高效的消息传递机制&#xff0c;还能解耦服务间的通信。本文将介绍如何在Spring Boot项目中集成RabbitMQ&#xff0c;…...

【Spring Boot 源码学习】ApplicationContextInitializer 详解

Spring Boot 源码学习系列 ApplicationContextInitializer 详解 引言往期内容主要内容1. 初识 ApplicationContextInitializer2. 加载 ApplicationContextInitializer3. ApplicationContextInitializer 的初始化 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们…...

软考2018下午第六题改编逻辑(状态模式)

在状态模式中&#xff0c;我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象 package org.example.状态模式.软考航空;/*** author lst* date 2023年12月07日 15:37*/ class FrequentFlyer {CState state;double flyMiles;public FrequentFlyer() {…...

基于深度学习的典型目标跟踪算法

目标跟踪是计算机视觉领域中一个重要的任务&#xff0c;它涉及在视频序列中持续地定位和追踪目标对象。以下是一些常见的深度学习目标跟踪算法&#xff1a; Siamese Network: Siamese网络是一种孪生网络结构&#xff0c;它通过将目标图像与周围环境进行对比&#xff0c;学习目…...