个人的笔记整理

MQ怎么处理多余请求任务?

监控 分区消费者不合理 宕机导致消息堆积 消息分布到指定的key

1.使用JMX监控消息堆积数量 从两个维度 lag和lead

Lag就是消息堆积数量

一旦出现 Lag 逐步增加的趋势,一定要定位问题,及时处理,避免造成业务损失。但是这种增长并不能很好提现问题的严重性,所以还有一个lead值

这里的 Lead 值是指消费者最新消费消息的位移与分区当前第一条消息位移的差值。差值越来越小也就意味着消费者的位移快要低于分区位移马上要数据丢失了。

1.如果消费任务宕机时间过长导致积压数据量很大

创建新的topic并配置更多数量的分区,将积压消息的topic消费者逻辑改为直接把消息打入新的topic,将消费逻辑写在新的topic的消费者中。

2.解决方案 key合理设置 分区合理设置 消费者合理设置

2.Kafka分区数设置的不合理或消费者"消费能力"不足的优化

  1. Kafka分区数是Kafka并行度调优的最小单元,如果Kafka分区数设置的太少,会影响Kafka Consumer消费的吞吐量。

    如果数据量很大,Kafka消费能力不足,则可以考虑增加Topic的Partition的个数,同时提升消费者组的消费者数量。

  2. 如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。

4.Kafka消息key设置的优化

使用Kafka Producer消息时,可以为消息指定key,但是要求key要均匀,否则会出现Kafka分区间数据不均衡。

所以根据业务,合理修改Producer处的key设置规则,解决数据倾斜问题。

KAFKA和其他MQ区别?

其他MQ世界范围内社区活跃度一般,目前业界主要rabbit和kafka两种比较多

为大数据而生的消息中间件,

如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的

实时性和可靠性低一些,最大的优点吞吐量高

RabbitMQ,

实时性和可靠性要求比较高的数据,吞吐量低一些

可靠性要求比较高的消息传递上,

erlang 的话如果需要做二次定制开发会比较难办

kafka架构:

对于传统的MQ而言,已经被消费的消息会从队列中删除,但在Kafka中被消费的消息也不会立马删除,在kafka的server.propertise配置文件中定义了数据的保存时间,当文件到设定的保存时间时才会删除,

  • 主题:Topic。主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务。同一个topic的数据既可以在同一个broker上也可以在不同的broker结点上

  • 分区:Partition。一个有序不变的消息序列。每个主题下可以有多个分区。每个分区在物理上对应一个文件夹,该文件夹里面存储了这个分区的所有消息和索引文件还有过期时间索引文件。在创建topic时可指定parition数量,生产者将消息发送到topic时,消息会根据 分区策略 追加到分区文件的末尾,属于顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)。其实分区的作用就是提供负载均衡的能力,提高吞吐量

  • 消息位移:Offset。表示分区中每条消息的位置信息,是一个单调递增且不变的值。

  • 副本:Replica。Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用。

  • leader

    每个partition有多个副本,其中有且仅有一个作为leader,leader会负责所有的客户端读写操作。

    follower

    follower不对外提供服务,只与leader保持数据同步,如果leader失效,则选举一个follower来充当新的leader。当follower挂掉、卡住或者同步太慢,leader会把这个follower从ISR列表中删除,重新创建一个follower。

  • 生产者,生产者发送消息到指定的topic下,消息再根据分配规则append到某个partition的末尾

  • 消费者:消费者从topic中消费数据。

  • 消费者位移:Consumer Offset。表征消费者消费进度,每个消费者都有自己的消费者位移。

  • 消费者组:Consumer Group。多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。

  • 一个分区只能被一个消费者组的消费者,也就是说消费者组其他消费者没办法消费这个分区,但多个consumer group可同时消费这一消息。这也是kafka用来实现一个topic消息的广播和单播的手段,如果需要实现广播,一个consumer group内只放一个消费者即可,要实现单播,将所有的消费者放到同一个consumer group即可。

  • 重平衡:Rebalance。

    一个topic分为多个分区,一个consumer group里面的所有消费者合作,一起去消费所订阅的某个topic下的所有分区(每个消费者消费一个分区),kafka会将该topic下的所有分区均匀的分配给consumer group下的每个消费者

  • 消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。但是会导致暂停所以要尽量避免

  • 三种情况触发重平衡:消费者数量改变 分区改变 topic改变(比如建立了一个新topic,但是首尾字母和原topic一致)

  • topic和重平衡关系?

**AR:**Assigned Replicas,某分区的所有副本(这里所说的副本包括leader和follower)统称为 AR。

  • ISR:In-Sync Replicas 副本同步队列
  • AR:Assigned Replicas 所有副本

**ISR:**In Sync Replicas,所有与leader副本保持"一定程度同步"的副本(包括leader副本在内)组成 ISR 。一定程度同步"就是指可忍受的滞后范围,这个范围可以通过server.properties中的参数进行配置。

**OSR :**Out-of-Sync Replied,在上面的描述中,相对leader滞后过多的follower将组成OSR 。

由此可见,AR = ISR + OSR,理想情况下,所有的follower副本都应该与leader 保持一定程度的同步,即AR=ISR,OSR集合为空

ISR 的伸缩性

