跳至主要內容

消息队列-⑥巩固篇

holic-x...大约 31 分钟消息队列消息队列

消息队列-⑥巩固篇

学习核心

  • 消息队列-基础篇
    • 核心概念
    • 应用场景

学习资料

消息队列应用核心题型

1.消息队列常见的应用场景?

消息队列:一个使用队列来进行通信的组件,其核心应用:异步处理、服务解耦、流量控制等

核心场景:应用解耦、异步提速、消息分发、削峰填谷

​ 这些概念都是比较抽象的,结合案例进行说明

为什么需要消息队列?

​ 由于互联网的快速发展,业务不断扩张,技术架构也需随之不断演进。

​ 从传统的单体架构到微服务架构,有成千上百的服务之间相互调用和依赖,因此需要有一个"组件"来解耦服务之间的关系、控制资源的合理应用、缓冲流量洪峰等,因此消息队列应运而生,它常用于实现:应用解耦、一步提速、消息分发、流量控制(削峰填谷)等场景

  • 典型的消息队列应用场景
    • 订单系统:在电商系统中,订单的创建、支付、发货等步骤可以通过消息队列进行异步处理和解耦
    • 日志处理:使用消息队列将日志从应用系统传输到日志处理系统,实现实时分析和监控
    • 任务调度:在批量任务处理、任务调度系统中,通过消息队列将任务分发给多个工作节点,进行并行处理
    • 数据同步:在数据同步系统中,消息队列可以用于将变更的数据异步同步到不同的存储系统或服务

什么情况下需要解耦?

​ 例如服务模块之间的调用,A模块调用B模块,一般场景下如果业务允许不需要等待B模块的响应,则可以考虑进行解耦,一来是提升A模块的响应处理效率,二来避免模块间的调用依赖导致相互影响。解耦后模块A、模块B是不会相互影响的

​ 例如订单服务:生成订单信息 =》发送到MQ =》消费者:支付、订单、物流等模块会相应处理订单信息,相互直接不会受到影响

​ 解耦和异步的作用其实比较类似,但解耦的本质在于避免模块间的相互影响,异步的本质则在于异步处理耗时任务,提升接口响应速度

什么情况下需要削峰?

​ 削峰主要针对的是对瞬时流量的处理

​ 例如一些秒杀场景中,瞬间大量流量打入,如果服务承载能力不足,就可能导致服务被搞崩,一般有两种场景:

  • 主服务承载能力不足:主服务本身能力承载不足直接被搞崩
  • 主服务可承载,下游服务承载能力不足:主服务的压力会传递给到下游服务,虽然主服务可以承载,但是下游服务无法承载就会被搞崩,进而导致链路异常

​ 引入MQ是为了将请求放到队列中,然后由相应的服务慢慢去消费,将瞬时的请求压力平摊到一段时间内进行处理。但需要注意的是如果是一直持续的大流量请求,MQ并不能单纯解决这种场景问题,因为会有持续的请求存入MQ,而消费者消费能力不足就会导致MQ中的任务囤积(消息囤积)进而导致异常,这种情况只能通过横向扩容来增强消费能力

什么情况下需要分发消息?

​ 消息分发的场景:A模块需要将一条通知告知给B、C、D,传统实现方式是依次通知,但如果新增了接收节点的话,A不可避免要相应进行迭代维护,这种方式扩展性较差。通过引入MQ,A只需要将消息传递给MQ,订阅了指定topic的接受节点会相应受到消息并做出响应,不需要A单独一个个去通知。

​ 这种思路其实也是有”解耦“的思想在里面,可以理解为**”分发消息是群发场景下的一种解耦方案“**

2.项目开发中,会如何选择消息队列?

​ 实际上还有很多消息队列可供选择。因此要结合实际业务场景选择合适的队列去适配。于个人而言,选型思路分析可以如下:为什么选择kafka?为什么不用xxx?

