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

图解RocketMQ之消息模型详解(1)

大家好,我是苍何。

经过前面两篇文章,相信大家对 RocketMQ 的整体架构以及主题 Topic 有了一定的了解,也理清了消费者、生产者、broker、nameserver 这几个组件的概念及作用。

rocketmq架构图

RocketMQ 我们通常叫消息队列,不知道大家有没有想过,为何叫消息队列呢?怎么不叫消息组件或者消息中间件呢?(当然有这么叫的,但少的就像我的头发一样😂)

这其实还要和我们这篇文章的主题(消息模型)有关,其实在早些时候,用来做消息中间件的组件确实是通过队列的方式来实现的,所以消息队列这一叫法多少有些历史原因。但如今很多消息中间件底层不仅仅是通过一个队列来简单实现消息存储的。

下面是本篇主要内容,方便小伙伴们快速查看。
文章目录

消息队列两大基础模型

放眼整个消息队列,需要着重理解两个基础模型,他们分别是队列模型主题模型

队列模型

我们知道队列是一种数据结构,秉持着先进先出的特性。为了更好的理解,我们还是请出炒菜大师鸡毛,这不暑假来了嘛,鸡毛和朋友们来贵州某景区旅游。

队列就像游客排队在售票窗口买票一样,排在前面的游客先买,买完就走,而排在后面的鸡毛就得乖乖等他前面的人都买完,才能轮到他。

鸡毛排队买票(队列模拟)

而这里的队列模型本质和队列数据结构并无什么差别,消息在队列中也是先进先出,先来的消息先被消费,后来的消息后被消费。

消息也跟排队买票一样

一开始消息队列就是这么设计的,消息从生产者发出后,到了消息队列,消费者们就依次取出消息消费。一切都那么顺理成章,消息被一个消费者消费完了也就完了,消息会被删除,其余消费者无法再消费这条消息。

这在早期的时候是满足需求的,消息被消费完后自然会被删除,防止堵塞在队列中影响后续消费者进行消费。

消息消费完就删除了

那如果这时候,消费者 B 也要消费刚刚消费者 A 消费完的消息,该怎么办呢?聪明的你估计很快想到,那可以增加多个队列啊,复制同一份消息到多个队列,A 消费完队列 1 的消息,B 接着消费队列 2 的消息,不就可以啦?

一份消息多个队列

这样问题是可以解决啦,但一份消息被保存在了多个队列上,不说别的,光资源占用和性能就极大有影响,而且随着消费者的增多,队列将会增多,冗余的消息会越来越多,雪球越滚越大,最后很难 hold 住,且大大有违背消息中间件的核心之一——解耦

那么有没有什么更好的办法呢?那当然有,那就是接下来的要讲的主题模型,也可以叫做发布-订阅模型。

主题模型

主题模型其实和我们设计模式中的观察者模式很像,消费者不再争抢着去队列中消费消息,而是去订阅消息,而订阅的依据是啥呢?其实就是上一篇中说的主题(Topic),

在主题模型中,生产消息的为发布者(Publisher),消费消息的为订阅者(Subscriber),存放消息的逻辑容器为主题(Topic)

这样,发布者和订阅者之间只需要基于主题,比如我往 normal-topic 这个主题发布消息,那么订阅了 normal-topic 这个主题的就能收到这个消息,而我向 vip-topic 这个主题发布消息,订阅了该主题的就能消费该主题的消息。

这样就能满足,不同消费者消费同一消息,且相互各不影响。是不是就方便很多了呢?

主题模型

RocketMQ 中的消息模型

RocketMQ 中的消息模型就是通过主题模型(也即发布订阅模型)来实现的,那么内部具体是如何实现的呢?

我们上一篇中说到 topic其实是一个逻辑概念,真正存放消息的最小单元其实是队列,那么问题很多的小明他就问了,消费者怎么知道该从哪里消费消息?一次消费完后如何保证下次不会再重复消费该消息呢?如果业务进行故障恢复,如何重新消费消息?

要想理清这些,就得了解 RocketMQ 的消息进度原理

别急,我们先来理清楚 2 个概念,消息点位消费点位

消息点位

消息实际存储在队列中,每个消息在队列中都会有一个唯一的 long 类型的坐标,有点类似于数组下标用来定位消息在队列中的位置,这个坐标称之为消息点位(Offset)。

消息点位

队列中最早的一条消息叫最小消费位点(MinOffset),相对应的,最新的一条消息就叫最大消费位点,按照道理来说,队列是可以无限扩充的,也即理论上最小和最大消费点位之间可以被无限拉大。但无穷的东西都是不存在的,当然还是会受限于宿主机(也就是服务器)内部的存储。一个 1 核 1 G 的服务器,你说能大到哪里去?😂