leader负责跟踪维护 ISR 集合中所有follower副本的滞后状态,当follower副本"落后太多" 或 "follower超过一定时间没有向leader发送同步请求"时,leader副本会把它从 ISR 集合中剔除。如果 OSR 集合中有follower副本"追上(就是leo追上了leader的hw)"了leader副本,那么leader副本会把它从 OSR 集合转移至 ISR 集合。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVkRZ1uz-1665944750662)(http://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/Kafka%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E4%B8%8E%E5%AE%9E%E6%88%98/assets/06dbe05a9ed4e5bcc191bbdb985352df.png)]

topic partition replication是一个什么样的比例关系,replication数量和consumer数量一般怎么去设置?

Partition:分区,为了实现扩展性,一个非常大的Topic可以分布到多个Broker上,一个Topic可以分为多个Partition,每个Partition是一个有序的队列(分区有序,不能保证全局有序)
Replica:副本Replication,为保证集群中某个节点发生故障,节点上的Partition数据不丢失,Kafka可以正常的工作,Kafka提供了副本机制,一个Topic的每个分区有若干个副本,一个Leader和多个Follower

消息队列 分区partition 副本replication 消费者

consumer保证每个分区都能被消费,所以最好和数量相等

一个partition保证有多个副本,kafka采用数据冗余方式来保证数据不会丢失,至少两个以上,leader和follwer

主题会分到一个或者多个broker

  • 第一层是主题层,每个主题可以配置 M 个分区,M个分区会均匀分布到各个broker上,而每个分区又可以配置 N 个副本。
  • 怎么计算分区在哪个broker上?
  • 取模均匀分配
  • 怎么计算副本在哪个broker节点上?
  • (第i个分区+第i个分区的第j个副本)取模broker节点 最后算出来就是在哪个broker上
  • 第二层是分区层,每个分区的 N 个副本中只能有一个充当领导者角色,对外提供服务;其他 N-1 个副本是追随者副本,只是提供数据冗余之用。
  • 第三层是消息层,分区中包含若干条消息,每条消息的位移从 0 开始,依次递增。

(Consumer Offset)消费者位移:每个消费者在消费消息的过程中必然需要有个字段记录它当前消费到了分区的哪个位置上,这个字段就是消费者位移

位移:分区内的消息位置,它是不变的,即一旦消息被成功写入到一个分区上,它的位移值就是固定的了。而消费者位移则不同,它可能是随时变化的因为它可能会重平衡去消费别的分区,另外每个消费者有着自己的消费者位移

partition副本的分配策略

每个topic有多个partition,每个partition有多个副本,这些partition副本分布在不同的broker上,以保障高可用,那么这些partition副本是怎么均匀的分布到集群中的每个broker上的呢?

※ kafka分配partition副本的算法如下,

① 将所有的broker(假设总共n个broker)和 待分配的partition排序;

② 将第i个partition分配到第(i mod n)个broker上;

③ 第i个partition的第j个副本分配到第((i+j) mod n)个broker上;

kafka的消息传递备份策略

生产者将消息发送给分区的leader,leader会将该消息写入其本地log,然后每个follower都会从leader pull数据,follower pull到该消息并将其写入log后,会向leader发送ack,当leader收到了ISR集合中所有follower的ack后,就认为这条消息已经commit了,leader将增加HW并且向生产者返回ack。在整个流程中,follower也可以批量的从leader复制数据,以提升复制性能。

producer在发送消息的时候,可指定参数acks,表示"在生产者认为发送请求完成之前,有多少分区副本必须接收到数据",有三个可选值,0、1、all(或-1),默认为1,

  • acks=0,表示producer只管发,只要发出去就认为发发送请求完成了,不管leader有没有收到,更不管follower有没有备份完成。数据可靠性最低
  • acks=1,表示只要leader收到消息,并将其写入自己log后,就会返回给producer ack,不考虑follower有没有备份完成,如果此时leader宕机,follwer没有备份完成就会丢失数据。
  • acks=all(或-1),表示不仅要leader收到消息写入本地log,还要等所有ISR集合中的follower都备份完成后,producer才认为发送成功。
  • 但是如果在follower同步完成后,leader准备发送ack,如果此时leader发生故障,会造成数据重复。(这里的数据重复是因为没有收到,所以生产者继续重发导致的数据重复)
  • 实际上,为了提高性能,follower在pull到消息将其保存到内存中而尚未写入磁盘时,就会向leader发送ack,所以也就不能完全保证异常发生后该条消息一定能被Consumer消费。

kafka的应用场景*

  • 日志收集:一个公司可以用Kafka收集各种服务的log,通过kafka以统一接口开放给各种消费端,例如hadoop、Hbase、Solr等。

  • 消息系统:解耦生产者和消费者、缓存消息等。

  • 运营指标:Kafka也经常用来记录运营监控数据。

kafka中的Leader选举

Kafka中zookeeper的作用

Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线、所有topic的分区副本分配和leader的选举等工作。Controller的工作管理是依赖于zookeeper的。

controller的选举

kafka集群中多个broker,有一个会被选举为controller

controller的选举是通过broker在zookeeper的节点下创建临时节点来实现的,利用zookeeper的强一致性特性,一个节点只能被一个客户端创建成功,创建成功的broker即为controller,即"先到先得"。

当controller宕机或者和zookeeper失去连接时,zookeeper检测不到心跳,zookeeper上的临时节点会被删除,而其它broker会监听临时节点的变化,当节点被删除时,其它broker会收到通知,重新发起controller选举。

leader的选举【分区副本的leader】

分区leader的选举由 controller 负责管理和实施,当leader发生故障时,controller通知leader对应的broker节点,然后broker节点会从ISR中的follwer中选一个新leader。

选举过程:在AR集合中找到第一个存活的属于ISR集合的follwer作为新的leader。AR集合在创建的时候顺序就已经被指定,顺序不会改变,ISR集合可能会因为follwer滞后的原因而发生改变,所以按照AR集合顺序找。

假设分区partition所有副本都不可用:

1、选择 ISR中 第一个活过来的副本作为Leader;

2、选择第一个任意活过来的副本作为Leader;

,如果一定要等待ISR中的副本活过来,那不可用的时间可能会相对较长。选择第一个活过来的副本作为Leader,如果这个副本不在ISR中,那数据的一致性则难以保证。kafka支持用户通过配置选择,以根据业务场景在可用性和数据一致性之间做出权衡。

消费组leader的选举

第一个加入consumer group的consumer即为leader,如果某一时刻leader消费者退出了消费组,那么会重新 随机 选举一个新的leader。

Kafka 的设计是什么样的?

文件存储

还有一个time index按照时间顺序来清理segment,通过index找到加速找到log文件的真实地址

Kafka 将消息以 topic 为单位进行归纳

将向 Kafka topic 发布消息的程序称为 producers.

将预订 topics 并消费消息的程序成为 consumer.

Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker.

producers 通过网络将消息发送到 Kafka 集群,集群向消费者提供消息

为了实现生产者的幂等性,Kafka引入了 Producer ID(PID)和 Sequence Number的概念。

l PID:每个Producer在初始化时,都会分配一个唯一的PID,这个PID对用户来说,是透明的。

l Sequence Number:针对每个生产者(对应PID)发送到指定主题分区的消息都对应一个从0开始递增的Sequence Number。

但是它只能保证单分区上的幂等性,不能实现跨会话的幂等性。

kafka可以保证生产消息幂等,通过map记录生产者最新消息id,结构为<生产者ID,MessageId> ,如果后续消息id不大于map中的消息id则丢弃。
消费幂等性也是同理,生产消息的时候生成一个全局id,消息被消费后保存全局id到数据库。每次消费的时候先判断全局id存不存在保证幂等性。至于机器执行过程宕机你可以采用事务保证一致性。
也可以业务层面保证幂等,生产消息的时候查询数据版本号,消费消息的时候带上版本号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQI9gK9y-1665944750663)(image-20220115035631817.png)]