​ 其实大多是场景中选用主流消息队列基本够用,因此在选择上考虑结合项目需求和业务场景去做考量

  • 项目需求:对性能和可靠性有要求(例如要支持5000QPS),基于Kafka和RocketMQ分布式的高可用特性,可以作为参考
  • 功能扩展需求:队列的其他功能暂时不作为重要的考量点
  • 团队技术栈:加上团队此前已经有比较成熟的技术体系,选择选择xxx

3.Kafka和RocketMQ有什么区别?

​ 这点主要结合个人使用来说,需要对比说明,如果不熟悉就老老实实挨打,专注于自己熟悉的技术栈

对比补充说明

  • RocketMQ 和 Kafka 相比,在架构上做了减法,在功能上做了加法跟 Kafka 的架构相比,RocketMQ 简化了协调节点和分区以及备份模型
  • 同时增强了消息过滤、消息回溯和事务能力,加入了延迟队列,死信队列等新特性

消息队列实践核心题型

1.消息丢失

kafka什么情况下会出现消息丢失的问题,kafka如何保证消息不丢失

消息丢失可以从三个环节进行分析:生产环节、存储环节、消费环节

  • 生产环节:kafka无法保证生产环节,这点由业务进行保证(通过重试机制异常告警等)

    • 业务侧需正确处理好消息发送的响应,如果发现消息发送异常则可采用重试机制,以确保生产环节消息不会丢失
  • 存储环节:依托于kafka的确认机制和持久化存储,可以确保数据在存储环节可以正常存储(acks的选择,一般选择这种方案(设置acks为1),可靠性和性能都相对较高)

  • 消费环节:消费环节则是基于提交偏移机制,有自动提交和手动提交

    • 自动提交即每次客户端消费一条消息自动提交偏移,让kafka可以通过这个偏移继续消费数据(就算服务出现宕机,后续也能通过这个偏移跟踪定位)
    • 可以通过”手动“提交的方式,只有当消费端确认消费一条消息后自行提交偏移成功后才认为是真正的消费成功,但是这个过程可能会存在客户端消费完消息后没有及时成功提交偏移,而导致消息被重复消费的问题

2.消息重复

kafka什么情况下会出现消息重复的问题,kafka如何保证消息不重复消费

针对消息重复的问题,也可以通过三个环节进行分析:

  • 生产环节:可能是网络抖动或者重试导致消息重复发送,这点kafka无法控制,由业务端控制
  • 存储环节:可以确保数据只存一条
  • 消费环节:kafka可以确保消息只能被同组的同一个消费者消费一次

​ 基于此,实际上kafka对消息不重复消费的保证还是要结合业务进行控制,可以通过业务的“幂等性处理机制”来进行处理,最常见的处理方式是通过Redis、MySQL进行幂等性处理

  • Redis:原子操作
  • MySQL:唯一约束、insert ignore、前置条件判断(版本号校验)

所谓幂等:从数学概念上理解,即以同样的参数多次调用同一个接口和调用一次接口产生的结果是一致的

3.消息有序性

什么时候需要“消息有序”

​ 在一些业务场景中,如果消息间的顺序执行对服务依赖存在影响,则需考虑消息有序的场景。

​ 例如最常见的金融服务的金额操作,假设A有1000元,A需要增值1000元然后统一给B转账2000元,正常顺序操作下这个操作是正确的。但是如果拆分两条消息处理记录顺序出现问题的话,就可能导致流程变为“A给B转账2000、A增值1000”,在A初始余额不足的情况下,这点肯定会导致异常。因此需要通过控制“消息有序”来确保流程的正确性

消息有序如何保证(假设有个业务进入kafka的消息都是有序的,要怎么做?)

从"全局有序"和"部分有序"切入

全局有序:如果要保证消息全局有序,则只能由一个队列往一个topic中发送消息(该topic只能有一个分区),消费者也必须以单线程消费这个队列,以此确保全局有序