不够存咋办?RocketMQ 内部会滚动删除队列中最小的消息,所以要想消息长久保存,要合理配置好资源。

消费点位

上面说消息点位其实是确定消息在队列中的位置坐标,每个主题的队列都可以被多个消费者订阅,如果消息被消费者消费完后就删除了,那下一个消费者就消费不到同样的消息了。

所以消费者消费完消息后不能删除,需要记录当前消费的消息的坐标位置,其实可以看成是消费记录,这个其实就是消费点位(ConsumerOffset)。

消费点位和消息点位的关系如下图:

消费点位和消息点位的关系

可以看出,其实消费点位记录的就是消息点位坐标,且介于最大消息点位和最小消息点位之间。且是存在服务端的和客户端无关,因此 RocketMQ 支持消费者的消息进度恢复。

消费者消费完一条消息就会记录一个消费点位,这样就能满足不同消费者可以消费同一条消息了。

消费点位简例

如上图所示,鸡毛和狗毛分别可以消费不同主题下的不同消息,且仅需要记录下各自的消费点位即可:

  • nomal-topic-鸡毛-3
  • nomal-topic-狗毛-3
  • vip-topic-鸡毛-2
  • vip-topic-狗毛-1

而且鸡毛和狗毛都可以同时消费 nomal-topic 这个主题下的 M 3 消息且消息消费完也不会消失,是不是很完美?

没错,相当完美,但实际企业级项目中,消费者并不只有单个,通常是以消费者组的形式存在,不同的消费者组可能位于不同的集群或者不同的机房,而生产者通常也会有多个,所以实际上,topic 是需要维护多个队列的。

至于有多少个队列,我们上一篇中说过,是可以在 topic 创建和编辑的时候配置的,消费者多就需要多配置,但至少要有一个队列

而生产者具体发送消息到哪个队列取决于 RocketMQ 的机制,默认是轮询方式,也就是根据消息产出的先后,依次向 topic 下的队列发送。

我们用一张图来系统性的分析企业级 RocketMQ 的具体部署架构吧,当然了熟悉 PmHub 的小伙伴应该也知道,PmHub 也完全按照企业级架构部署的(毕竟我们主打的就是企业级项目👍)

PmHub中RocketMQ架构

消费点位的设计非常灵活,当然了对于我们面试来说,同样也是有很多文章可以挖的,比如 RocketMQ 如何处理消息堆积,如何实现重复消费或者跳过部分消息不消费,我们将在下一篇继续深挖。

我是苍何,这是图解 RocketMQ 教程的第三篇,我们下篇见~

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java程序中常见问题
  • Linux源码阅读笔记14-IO体系结构与访问设备
  • LC61----1374. 生成每种字符都是奇数个的字符串(字符串)---java版
  • C++树(二)【直径,中心】
  • 初识dockerFile之RUN和WORKDIR
  • 在 VM 虚拟机中安装 openEuler + 桌面
  • 【RT摩拳擦掌】RT600 4路音频同步输入1路TDM输出方案
  • 自动导入unplugin-auto-import+unplugin-vue-components
  • 十八、指针
  • Linux下如何设置系统定时任务
  • 鸿蒙 next 5.0 版本页面跳转传参 接受参数 ,,接受的时候 要先定义接受参数的类型, 代码可以直接CV使用 [教程]
  • 神经网络之卷积神经网络
  • 运维锅总浅析Kubernetes之Ceph
  • DVWA的安装和使用
  • CTF ssrf 基础入门 (一)
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • Docker入门(二) - Dockerfile
  • Flannel解读
  • Java Agent 学习笔记
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • MySQL数据库运维之数据恢复
  • Node项目之评分系统(二)- 数据库设计
  • PHP面试之三:MySQL数据库
  • spring cloud gateway 源码解析(4)跨域问题处理
  • Swoft 源码剖析 - 代码自动更新机制
  • Unix命令
  • 给初学者:JavaScript 中数组操作注意点
  • 关于for循环的简单归纳
  • 后端_MYSQL
  • 基于axios的vue插件,让http请求更简单
  • 看域名解析域名安全对SEO的影响
  • 巧用 TypeScript (一)
  • 数组的操作
  • 算法之不定期更新(一)(2018-04-12)
  • 一个项目push到多个远程Git仓库
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • $L^p$ 调和函数恒为零
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (转)EOS中账户、钱包和密钥的关系
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • .axf 转化 .bin文件 的方法
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET 读取 JSON格式的数据
  • .NET 分布式技术比较
  • .Net 执行Linux下多行shell命令方法
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!