如果我想实现多分区以及多会话上的消息无重复,应该怎么做呢?答案就是事务(transaction)或者依赖事务型 Producer。

为了按跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将Producer获得的PID(可以理解为Producer ID)和Transaction ID进行绑定,这样当Producer重启之后就可以通过正在进行的Transaction ID获得原来的PID。

但是,事务型 Producer 的性能要更差

kafka事务隔离级别

各大主流数据库厂商都比较统一。所谓的 read committed,指的是当读取数据库时,你只能看到已提交的数据,即无脏读。同时,当写入数据库时,你也只能覆盖掉已提交的数据,即无脏写。

mysql为什么选择可重复读不选择读已提交?

1.1 生产者分区写入策略

生产者写入消息到topic,Kafka将依据不同的策略将数据分配到不同的分区中

\1. 轮询分区策略

\2. 随机分区策略

\3. 按key分区分配策略

消费者组Rebalance机制

Kafka中的Rebalance称之为再均衡,是Kafka中确保Consumer group下所有的consumer如何达成一致,分配订阅的topic的每个分区的机制。

Rebalance触发的时机有:

\1. 消费者组中consumer的个数发生变化。例如:有新的consumer加入到消费者组,或者是某个consumer停止了。

这是重点关注的: 原因有垃圾回收fullgc导致的心跳没法检测触发重平衡 还有注意真正发送重平衡分配策略 危害:orederingkey stw

\1. 订阅的topic个数发生变化

消费者可以订阅多个主题,假设当前的消费者组订阅了三个主题,但有一个主题突然被删除了,此时也需要发生再均衡。kafka识别主题的方式是根据首位字母来识别的

\1. 订阅的topic分区数发生变化

l 发生Rebalance时,consumer group下的所有consumer都会协调在一起共同参与,Kafka使用分配策略尽可能达到最公平的分配

l Rebalance过程会对consumer group产生非常严重的影响,Rebalance的过程中所有的消费者都将停止工作,直到Rebalance完成

Range范围分配策略是Kafka默认的分配策略,它可以确保每个消费者消费的分区数量是均衡的。

注意:Range范围分配策略是针对每个Topic的。

范围分配就是消费者对分区进行取模,然后留下的分区余数由最后一个消费者负责

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WYt6hGVn-1665944750664)(image-20220115040036062.png)]

这种方式的弊端在于如果分区和消费者不能平均分配的话就会造成前几个消费者多分配分区,导致资源负载不均衡。

按照字典序排序(topic和分区的hashcode进行排序),然后通过轮询方式逐个将分区以此分配给每个消费者。

假如有三个消费者第一个取第一个第二个取第二个第三个取第三个然后第一个又去获取第四个这样轮询,这样还是会导致分配不太均衡

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zc4ioa4p-1665944750664)(image-20220115040126794.png)]

1.1.1 Stricky粘性分配策略

从Kafka 0.11.x开始,引入此类分配策略。主要目的:

\1. 分区分配尽可能均匀

\2. 在发生rebalance的时候,分区的分配尽可能与上一次分配保持相同

没有发生rebalance时,Striky粘性分配策略和RoundRobin分配策略类似。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bY49JFPZ-1665944750664)(image-20220115040348659.png)]

通过上图,我们发现,consumer0和consumer1原来消费的分区大多发生了改变。接下来我们再来看下粘性分配策略。

。所谓的有粘性,是指每次 Rebalance 时,该策略会尽可能地保留之前的分配方案,尽量实现分区分配的最小变动。

消费者组Rebalance缺点:在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 完成。类似垃圾回收的stw

如何避免Rebalance:

网络问题 负载均衡 fullgc