部分有序:考虑到大部分需求是不分有序,因此可以基于分区概念(子业务分区、客户分区),将topic内部划分为多个分区(多个队列),每个队列对应一个单点成处理的消费者,在满足部分有序的基础上又可以提升消息处理效率

分区演进分析

​ 基于“分区”概念,最简单粗暴的做法就是根据业务进行分区,每个业务一个分区。

​ 但考虑到对并发度的支持和业务性质,需要从性能和业务需求两方面去兼顾,如果单个业务承载太大,则可以进一步对子业务进行分区(这点可以对标MySQL分库分表的概念)。

​ 分区:

  • 子业务分区:将业务按照一定维度拆分为多个不同的子业务
    • 例如金融服务(风控子业务、支付子业务)、通知服务(短信子业务、微信子业务),每个子业务对应各自的分区
  • 客户分区:基于客户间不存在相互依赖,客户内部需要消息有序的场景
    • 例如某个客户希望进行体重管理,需要将自己的日常计划、体重等记录进行有序排列等场景,可以按照userId进行分区,最简单的形式就是Hash(userId)进行分区。
      • 基于这个分区设计,当增删节点时会对现有Hash路由造成影响,可维护性比较差,可以考虑Redis的“Hash槽方案”、“一致性Hash方案”进行优化
      • 基于这个分区设计,可能会存在分区数据倾斜的问题
    • 为了进一步解决上述分区数据倾斜问题,可以考虑“大小客户分区的方式”,按照客户大小进行分区,大客户一个分区,相当于开通vip通道,不和其他小客户挤在一起
    • 大客户的鉴定:提前预知(现实业务场景中会和大客户接洽签单的)、过程感知(在业务进行过程中去发现大客户,然后采取方案)

4.MQ消息积压了怎么办

核心:消息堆积往往是因为生产速度和消费速度不匹配导致(例如消费失败反复重试、消费者能力弱等),遇到消息积压的情况,首先需定位排查消息积压的产生因素(例如什么原因导致消费慢?)然后对症下药

​ MQ消息积压是MQ常见的问题,一般的解决方案可以结合其产生因素进行分析(消息监控、分析问题、弃车保帅)

  • 监控(发现问题):通过脚本监控查看“偏移量”,确认是否出现积压的情况(积压与否是取决于业务实际场景的)
  • 异常:由于服务异常导致阻塞,需要介入排查异常原因,然后进行处理
  • 无关的依赖服务太多:为了适配一些特殊活动场景,可以将一些和现有活动业务无关的依赖服务暂停,提升整体的消费速率
  • 消费能力不足:压力过载(压力超过可承载范围)
    • 优化:如果是本身消费能力较弱,则可优化消费逻辑(例如单条消费切为批量处理等)
    • 扩容:这是最常见的一种解决思路,用钱解决问题。但需要注意看场景是否支持水平扩容,否则加机器也没有用
    • 缓冲(增加中间消费者):在kafka和真正的消费者之间放一层“缓冲带”暂时将任务缓冲起来,主要是为了解决MQ消息积压问题,但实际上消息真正被消费处理的能力并没有提升,本质还是要解决消费效率提升的问题
    • 保新:在一些特殊业务场景中,如果业务许可的话,可以暂时将积压的任务放一边,将新产生的任务放到新队列,消费新队列即可,待后续消费端缓过来再去处理老消息

消息队列架构核心题型

1.有了Topic为什么还要Partition?

​ 不同消息可以存放在不同的Topic下用于业务隔离,避免消息混乱。但考虑到业务的量级是存在差异的,因此kafka引入Partition,一个Topic下可以有多个Partition(类似数据库分库分表的概念),以支撑业务处理的并发度

2.Partition是逻辑概念还是物理概念?

​ Topic 是逻辑概念,Partition 是物理概念,消息通过指定key发送,实际存储在某个Topic下的某个Partition分区

3.介绍消息生产的流程

