MQ-Kafka、rocketmq | 豆包MarsCode AI刷题

117 阅读11分钟
  • kafka:

一、基本架构基本组成:生产者、Cluster集群下的Topic、消费者组Topic组成:用Offset表示消息在partition中的位置ISR与Replica:用于容灾(高可用)总体架构:Broker集群Kafka架构:需要注册中心Zookeeper二、一条消息的发送从Producer到Broker:Batch发送、压缩Broker将消息存到本地磁盘:顺序写Broker读出消息给Consumer:优化传统Broker查询中的数据拷贝:零拷贝Consumer组接收消息:分配策略、reBalance三、使Kafka性能提高的功能


一、基本架构

基本组成:生产者、Cluster集群下的Topic、消费者组

image-20241125133155305

Topic组成:用Offset表示消息在partition中的位置

image-20241125133356910

ISR与Replica:用于容灾(高可用)

image-20241125133854549

总体架构:Broker集群

image-20241125134247432

Kafka架构:需要注册中心Zookeeper

image-20241125134339154

二、一条消息的发送

从Producer到Broker:Batch发送、压缩

image-20241125134728920

Broker将消息存到本地磁盘:顺序写

image-20241125134928131

  • 消息在Broker本地磁盘的存储架构:
  • 注意:这些LogSegment的日志文件以第一个消息的offset命名

image-20241125135213423

  • 了解磁盘底层读写方式:寻道的成本高,应该尽量避免

image-20241125135531454

  • Broker采用顺序写的方式对数据进行存储,能提高磁盘的写入效率

image-20241125135618196

Broker读出消息给Consumer:

image-20241125135912321

  • Broker用二分查找offset所在文件
  • 比如:找offset == 28,就要找到图中6.log文件

image-20241125140021659

  • 通过不同索引文件找到消息:

image-20241125140423597

  • 时间戳索引查找:进一步优化查找

image-20241125140509694

优化传统Broker查询中的数据拷贝:零拷贝

image-20241125140945523

  • 零拷贝优化:
  • 注:Broker数据写入的时候也是用到相关技术,直接将数据从应用空间写入磁盘(不需要拷贝到内核态)

image-20241125141049135

Consumer组接收消息:分配策略、reBalance

  • 涉及消费者组中consumer与Partition的分配问题:

image-20241125141317121

  • 用Coordinator进行分配:实现重平衡

image-20241125141532838

image-20241125142934242

三、使Kafka性能提高的功能

image-20241125143235689

rocketmq:

  • RocketMQ:

一、介绍MQ1)MQ = Message + Queue:2)RocketMQ重要概念3)rockermq的安装:二、API1)快速入门:2)细说消费者:3)消费模式:push、pull4)MQ发送各种消息:同步、异步、单项、延迟、批量* 一、各种消息的使用情况:“ * ”越多越常用* 二、同步消息:安全但是性能不高* 三、异步消息:在发送端不可等待的时候,可以减少业务耗时* 四、单向消息:不用返回结果,吞吐量大、消息可能丢失(比如发送日志)* 五、延迟消息:* 六、批量消息:5)顺序消息:计算投递队列的算法 + consumer顺序模式6)tag:区分过滤同类型的消息7)key:消息去重的唯一标识8)重复消费问题的解决方案之一:MySQL去重表 - 唯一索引9)重试发送 / 接收消息:retryTimes10)springBoot整合mq:11)消费的两种模式:负载均衡、广播12)解决消息堆积问题:理论13)消息丢失问题:14)安全:<过>三、原理1)事务原理:2)延迟消息原理:3)消费重试:四、秒杀项目1)秒杀前置知识:<过>2)秒杀架构:<过>3)环境准备:<过>* 表:* 模块:* 配置:4)业务:关注mq部分五、RocketMQ与Kafka的联系结构命名的联系:架构:存储结构:


一、介绍MQ

1)MQ = Message + Queue:

  • 一、MQ的两个概念:
  • Message:消息,是数据
  • Queue:队列,是一种数据结构
  • 二、队列:
  • 作用一:削峰限流 -> MQ缓存消息,服务端就可以返回结果了,减少业务耗时
  • 作用二:解耦合 -> 消费者服务启不启动不影响生产者的服务,健壮性、高可用性
  • 三、了解几种MQ:rocketmq、kafka
  • 用表格对比:
  • 四、中间件:mq属于消息中间件
  • 什么是跨平台

2)RocketMQ重要概念

  • 一、一些成分的含义:
  • 二、broker内部:Topic + Queue
  • Topic:更多指一种标签,给同一类信息打上标签
  • Queue:队列,是真实存在的一种数据结构,一个Topic对应多个queue,存在在broker中
  • 三、broker集群:讲讲name server作用
  • 四、详解RocketMQ架构