如果某个 Consumer 实例不能及时地发送这些心跳请求,Coordinator 就会认为该 Consumer 已经“死”了,从而将其从 Group 中移除,然后开启新一轮 Rebalance

  • 如果 Consumer Group 下的 Consumer 实例数量发生变化,就一定会引发 Rebalance。这是 Rebalance 发生的最常见的原因。
  • 第一类非必要 Rebalance 是因为未能及时发送心跳,导致 Consumer 被“踢出”Group 而引发的。因此,你需要仔细地设置session.timeout.ms 和 heartbeat.interval.ms的值

第二类非必要 Rebalance 是 Consumer 消费时间过长导致的

第三Consumer 端的 GC 表现,比如是否出现了频繁的 Full GC 导致的长时间停顿,从而引发了 Rebalance

Kafka的高可用

1.分区 2.senfile零拷贝 pagecache 3.TCP多路复用 Pull拉取

1、分区

将一个topic拆分多个partition,每个partition都是一个物理文件,物理文件对应了所有日志消息,在这些物理磁盘文件上进行顺序读写

设置了索引快速来找到这些物理文件。

并发将一个topic拆分多个partition, kafka读写的单位是partition,因此,将一个topic拆分为多个partition可以提高吞吐量。但是,这里有个前提,就是不同partition需要位于不同的磁盘(可以在同一个机器)。如果多个partition位于同一个磁盘,那么意味着有多个进程同时对一个磁盘的多个文件进行读写,使得操作系统会对磁盘读写进行频繁调度,也就是破坏了磁盘读写的连续性。

Partition是物理上的概念,每个Partition对应着一个log文件,该log文件中存储的就是producer生产的数据,

1.采用追加写分区下的log,故避免了缓慢的随机 I/O 操作,改为性能较好的顺序 I/O 写操作

2.如果你不停地向一个日志写入消息,最终也会耗尽所有的磁盘空间,因此 Kafka 必然要定期地删除消息以回收磁盘

通过日志段(Log Segment)机制。在 Kafka 底层,一个日志又近一步细分成多个日志段。

消息被追加写到当前最新的日志段中,当写满了一个日志段后,Kafka 会自动切分出一个新的日志段,并将老的日志段封存起来。Kafka 在后台还有定时任务会定期地检查老的日志段是否能够被删除,从而实现回收磁盘空间的目的。

1、顺序读写磁盘

磁盘分为顺序读写与随机读写,内存一样也分为顺序读写与随机读写。基于磁盘的随机读写确实很慢,但基于磁盘的顺序读写性能却很高

利用了磁盘连续读写性能远远高于随机读写的特点。

Kafka Broker 是如何持久化数据的。总的来说,Kafka 使用消息日志(Log)来保存数据,一个日志就是磁盘上一个只能追加写(Append-only)消息的连续的物理文件。因为只能追加写入,故避免了缓慢的随机 I/O 操作,改为性能较好的顺序 I/O 写操作,这也是实现 Kafka 高吞吐量特性的一个重要手段。不过如果你不停地向一个日志写入消息,最终也会耗尽所有的磁盘空间,因此 Kafka 必然要定期地删除消息以回收磁盘。怎么删除呢?简单来说就是通过日志段(Log Segment)机制。在 Kafka 底层,一个日志又近一步细分成多个日志段,消息被追加写到当前最新的日志段中,当写满了一个日志段后,Kafka 会自动切分出一个新的日志段,并将老的日志段封存起来。Kafka 在后台还有定时任务会定期地检查老的日志段是否能够被删除,从而实现回收磁盘空间的目的。

寻找log时候使用了稀疏索引

Kafka高效文件存储设计特点

  • Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
  • 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。
2、page cache

为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。这样做是因为,

> JVM中一切皆对象,对象的存储会带来额外的内存消耗;

> 使用JVM会受到GC的影响,随着数据的增多,垃圾回收也会变得复杂与缓慢,降低吞吐量;

另外操作系统本身对page cache做了大量优化,通过操作系统的Page Cache,Kafka的读写操作基本上是基于系统内存的,读写性能也得到了极大的提升。

3.零拷贝

图片

整个过程如上图所示,这个过程包含4次copy操作和2次系统上下文切换,而上下文切换是CPU密集型的工作,数据拷贝是I/O密集型的工作,性能其实非常低效。零拷贝就是使用了一个名为sendfile()的系统调用方法,将数据从page cache直接发送到Socket缓冲区,避免了系统上下文的切换,消除了从内核空间到用户空间的来回复制。从上图可以看出,"零拷贝"并不是说整个过程完全不发生拷贝,而是站在内核的角度来说的,避免了内核空间到用户空间的来回拷贝。

6.采用pull主动拉取

7.网络通信基于TCP数据流,实现了多路复用

Kafka 消息是采用 Pull 模式,还是 Push 模式?

生产者使用push模式将消息发布到Broker,消费者使用pull模式从Broker订阅消息。

push模式很难适应消费速率不同的消费者,如果push的速度太快,容易造成消费者拒绝服务或网络拥塞;如果push的速度太慢,容易造成消费者性能浪费。

pull可以让消费者按照自己的速率来拉取,拥有更高的吞吐量

采用pull的方式也有一个缺点,就是当Broker没有消息时,消费者会陷入不断地轮询中,为了避免这点,kafka有个参数可以让消费者阻塞知道是否有新消息到达。

Kafka如何保证消息不丢失

1.消费者异常导致的消息丢失

手动提交位移

如果是多线程异步处理消费消息,Consumer 程序不要开启自动提交位移,而是要应用程序手动提交位移

消费者可能导致数据丢失的情况是:消费者获取到了这条消息后,还未处理,Kafka 就自动提交了 offset,这时如果消费者宕机,那这条消息就丢失了。

但是手动提交位移需要注意重复消费问题,比如消费者刚处理完,还没手动提交位移,这时自己宕机了,此时这条消息肯定会被重复消费一次,这就需要消费者根据实际情况保证幂等性。