​ 消息生产的流程有3个环节:创建消息=》序列化=》发送消息

  • 创建消息:即封装数据为指定的kdfka格式(消息格式)
  • 序列化:将创建好的消息数据序列化为二进制数据,用于网络传输
  • 发送消息:调用方法,设定指定存储key(对应topic的指定分区),发送消息

4.有消费者为什么还要有消费者组?

​ 消费者组的概念是指将做同一个业务的消费者聚合起来共同协作,它可以理解为统一管理、协同的作用,提升业务处理并发度。当消费者组成员发生变化时会触发相应的再平衡机制,用于协调相应的消费者组重新的分区分配

5.介绍下再平衡机制

​ 再平衡机制的引入是用于协调消费者中的分配分区。当消费者组成员发生变化或者分区数量发生变化的时候,就会相应触发再平衡机制,对消费者组成员重新进行分区分配。

再平衡过程

  • 暂停消费者
  • 触发再平衡
  • 重新分配分区
  • 通知消费者
  • 恢复消费

分区分配策略

​ 分配策略可以分为两大类:一类是“急切的再平衡”、一类是“增量的再平衡”

“急切的再平衡”:基于STW概念,它会暂时暂停所有消费者的工作,直到再平衡工作完成才会通知消费中继续工作。基于这种机制可能存在两个问题:

  • 消费者闲置:再平衡过程中,相关的消费者被闲置
  • 再平衡影响原有分配格局:经过再平衡后,消费者会重新加入消费者组获取新的分区分配,不一定能够取回原有的分区,整个格局变了

“增量的再平衡”:它是一种优化演进方案,一些没有收到影响的消费者可以正常进行工作,它是一种渐进式的调整,可能会经过多次调整达到一个最终适配的平衡状态,这种方式虽然稳但是相对来说耗费的时间也是比较多的

消息队列高可用核心题型

1.Replica、Leader、Follower 三者概念

​ Kafka 高可用的一个重要核心是“多副本”机制,而Replica、Leader、Follower可以理解为相应的概念核心

​ 此处的副本概念强调的是“分区副本”(理解概念,关注核心)

​ Replica:Kafka集群中的一个分区副本(每个副本保存了分区的完整数据)

​ Leader:Leader 分区副本(Leader 负责处理分区的所有读写请求)

​ Follower:Follower 分区副本(Follower 只能同步 Leader 数据)

​ 可以简单理解为 Replica = Leader + Follower

2.Kafka 中 AR、ISR、OSR 三者概念

​ AR、ISR、OSR 概念的引入是“副本写入机制”的扩展概念。当副本写入策略(即如何确认写入成功的参考标准)为all,此时只有Leader接收到“所有副本”的确认反馈才认为写入成功(此处的“所有副本”是有条件的,如果“所有”为“全部副本”概念,那么就会出现一台Follower宕机就会造成整个集群的不可用,这点肯定是不行的,因此引入AR、ISR、OSR概念,来约束这个“所有”的概念 =》即“所有副本” = ISR集合中的副本)

  • AR:分区的所有副本(包括Leader、Follower),是一个整体的集合
  • ISR:指的是和Leader副本保持同步(或数据同步的差异在一定阈值范围内)的副本集合(这个集合是动态变化的)
  • OSR:指和Leader副本不同步(或数据同步的差异超出一定阈值)的副本集合

3.分区副本什么情况下会从ISR中剔除?

ISR 是动态变化的机制,回归ISR的概念,既然它和Leader副本保持“高度”同步,那么当不满足这个条件时就会被动态剔除。例如副本所在的broker宕机或者网络抖动导致没跟上节奏

​ 每个Partition都会由Leader 动态维护一个与自己基本保持同步的ISR列表。所谓动态维护,就是说如果一个Follower比一个Leader落后超过了给定阈值(默认是10s),则Leader将其从ISR中移除。如果OSR列表内的Follower副本与Leader副本保持了同步,那么就将其添加到ISR列表当中