3)rockermq的安装:

  • 在Linux中安装RocketMQ,配置和启动,记录ip、port消息,在windows开发也可以直接连(springboot yml)

www.bilibili.com/video/BV1jL…


二、API

1)快速入门:

  • 一、大概流程:
  • ①引入依赖:这里先用原生api熟悉
  • ②实现:/ 注意是原生api的顺序 /
  • producer:创建生产者组 -> 连接nameserver -> 启动生产者组 -> 创建消息并发送 -> 关闭生产者(shutdown)
  • consumer:创建消费者组 -> 连接nameserver -> 订阅 -> 设置监听器(设置顺序/并发模式等) -> 启动消费者组 -> 挂起当前线程
  • “ system.in.read() ”:将该线程一直挂起就可以一直监听消息
  • ③以上consumer打印内容:
  • 三、补充 - 生产者的一些细节:

2)细说消费者:

  • 一、聊消费者组:
  • 消费者组内的消息订阅关系必须一致
  • mq内消息的投递有两种策略:负载均衡模式和广播模式
  • 二、消费者和队列的数量关系:需要保持队列的数量 >= 消费者的数量,以免消费者的浪费
  • 三、代理者点位、消费者点位、差值的概念:
  • 差值:就是这两个点位的差,就是还没有消费的消息数

3)消费模式:push、pull

  • 一、pull、push模式:了解

4)MQ发送各种消息:同步、异步、单项、延迟、批量

* 一、各种消息的使用情况:“ * ”越多越常用
* 二、同步消息:安全但是性能不高
  • 之前的“ 快速入门 ”就是发送同步消息(发送消息 -> 返回结果“SEND_OK”)
* 三、异步消息:在发送端不可等待的时候,可以减少业务耗时
  • 结果:本线程先结束,再发送消息
* 四、单向消息:不用返回结果,吞吐量大、消息可能丢失(比如发送日志)
  • 补充:一般发送日志,可以发送到数据库,然后进行进一步排查
  • 代码示例:
* 五、延迟消息:
  • 场景:买火车票时,producer先选座位,MQ等待15分钟通知,consumer根据付没付钱进行相关业务处理
  • producer发送消息:一共有18个投递等级
  • consumer接收消息:
  • 细节:如果之前在配置一些东西的时候,默认时8g然后改为512m的话,阉割之后,第一次发送和接收消息的时间不准,第二次就准了
* 六、批量消息:
  • producer:集合可以作为send()参数
  • 注意:批量消息全部会进入一个队列

5)顺序消息:计算投递队列的算法 + consumer顺序模式

  • 一、场景:比如“ 下订单、发短信、发货”一个topic的三条消息,需要的是顺序发送消息
  • 但是默认的消息发送是并发模式的,即不能保证producer先发送和consumer先接收的消息
  • 比如producer发送三个消息,是在这几个队列中轮询发送的,而我们要求的是三个消息有顺序的在一个队列中
  • 细说“有序”:只需要局部有序,这三条消息在一条队列中有序,就能保证顺序了
  • 二、实现
  • 环境准备:创建一个messageModel类:
  • 假设一些数据
  • producer发送顺序消息:
  • 核心是send()方法的第二个参数:*new MessageQueueSelector() {}*里面的决定消息发送队列的逻辑,这样处理可以使消息可以轮询发送给不同的队列(因为按订单号的累加,取膜后的i也会累加),避免直接返回mqs.get(0)这样堆在一个队列中
  • consumer接收顺序消息:监听器指定为顺序模式(MessageListenerOrderly)
  • 效果:多线程但是顺序消费
  • 三、补充 - 顺序模式和并发模式的重试的区别:
  • 顺序模式下如果消费不成功,会无限重试
  • 并发模式下如果消费失败超过16次,会将消息放进死信队列

6)tag:区分过滤同类型的消息

  • 场景:同样是订单消息,但是“衣服”对应的逻辑是发快递,而“生鲜”对应的逻辑是附近门店配送
  • 如何区分这两类消息:
  • ①可以定义为两个不同的topic
  • 通过tag进行区分
  • 一、实现 - producer:发送两个tag不同,同一topic的消息:
  • 二、consumer:
  • 同一个消费者组的订阅关系必须一致:订阅不同tag必须不同消费者组
  • 三、效果:两个组都订阅了vip1,各收到一份vip1的消息
  • 注意:这里是两个组,都订阅肯定都要受到;如果是一个组的几个消费者,则是可以选择负载均衡或者广播
  • 什么时候用两个topic,什么时候用tag:看看官方文档