2.生产者数据传输导致的消息丢失

1.acks的选择 2.回调API 3.重试次数

对于生产者数据传输导致的数据丢失主常见情况是生产者发送消息给 Kafka,由于网络等原因导致消息丢失,对于这种情况也是通过在 producer 端设置 acks=all 来处理,这个参数是要求 leader 接收到消息后,需要等到所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试。但是这样会造成延迟较高,所以实际生产环境要权衡一下延迟和数据一致性如何抉择。还有两种

当ack=0时,producer不等待broker的ack,不管数据有没有写入成功,都不再重复发该数据
当ack=1时,broker会等到leader写完数据后,就会向producer发送ack,但不会等follower同步数据,如果这时leader挂掉,producer会对新的leader发送新的数据,在old的leader中不同步的数据就会丢失

Producer 永远要使用带有回调通知的发送 API,一旦出现消息提交失败的情况,你就可以有针对性地进行处理。

把重试次数调成max,在 producer 端设置 retries=MAX(很大很大很大的一个值,无限次重试的意思):这个参数的含义是一旦写入失败,就无限重试,卡在这里了。

3.Kafka 导致的消息丢失

1.分区副本数量设置 ,对follwer的感知 2.禁止borker副本落后太多 3.心跳检测速率,进入ISR中的follower和leader之间的速率不会相差10秒

1.设置参审每个分区至少要求两个副本 ,leader要至少能感知到一个follwer和自己联系,保证挂了还有备份,最好设置Broker参数,将消息多保存几份,kafka目前防止消息丢失的主要机制就是冗余。

2。把落后太多的broker副本设置为禁止成为新的leader

Kafka 导致的数据丢失一个常见的场景就是 Kafka 某个 broker 宕机,,而这个节点正好是某个 partitionleader 节点,这时需要重新选举该 partitionleader。如果该 partitionleader 在宕机时刚好还有些数据没有同步到 follower,此时 leader 挂了,在选举某个 followerleader 之后,就会丢失一部分数据。

对于这个问题,Kafka 可以设置如下 4 个参数,来尽量避免消息丢失:

  • topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本;
  • Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个参数的含义是一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 节点。

At Least Once ack = all

  • 最多一次(at most once):消息可能会丢失,但绝不会被重复发送。

  • 至少一次(at least once):消息不会丢失,但有可能被重复发送。

  • 精确一次(exactly once):消息不会丢失,也不会被重复发送。

  • 幂等:partition内部的exactly-once顺序语义
  • 当ack=0时,producer不等待broker的ack,不管数据有没有写入成功,都不再重复发该数据
    当ack=1时,broker会等到leader写完数据后,就会向producer发送ack,但不会等follower同步数据,如果这时leader挂掉,producer会对新的leader发送新的数据,在old的leader中不同步的数据就会丢失

  • acks=all 来处理,这个参数是要求 leader 接收到消息后,需要等到所有的 follower 都同步到了消息之后,才认为本次写成功了

0.11版本的kafka,引入了一项重大特性:幂等性,开启幂等性的Producer在初始化的时候会被分配一个PID,发往同一个Partition的消息会附带Sequence Number,而Broker端会对<PID,Partition,SeqNumber>做缓存,当具有相同主键的消息的时候,Broker只会持久化一条。

kafka为什么要用tcp?

多路复用

是指将两个或多个数据流合并到底层单一物理连接中的过程。TCP 的多路复用请求会在一条物理连接上创建若干个虚拟连接,每个虚拟连接负责流转各自对应的数据流。其实严格来说,TCP 并不能多路复用,它只是提供可靠的消息交付语义保证,比如自动重传丢失的报文。

已知的 HTTP 库在很多编程语言中都略显简陋。

在创建 KafkaProducer 实例时,生产者应用会在后台创建并启动一个名为 Sender 的线程,该 Sender 线程开始运行时首先会创建与 Broker 的连接

kafka的tcp连接

我在这里稍微解释一下 bootstrap.servers 参数。它是 Producer 的核心参数之一,指定了这个 Producer 启动时要连接的 Broker 地址。请注意,这里的“启动时”,代表的是 Producer 启动时会发起与这些 Broker 的连接。因此,如果你为这个参数指定了 1000 个 Broker 连接信息,那么,你的 Producer 启动时会首先创建与这 1000 个 Broker 的 TCP 连接。

TCP 连接还可能在两个地方被创建:一个是在更新元数据后,另一个是在消息发送时。为什么说是可能?因为这两个地方并非总是创建 TCP 连接。当 Producer 更新了集群的元数据信息之后,如果发现与某些 Broker 当前没有连接,那么它就会创建一个 TCP 连接。同样地,当要发送消息时,Producer 发现尚不存在与目标 Broker 的连接,也会创建一个。

Kafka 如何保证消息的顺序性

首先考虑数据不丢失 然后考虑分区上的orderingkey 然后考虑消费者重平衡问题

在某些业务场景下,我们需要保证对于有逻辑关联的多条MQ消息被按顺序处理,比如对于某一条数据,正常处理顺序是新增-更新-删除,最终结果是数据被删除;如果消息没有按序消费,处理顺序可能是删除-新增-更新,最终数据没有被删掉,可能会产生一些逻辑错误。对于如何保证消息的顺序性,主要需要考虑如下两点:

  • 如何保证消息在 Kafka 中顺序性;
  • 如何保证消费者处理消费的顺序性。

ordering-KEY

对于 Kafka,如果我们创建了一个 topic,默认有三个 partition。生产者在写数据的时候,可以指定一个 key,比如在订单 topic 中我们可以指定订单 id 作为 key,那么相同订单 id 的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。消费者从 partition 中取出来数据的时候,也一定是有顺序的。通过制定 key 的方式首先可以保证在 kafka 内部消息是有序的。