4.分区副本中的Leader如果宕机,但是ISR却为空该如何处理?

​ 正常情况下如果Leader宕机,则会通过Leader选举机制重新从ISR中选举一个。如果说ISR为空,那么可以考虑“OSR”,通过配置参数 unclean.leader.election 来决定是否从OSR中选举出leader,但需注意 OSR 的消息较为滞后,可能会出现消息丢失的问题。

​ 如果没有配置unclean参数,且ISR也为空,那么OSR也没办法参与竞选,就会最终导致这个分区不可用(因为没有新Leader来支持读写了,只能被动等待原Leader恢复或者看看是否有新副本节点动态ISR)

5.分区副本之间同步,是推还是拉?

​ 是“拉机制”,Follower副本主动拉取Leader的数据进行同步:涉及“HW(高水位线)”、“LEO(下一条要写入的位置)”概念

  • Follower 拉取,发现状态同步到最新,则不做任何操作
  • 写入新数据到Leader(Leader的LEO改变),Follower拉取发现LEO不同(Follower的LEO滞后于Leader的LEO)因此拉取消息进行同步,并相应更新自身的LEO值
  • Follower 再次拉取,Leader发现LEO变高了(对比Leader目前的HW),引发Leader的HW更新并反馈给Follower,Follower收到反馈后就会相应更新HW,继续和Leader保持同步,以此类推

拉模式的优势在于副本机器可以根据自身的负载情况来拉取

消息队列高性能核心题型

1.Kafka为什么这么快?

​ Kafka 的高性能得益于很多方面,可以结合“底层机制”+“应用层”进行性能优化说明

​ 从**“存储设计”(多层次:分治(Topic分区)+查询优化(Partition的多文件拆分))、“读写”(顺序写、Page Cache)、“传输”(零拷贝)、“应用层”(批量操作、数据压缩)**等方面提供了性能优化支持

2.聊聊Kafka顺序写机制

​ “顺序写”指的是顺序将数据写入磁盘(对照kafka的写入机制即将数据追加到磁盘末尾),这种写入方式的性能是非常高的(其性能既接近普通内存写,又降低了数据丢失风险),顺序写入减少了磁盘寻道时间,使得磁盘IO操作更加高效,能够持续高速写入数据,从而实现高吞吐量

3.聊聊Kafka页缓存机制

​ “顺序写入磁盘”既然可以那么快,那么“顺序写入内存”则性能更快

​ 页缓存时Kafka充分利用操作的系统page cache进行读写优化,提升效率

  • 写入消息:将消息写入page cache,然后由操作系统异步刷入磁盘
  • 读取消息:读取消息先从page cache读取,如果命中则返回,如果未命中则从磁盘中读取并回写到page cache后返回

4.聊聊Kafka零拷贝机制

​ Kafka的零拷贝机制是针对传输环节的优化

  • 零拷贝技术可以优化数据发送效率,Kafka则充分利用了Linux的这个特性来提升性能
  • Kafka底层传输是通过调用sendfile()系统调用函数,进而减少【上下文切换】、【数据拷贝】带来的损耗
    • 【上下文切换】:sendfile() 替换了原有的read()、wirte()(传统:2次=》零拷贝:1次)
    • 【数据拷贝】:数据直接在核心态传输,只需要拷贝2次(传统:4次=》零拷贝:2次)

5.聊聊Kafka分层设计机制

​ Kafka分层设计采用“分治”+“查询优化”的核心实现分层设计:

  • 分治(Topic分区):将Topic划分为多个Partition,可以理解为将一个大数组拆分为多个小数组分布在broker上,可以提升操作并发度
  • 查询优化(Partition的进一步拆分):Kafka进一步将Partition拆分为多个文件进行存储(.log.timeindex.index),可以通过索引文件快速定位数据

6.聊聊Kafka哪些环节用了批量操作