7)key:消息去重的唯一标识

  • key、msgId:自带的和自定义的
  • 一、示例:
  • producer:发送带key消息(在定义消息的时候指定)
  • consumer:拿到key -> .getKeys()
  • 二、消息的去重:
  • 为什么需要消息的去重:因为消息可能会有重复
  • ①生产者多次投递:几乎不可能因为遵循的是tcp协议(三次握手,如下),但是有一点点可能
  • ②消费者方因为扩容时会重试:重平衡机制
  • 三、如何解决:在comsumer方解决,通过唯一标识key
  • 怎么判断key存不存在(有没有被消费过):将消费的key存放进一个容器中(redis等数据库),拿到消息时先setnx插入redis,如果插入失败则已存在

8)重复消费问题的解决方案之一:MySQL去重表 - 唯一索引

  • 一、解决思路:
  • 补充 - 了解幂等性:多次操作的影响相同
  • 二、解决方法:MySQL建一个去重表(key字段建立唯一索引),接收消息的时候,先把key插入数据库,如果插入成功才执行业务
  • producer:
  • consumer:核心就是业务前插入数据库 + 数据库表字段设置唯一索引
  • ①用jdbcTemplate:
  • ②用原生的jdbc:

9)重试发送 / 接收消息:retryTimes

  • 一、producer:重试发送消息
  • 二、consumer:重试接收消息
  • 注意:重试间隔时间逐次递增
  • 三、死信队列:并发消费模式下重试次数用完,将消息发配死信队列!
  • 四、处理死信队列
  • ①将这些消息存储起来,让人工介入
  • ②更常用:将正常处理业务和处理死信消息结合起来

10)springBoot整合mq:

  • 首先要自己添加依赖,因为官方没有收录版本

image-20240625213822969

  • 一、producer项目:
  • yml配置:name server、group
  • 发送同步消息:
  • 异步消息:
  • 单向消息、延迟消息:
  • 顺序消息之producer:前置环境准备于之前相同(MsgModel自定义类)
  • 带tag消息:用冒号分割
  • 带key消息:放在消息头,setHeader()

  • 二、consumer项目:
  • yml:

image-20240625210416901

  • 监听器:一直监听,不用system.in.read()。直接在类上定义消费者组
  • 根据需要,在定义泛型的时候可以指定MessageExt或者String(只拿消息体)
  • 没有返回值:报错了就是拒收,没有报错就是SUCCESS

image-20240625210739344

image-20240625210908461

image-20240625210922729

  • 顺序消息之consumer:指定顺序模式(默认并发模式)

image-20240625212526199

  • 消费带tag的消息:

image-20240625213124412

11)消费的两种模式:负载均衡、广播

  • 消费的负载均衡 / 广播模式差别:
  • 集群模式(负载均衡模式)消费者点位随消费而后移
  • 但是广播模式,mq不会记录消费者点位(消没消费都不管)

image-20240625221455582

12)解决消息堆积问题:理论

  • 怎么看:一般一个队列超过10w消息,就算消息堆积了
  • 为什么会有消息堆积 -> 解决:三个方法

image-20240625222915910

  • 详细代码操作:

www.bilibili.com/video/BV1jL…

13)消息丢失问题:

  • 一、为什么会丢失消息:原理
  • ①同步刷盘:在producer发送消息到broker时,broker同步将消息写入磁盘(物理上,断电不丢失),如果写入时宕机,可能存在丢失问题(少)
  • ②异步刷盘:在producer发送消息到broker时,broker将消息存在buffer(缓冲区,在内存,断电消息丢失)中,再批量存入磁盘
  • 二、怎么避免消息补发:
  • ①在producer发送消息同时,将消息记录进mysql中,如果消息被mq接收成功,则status = 2,否则status = 1。设置定时任务,每天检查出status = 1的信息进行补发
  • ②开启broker集群模式,搞主备模式,将消息持久化在不同的硬件上

14)安全:<过>

www.bilibili.com/video/BV1jL…


三、原理

1)事务原理:

2)延迟消息原理:

3)消费重试:


四、秒杀项目

1)秒杀前置知识:<过>

  • QPS:每秒处理请求的数量

2)秒杀架构:<过>

  • 尽量减少接口的处理时间:

image-20240626145510179

  • 项目架构:

image-20240626150148872

3)环境准备:<过>

* 表:
  • goods:
  • order:
* 模块:
  • web服务:

image-20240626150525239

  • service服务:

image-20240626150507229

  • 再加入fastjson、mq等依赖(官方没有配置)
* 配置:
  • web服务:

image-20240626150739775

  • service服务:

image-20240626153908354

image-20240626153836573

4)业务:关注mq部分

  • web服务的用户抢购处理:

image-20240626153430555

image-20240626153540249

image-20240626153744337

  • web服务:定时任务,将mysql库存同步到redis中
  • ps:后面还有秒杀项目的测试、redis分布式锁等

www.bilibili.com/video/BV1jL…