Producer 端:

回调api acks

Kafka 的发送端发送消息,如果是默认参数什么都不设置,则消息如果在网络没有抖动的时候,可以一批批的按消息发送的顺序被发送到 Kafka 服务器端。但是,一旦网络波动了,则消息就可能出现失序。首先要考虑同步发送消息。

把ack设置为all(-1),保证发送以后所有follower同步成功,这样就实现了一条一条的发送效果。或者考虑使用带回调函数发送,每次等结果返回以后再发送下一条。同时为了保证幂等性还可以用一个map存入pid和sequence number,每次比对是不是比当前sequence number小。

broker端:

orderin-key

因为Kafka 只保证同个分区内的消息是有序的,所以要想办法让同类的消息进入相同的分区,通常同类型消息用他们的消息类型作为key,然后相同的key类型进入同一个分区,可以让他们进入相同的分区保证有序。

但是,这里有个问题,就是当我们对分区的数量进行改变的时候,会把以前可能分到同样的分区的消息,分到别的分区上。这就不能保证消息顺序了。

面对这种情况,就需要在动态变更分区的时候,考虑对业务的影响。有可能需要根据业务和当前分区需求,重新划分消息类别。

Consumer 端:

避免重平衡

由于 Rebalance 相当于让消费者组重新分配分区,这就可能造成消费者在 Rebalance 前、后所对应的分区不一致。分区不一致,那自然消费顺序就不可能一致了。所以,我们都会尽量不让 Rebalance 发生。

Rebalance三种情况会变化:

消费者变化

topic数量变化

分区数量变化 通常我们关心的是消费者变化

Kafka中的ISR、AR代表什么?ISR的伸缩指什么?

延迟时间replica.lag.time.max.ms延迟条数replica.lag.max.messages两个维度

  • ISR:In-Sync Replicas 副本同步队列
  • AR:Assigned Replicas 所有副本

ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms延迟条数replica.lag.max.messages两个维度,当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR,存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。

AR=ISR+OSR。

为什么使用MQ?MQ的优点

异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
日志处理 - 解决大量日志传输。
消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

消息队列的缺点

1、 系统可用性降低

系统引入的外部依赖越多,越容易挂掉。

2、 系统复杂度提高

加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。

3、 一致性问题

A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,这就数据不一致了。

稀疏文件

有些软件在创建文件的时候,会先给文件提供空间,然后再逐渐把数据写入文件,

你创建了一个60GB的虚拟机磁盘文件,在正常情况下,该文件要占用60GB的物理磁盘空间。

但在大多数情况下虚拟磁盘并未写满,例如下图,虚拟机内的操作系统本身仅占用了9.29GB,还有50.7GB的剩余空间,如果在物理硬盘上创建完整60GB的虚拟磁盘文件未免太浪费了:

我们可以采取一种技巧减少物理磁盘的占用:将60GB的虚拟磁盘分为“真正写入”的部分和“不写入”的部分,前者把已用空间里的数据真正写入物理磁盘,未用空间的数据不写入磁盘但作声明:剩下的数据都是0。

这样一来,虚拟机用户虽然看到了完整的磁盘容量,但只有一部分数据真正写入了物理磁盘。随着写入数据量的增大,虚拟磁盘所占的物理空间也逐渐增大,直至声明的最大空间。所以稀疏文件包含有大量的0,但文件系统并没有真正使用磁盘上的物理块来存储这些0

Kafka中的事务是怎么实现的?

kafka事务有两种

producer事务和consumer事务
producer事务是为了解决kafka跨分区跨会话问题
kafka不能跨分区跨会话的主要问题是每次启动的producer的PID都是系统随机给的,所以不能包装每次都是在同一个分区生产

为了按跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将Producer一开始的PID(可以理解为Producer ID)和Transaction ID进行绑定,并且第一次向broker注册的时候broker就会记录这个TID,这样当Producer重启之后虽然会生成一个新的PID,但是它的TID没变,当向broker发起请求的时候发现TID一致但是PID变了就会用之前那个PID覆盖掉现在的,并获取上一次的事务状态信息,从而继续上次工作

consumer事务则是手动开启位移的提交来保证事务

topic 的分区数可不可以减少?如果可以怎么减少?如果不可以,那又是为什么?

不可以减少,减少了分区之后,首先考虑orderingkey的问题,还要考虑重平衡问题

Kafka 中有那些地方需要选举?这些地方的选举策略又有哪些?

在ISR中需要选举出Leader,选择策略为先到先得。在分区中需要选举,需要选举出Leader和follower。

失效副本是指什么?有那些应对措施?

失效副本为速率比leader相差大于10s的follower,ISR会将这些失效的follower踢出,等速率接近leader的10s内,会重新加入ISR

描述下 Kafka 中的领导者副本(Leader Replica)和追随者副本(Follower Replica)的区别

Kafka副本当前分为领导者副本和追随者副本。只有Leader副本才能对外提供读写服务,响应Clients端的请求。Follower副本只是采用拉(PULL)的方式,被动地同步Leader副本中的数据,并且在Leader副本所在的Broker宕机后,随时准备应聘Leader副本。

加分点:

  • 强调Follower副本也能对外提供读服务。自Kafka 2.4版本开始,社区通过引入新的Broker端参数,允许Follower副本有限度地提供读服务。
  • 强调Leader和Follower的消息序列在实际场景中不一致。通常情况下,很多因素可能造成Leader和Follower之间的不同步,比如程序问题,网络问题,broker问题等,短暂的不同步我们可以关注(秒级别),但长时间的不同步可能就需要深入排查了,因为一旦Leader所在节点异常,可能直接影响可用性。