​ 批量操作是后端优化的一个通用设计,Kafka提供了批量发送和批量消费概念:

  • 批量发送:将消息放在缓冲地带,待达到一定量后统一发送(减少IO交互和网络带宽的性能损耗)
  • 批量消费:拉取一批数据后再统一消费(减少IO交互和网络带宽的性能损耗)

7.聊聊Kafka数据压缩

​ Kafka的数据压缩是对消息进行压缩,便于传输。Kafka对数据压缩的环节体现在producer端和broker端

在prodcuer端进行数据压缩:对消息按照指定压缩算法进行压缩,可以和批量操作搭配使用,提升压缩效益,达到更好的性能提升效果

在broker端进行数据压缩:在broker端压缩存在一定的规则

  • 如果broker端指定了压缩,而prodcuer端没有指定压缩,则broker正常压缩即可
  • 如果broker端和prodcuer端都指定了压缩,则看两者的配置是否相同
    • 压缩配置相同,则broker不会重复压缩
    • 压缩配置不同,则broker会对消息进行解压缩,然后再按照broker端配置的压缩算法进行重新压缩

消息队列扩展核心题型

1.消息队列的模型有哪些?

核心:消息队列的模型主要有队列模型(点对点模型)、发布/订阅模型

点对点模型:生产者将消息发送到队列,消费者从队列读取并处理消息,并且每条消息只能被消费者消费一次。这点和数据结构里的队列概念类似(消费即出队),所以在某些资料也会称之为队列模型

发布/订阅模型:为了解决一条消息可以被多个消费者消费的问题,引入发布/订阅模型。将消息发送到一个Topic中,让所有订阅了该Topic的订阅者都能消费这条纤细

​ RabbitMQ 采用的是队列模型,RocketMQ和Kafka采用发布/订阅模型

2.消息队列的核心术语

核心:角色(Producer、Consumer、Broker、命名中心)、分区(Topic、Partition)、消费(消费者组、offset)

​ 一般将发送消息方称为生产者Producer、消息接收方称为消费者Consumer、消息队列服务端称为Broker(通过命名中心(RocketMQ称namesvr、Kafka用zookeeper)存储Broker、生产者消费者等服务地址、主题信息等内容)

​ 消息从Producer发往Broker,随后Broker将消息存储至本地,然后Consumer从Broker拉取消息进行消费(或者Broker推送消息至Consumer)

​ Kafka 中可以通过对Topic进行分区(Partition),进而提升主题消费的并发能力(可进行子业务分区或者按照客户进行分区)

​ 与消费者相关的还有消费者组概念,一条消息会被发往订阅了这个topic的消费者组,但一条消息只能被同一个消费者组的同一个消费者消费,因此每个消费者组会维护其相应topic的消费偏移量offset,通过这个偏移量来确定消费位置(消费点位),避免消息被重复消费

image-20241021085714549

3.消息队列设计成推消息还是拉消息?推拉模式的优缺点?

场景分析

​ 针对消息队列场景中推拉模式的讨论一般指的是Consumer和Broker的交互:

  • 推模式:Broker 主动推送消息给Consumer进行消费
  • 拉模式:Consumer主动从Broker中拉取消息进行消费

​ 对于Producer和Broker的交互,一般是由Producer主动推送消息给Broker的。试着反向思考,如果Broker拉取Producer消息,则Producer需要将消息保存在本地等待Broker拉取,那么如果有成千上百个Producer时,则消息的可靠性需要由Producer和Broker共同保证。Broker可以通过多副本机制来确保消息的存储可靠,但是Producer方的可靠性则是难以把控了,因此默认的情况下都是由Producer推送消息给Broker的