注意:之前确保一致性的主要手段是高水位机制(HW),但高水位值无法保证Leader连续变更场景下的数据一致性,因此,社区引入了Leader Epoch机制,来修复高水位值的弊端。

高水位

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SSR3mMZ1-1665944750665)(http://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/Kafka%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E4%B8%8E%E5%AE%9E%E6%88%98/assets/c2243d5887f0ca7a20a524914b85a8dd.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o9vjspkI-1665944750665)(http://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/Kafka%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E4%B8%8E%E5%AE%9E%E6%88%98/assets/f30a4651605352db542b76b3512df110.png)]

leader的高水位和follwer中日志末端位移最小的那个水位一致

Follower 副本的高水位更新需要一轮额外的拉取请求才能实现,也就是说第一次拉取更新了日志末端位移还要再拉取一次才能更新HW,如果这种额外的拉取因为高水位和同步的时机不一致很容易出现错误导致数据丢失或者数据不一致,如果扩展到多个副本情况就更加糟糕

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HqSOZuVY-1665944750666)(image-20220126220711389.png)]

follwer的高水位不能超过leader的高水位

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7B25SdV-1665944750666)(http://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/Kafka%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E4%B8%8E%E5%AE%9E%E6%88%98/assets/69f8ccf346b568a7310c69de9863ca42.png)]

总结:生产者发送数据给leader,leader和所有follwer发出确认就算成功了,leader发送数据给副本,然后以为同步成功,其实副本在同步时候宕机了(因为副本还没写入磁盘就会发出ack确认),然后副本恢复以后会截断高水位后面的信息保持高水位和日志末端位移一致重新去leader那里同步,同步的时候leader刚好也宕机了,然后这个副本就会成为leader并且以为它已经同步成功了,然后旧的leader就作为follwer去同步并且把自己的高水位后面的数据截断,然后把高水位调整成和新leader相同的高水位,因为副本的高水位不能超过leader的高水位。

为什么数据截断可以保证数据一致?

因为对follower来说leader也可能宕机,要选一个新的follwer来当leader,这时候并不知道新的follwer它的数据同步到哪个位置

  • 1)Epoch主要存版本号和起始位移
  • Epoch。一个单调增加的版本号。每当副本领导权发生变更时,都会增加该版本号。小版本号的 Leader 被认为是过期 Leader,不能再行使 Leader 权力。
  • 起始位移(Start Offset)。Leader 副本在该 Epoch 值上写入的首条消息的位移。
  • Kafka Broker 会在内存中为每个分区都缓存 Leader Epoch 数据,同时它还会定期地将这些信息持久化到一个 checkpoint 文件中。当 Leader 副本写入消息到磁盘时,Broker 会尝试更新这部分缓存。如果该 Leader 是首次写入消息,那么 Broker 会向缓存中增加一个 Leader Epoch 条目,否则就不做更新。这样,每次有 Leader 变更时,新的 Leader 副本会查询这部分缓存,取出对应的 Leader Epoch 的起始位移,以避免数据丢失和不一致的情况。
  • epoch解决方法:follwer恢复以后去找leader确认,日志末端位移没有变,并且版本号也没有变,就不进行截断操作,leader宕机恢复以后作为follwer发现版本号变了,但是好在新ledaer的日志末端位移没有变。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lRPjhuGQ-1665944750666)(image-20220126224721115.png)]

Leader 副本

处理生产者请求的逻辑如下:

  1. 写入消息到本地磁盘。
  2. 更新分区高水位值

处理 Follower 副本拉取消息的逻辑如下:

  1. 读取磁盘(或页缓存)中的消息数据。
  2. 使用 Follower 副本发送请求中的位移值更新远程副本 日志末端位移 值。
  3. 更新分区高水位值

follower故障和leader故障

follower故障:follower发生故障后会被临时提出ISR,等待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉(保证数据一致性),从HW开始向leader进行同步,等待该follower的LEO大于等于该partition的HW,即follower追上leader之后,就可以重新加入ISR了。

  • leader故障:leader发生故障之后,会从ISR中选出一个新的leader,为了保证多个副本之间的数据的一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader中同步数据。

,leader副本中的HW,决定了消费者能消费的最新消息能到哪个offset。

img

leader的hw

ISR 集合中最小的 LEO 即为分区的 HW

Kafka 相关的监控告警怎么做的

lag lead ping jvm

主机监控

,指的是监控 Kafka 集群 Broker 所在的节点机器的性能。通常来说,一台主机上运行着各种各样的应用进程,这些进程共同使用主机上的所有硬件资源,比如 CPU、内存或磁盘等。

常见的主机监控指标包括但不限于以下几种:

  • 机器负载(Load)
  • CPU 使用率
  • 内存使用率,包括空闲内存(Free Memory)和已使用内存(Used Memory)
  • 磁盘 I/O 使用率,包括读使用率和写使用率
  • 网络 I/O 使用率
  • TCP 连接数
  • 打开文件数
JVM 监控

1.使用JMX监控消息堆积数量 从两个维度 lag和lead

Lag就是消息堆积数量

一旦出现 Lag 逐步增加的趋势,一定要定位问题,及时处理,避免造成业务损失。但是这种增长并不能很好提现问题的严重性,所以还有一个lead值

这里的 Lead 值是指消费者最新消费消息的位移与分区当前第一条消息位移的差值。差值越来越小也就意味着消费者的位移快要低于分区位移马上要数据丢失了

Kafka Broker 进程是一个普通的 Java 进程,所有关于 JVM 的监控手段在这里都是适用的。