Consumer和Broker的交互(推拉模式分析)

  • 推模式:

    • 优势:
      • 消息实时性高:Broker接收完消息之后就可以立马推送给Consumer
      • 消费方使用简单:对于消费者来说即来即用,不需要做额外的操作,只需要耐心等待消息到来然后进行消费即可
    • 不足
      • 推送速率难以适配消费速率:推模式难以根据消费者的状态去控制推送速率,如果没有把控好平衡就容易造成一种"攻击态"(即推送速率过快,消费无法适配导致崩盘)
    • 适用场景:
      • 推模式难以根据消费者的状态去控制推送速率,因此适用于消息量不大、消费能力强、实时性要求就较高的情况
  • 拉模式:

    • 优势:
      • 消费方控制主动权:消费方可以自由选择拉取策略,根据自身消费能力进行适配
      • Broker方责任轻松:对于Broker方而言,只需要关注消息存储,对于消息消费则是通过和消费者进行对接,消费者需要多少消息就给多少
    • 不足:
      • 无法明确拉取频率(空拉/消息忙请求、消息延迟):由消费者进行主动拉取,因为消费者无法明确消息拉取的时机,只能通过不断拉取进行确认(但又不能设定过高的频次,频次过高容易空拉)
        • 拉取频次过高:相当于变相攻击Broker,但并不是每次拉取都有任务,容易造成**空拉(消息忙请求)**的情况
        • 拉取频次过低消息延迟加剧
  • 推拉模式选择

    • Rocket 和 Kafka 都选择了拉模式(业界的ActiveMQ是基于推模式)
    • 场景分析:倾向选择拉模式。Broker作为一个中间层不该承载过大压力(不该成为消费瓶颈),其核心在于关注自身消息持久化存储的可靠性,而对于消息消费则应由消费方进行把控。此外,为了避免拉模式的缺陷,Rocket 和 Kafka 采用了长轮询的方式来实现拉模式,即通过消费者去Broker处拉取消息时,当有消息则立即返回,如果没有消息则采用延迟处理的策略(即保持连接,暂时hold住请求),当有新消息到来时则通过之前hold住的请求及时返回消息,以确保消息的及时性,在避免频繁拉取的同时减少消息延迟

4.Kafka 中关于事务消息的实现?

​ Kafka 的事务消息是在一次事务中需要发送多个消息的场景下,需要保证多个消息之间的事务约束(即多条消息要么都发送成功,要么都发送失败)。Kafka 的事务基本上是配合其幂等机制来实现exactly once语义的

  • 事务开启:当生产者开始一个事务时,它会向 Kafka集群中的事务协调者(通常是 Broker 的一部分)发送一个请求。事务协调者将这个事务记录到专门的事务日志中
  • 消息发送:生产者随后可以发送一系列消息,这些消息会被标记为事务的一部分。与 RocketMQ不同,Kafka 并不会让这些消息立即对消费者可见;相反,它们会被暂时隔离
  • 预提交:当生产者完成了所有的消息发送后,它可以向事务协调者发送一个预提交请求。事务协调者会在事务日志中标记该事务的状态为预提交,并通知相关分区
  • 事务提交/回滚:生产者接着发送最终的提交或回滚请求给事务协调者。如果选择提交,协调者会完成两阶段提交过程
    • 第一阶段:将事务状态更新为预提交,并将此更改写入事务日志
    • 第二阶段:向所有参与事务的分区发送一条特殊的控制消息,指示事务已经提交。消费者只有在看到这条控制消息后才会消费属于该事务的消息
  • 事务结束:一旦提交过程完成,事务协调者会在事务日志中记录事务的最终状态,表明事务已经成功结束或者被回滚

5.Kafka 中 Zookeeper 的作用?

对于Kafka而言Zookeeper扮演什么角色?

​ ZooKeeper 是一个开源的分布式协调服务框架,也可以认为它是一个可以保证一致性的分布式(小量)存储系统。特别适合存储一些公共的配置信息、集群的一些元数据等等。它有持久节点和临时节点,而临时节点这个玩意再配合 Watcher 机制就很有用。当创建临时节点的客户端与 ZooKeeper 断连之后,这个临时节点就会消失,并且订阅了节点状态变更的客户端会收到这个节点状态变更的通知

​ Zookeeper可以用于服务发现:集群中某一服务上线或下线都可以被监测到,也可以实现故障转移的监听机制。而前期版本的Kafka是强依赖于Zookeeper实现元数据管理、选举、扩容等机制

  • 元数据管理:存储Broker的信息、主题数据、分区数据等
  • 选举:例如控制器的选举,每个 Broker 启动都会尝试在 ZooKeeper 注册 /controller 临时节点来竞选控制器,第一个创建/controller 节点的 Broker 会被指定为控制器。竞争失败的节点也会依赖 watcher 机制,监听这个节点,如果控制器宕机了,那么其它 Broker 会继续来争抢,实现控制器的 failover
  • 扩容:

为什么后期版本迭代Kafka要抛弃Zookeeper?

​ Zookeeper 拥有分布式协调能力,Kafka主要是用Zookeeper来管理Broker/Topic数据、记录分区和消费者关系、存储配置以及选择Controller。在很早的Kafka版本中,Kafka会利用Zookeeper存储消费偏移信息,但由于Zookeeper不适合用作频繁IO交互的场景,因此砍掉了这个设计,通过引入“内部主题”(_consumer_offsets)存储记录消费偏移(提交),减轻Zookeeper负担,让其专注协调相关事宜

  • 运维分析:Kafka本身作为一个中间件,还需要依赖Zookeeper中间件,因此从运维角度上看需要兼顾Kafka和Zookeeper的集群运维
  • 性能分析:Zookeeper有一个"强一致性"的特点,当Zookeeper集群中的某个节点发生变更时,会通知其他Zookeeper节点同时执行更新,需要等待超半数节点写入完成才能接着往下(因此整体上看写入性能较差)。当Kafka需要存储的元数据、配置、偏移量信息过多的时候,Zookeeper的性能和稳定性就会下降,且ZK的选举过程也不快
    • 为了解决这个问题,Kafka对偏移量的存储进行了优化,即引入内部主题_consumer_offsets进行存储,将位移的提交和获取当做消息一样进行处理,避免频繁访问Zookeeper导致性能下降

去Zookeeper:为了降低对ZK的依赖,Kafka此前对版本进行了相应的优化。在后续的迭代版本中为了真正达到去ZK的目的,Kafka将元数据存储到自己的内部(类似于此前的位移存储优化),并引入KRaft 实现Controller选举,进而逐步取代ZK的位置

既然要去ZK为什么一开始还要用ZK?

​ 项目都是在版本演进中不断迭代优化的,ZK的引入是由于其是一个功能强大且经过验证的分布式协调工具,因此在版本早期引入ZK能够简化一些版本实现,但随着后期的业务发展其依赖过度的弊端也逐渐显现,因此才考虑是否有其他平替方式能够优化系统性能进行改造

​ 软件开发从来没有绝对的适配性,只有在版本演进中慢慢进行迭代优化和改造

6.Kafka 的应用场景?

Kafka 的吞吐很高,使用的场景也很多,以下为一一些典型场景:

  • 日志聚合:Kafka 可以用来收集应用日志、服务器日志、数据库日志等,将这些日志集中到一个地方进行分析和监控
  • 监控数据:实时收集系统监控数据,如 CPU 使用率、内存使用率、网络流量等,并通过 Kafka 进行分析和报警
  • 数据管道:Kafka 可以作为数据管道,将数据从一个系统传输到另一个系统,如将数据从数据库传输到数据仓库数据湖、Elasticsearch等
  • 流处理相关:结合流处理框架(如Apache flink、Apache Storm、Apache Spark Streaming 等),使用 Kafka 作为数据流平台,进行实时数据处理和分析

​ 可以看到一般都是大数据、实时大流量数据的场景用 Kafka,而业务的消息用 Kafka 的比较少,更多使用的是RocketMQ 之类的,因为相比而言 RocketMQ 业务功能会多很多,例如延迟消息、事务消息等等

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3