具体到 Kafka 而言,就是全面了解 Broker 进程。比如,Broker 进程的堆大小(HeapSize)是多少、各自的新生代和老年代是多大?用的是什么 GC 回收器?这些监控指标和配置参数

  1. Full GC 发生频率和时长。这个指标帮助你评估 Full GC 对 Broker 进程的影响。长时间的停顿会令 Broker 端抛出各种超时异常。
  2. 活跃对象大小。这个指标是你设定堆大小的重要依据,同时它还能帮助你细粒度地调优 JVM 各个代的堆大小。
  3. 应用线程总数。这个指标帮助你了解 Broker 进程对 CPU 的使用情况。
集群监控

监控 Kafka 客户端。

客户端程序的性能同样需要我们密切关注。不管是生产者还是消费者,我们首先要关心的是客户端所在的机器与 Kafka Broker 机器之间的网络往返时延(Round-Trip Time,RTT)。通俗点说,就是你要在客户端机器上 ping 一下 Broker 主机 IP,看看 RTT 是多少。

查看 Broker 端关键日志。

查看 Broker 进程是否启动,端口是否建立,确保服务正常运行。

【稠密索引】

稠密索引,即每一条记录,对应一个索引字段。稠密索引,访问速度非常块,但是维护成本大。根据索引字段不一样,有候选键索引和非候选键索引之分。

img

稀疏索引

相对稠密索引,稀疏索引并没有每条记录,建立了索引字段,而是把记录分为若干个块,为每个块建立一条索引字段,一些数据经常被使用、被查询,那这些字段就应该设置稀疏索引。

假设需要搜索字段值为K的记录,那先检索出比K小的最大值索引块,再根据该字段所在的块也就是记录集合,进行顺序查找,直到找到记录K。

稀疏索引,数据查询速度较慢,但是存储空间小,维护成本低。

Kafka没有部署在Zookeeper上?

1.kafka和zookeeper交互方式

broker topic consumer都注册在zookeeper上

Zookeeper负责负载均衡 选举 数据保存

Kafka集群中会有一个broker被选举为Controller负责跟Zookeeper进行交互,它负责管理整个Kafka集群中所有分区和副本的状态。其他broker监听Controller节点的数据变化。

Controller具体职责如下:

leader 通知更新 分区分配

  • 监听分区变化

比如当某个分区的leader出现故障时,Controller会为该分区选举新的leader。

当检测到分区的ISR集合发生变化时,Controller会通知所有broker更新元数据。

当某个topic增加分区时,Controller会负责重新分配分区。

负载均衡

broker向Zookeeper进行注册后,生产者根据broker节点来感知broker服务列表变化,这样可以实现动态负载均衡。

consumer group中的消费者,可以根据topic节点信息来拉取特定分区的消息,实现负载均衡。

2.带来的问题

1.运维复杂度

Kafka本身就是一个分布式系统,但是需要另一个分布式系统来管理,复杂性无疑增加了。

2.分区瓶颈

如果 ZooKeeper 集群的某个节点的数据发生变更,则会通知其它 ZooKeeper 节点同时执行更新,就得暂停等着大家(超过半数)都写完了才行,这写入的性能就比较差了。一旦宕机就要暂停重新选leader

如果写入的数据量过大,ZooKeeper 的性能和稳定性就会下降,可能导致 Watch 的延时或丢失。

Consumer 的位移数据是保存在 ZooKeeper 上的,所以当提交位移或者获取位移的时候都需要访问 ZooKeeper ,这量一大 ZooKeeper 就顶不住。

3.解决方案

选举策略:

Controller Leader 的选举, Kafka Raft协议 来实现 ,能解决 Controller Leader 的选举,并且让所有节点达成共识。

位移主题

老版本 Consumer 的位移管理是依托于 Apache ZooKeeper 的,它会自动或手动地将位移数据提交到 ZooKeeper 中保存。

Consumer 重启后,它能自动从 ZooKeeper 中读取位移数据,从而在上次消费截止的地方继续消费。这种设计使得 Kafka Broker 不需要保存位移数据,减少了 Broker 端需要持有的状态空间,因而有利于实现高伸缩性。

新版本Consumer 的位移数据作为一条条普通的 Kafka 消息放到位移主题里面

为什么?

考虑到它要求这个提交过程不仅要实现高持久性,还要支持高频的写操作。

位移主题由kafka自动管理。

这个主题存的到底是什么格式的消息呢?

位移主题的 Key 中应该保存 3 部分内容:<Group ID,主题名,分区号 >

所谓协调者,在 Kafka 中对应的术语是 Coordinator,它专门为 Consumer Group 服务,负责为 Group

1.执行 Rebalance

2.提供位移管理

3.组成员管理等。

具体来讲,Consumer 端应用程序在提交位移时,其实是向 Coordinator 所在的 Broker 提交位移。同样地,当 Consumer 应用启动时,也是向 Coordinator 所在的 Broker 发送各种请求,然后由 Coordinator 负责执行消费者组的注册、成员管理记录等元数据管理操作。

所有 Broker 在启动时,都会创建和开启相应的 Coordinator 组件。也就是说,所有 Broker 都有各自的 Coordinator 组件。那么,Consumer Group 如何确定为它服务的 Coordinator 在哪台 Broker 上呢?答案就在我们之前说过的 Kafka 内部位移主题 __consumer_offsets 身上,offeset有一个计算公式可以得出。

通过 kafka-topics.sh 脚本来创建一个名为topic-test1并且副本数为2、分区数为4的topic。(如无特殊说明,本文所述都是基于1.0.0版本。)

bin/kafka-topics.sh --create --zookeeper 192.168.0.2:2181/kafka100 --topic topic-test1 --replication-factor 2 --partitions 4
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值