首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

(六)Redis 消息队列 List、Streams

编程知识
2024年07月29日 14:08

Redis 适合做消息队列吗?有什么解决方案?首先要明白消息队列的消息存取需求和工作流程。

1、消息队列

我们一般把消息队列中发送消息的组件称为生产者,把接收消息的组件称为消费者,下图是一个通用的消息队列的架构模型:
消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性。

(1)消息保序

虽然消费者是异步处理消息,但是,消费者仍然需要按照生产者发送消息的顺序来处理消息,避免后发送的消息被先处理了。

(2)重复消息处理

消费者从消息队列读取消息时,有时会因为网络堵塞而出现消息重传的情况。如果多次处理重复消息的话,就可能造成一个业务逻辑被多次执行,从而出现数据问题。

(3)消息可靠性保证

消费者在处理消息的时候,还可能出现因为故障或宕机导致消息没有处理完成的情况。此时,消息队列需要能提供消息可靠性的保证,也就是说,当消费者重启后,可以重新读取消息再次进行处理,否则,就会出现消息漏处理的问题了。

2、List 方案

List 本身就是按先进先出的顺序对数据进行存取的,所以,如果使用 List 作为消息队列保存消息的话,已经能满足消息 保序 的需求了。具体来说,生产者可以使用 LPUSH 命令把要发送的消息依次写入 List,而消费者则可以使用 RPOP 命令,从 List 的另一端按照消息的写入顺序,依次读取消息并进行处理。
List 并不会主动地通知消费者有新消息写入,如果消费者循环调用 RPOP 命令又会带来 CPU 开销问题。Redis 提供了 BRPOP 命令,称为阻塞式读取,客户端在没有读到队列数据时,自动阻塞,直到有新的数据写入队列,再开始读取新数据。和消费者程序自己不停地调用 RPOP 命令相比,这种方式能节省 CPU 开销。

在解决 重复消息处理 的问题上,一方面,消息队列要能给每一个消息提供全局唯一的 ID,另一方面,消费者程序要把已经处理过的消息的 ID 记录下来,如果已经处理过,消费者程序就不再进行处理了。这种处理特性也称为幂等性,指对于同一条消息,消费者收到一次或多次的处理结果是一致的。不过,List 本身是不会为每个消息生成 ID,所以,消息的全局唯一 ID 需要生产者程序在发送消息前自行生成,并包含在消息中以供消费者处理。

为了 保证消息可靠性 ,List 类型提供了 BRPOPLPUSH 命令,这个命令的作用是让消费者程序从一个 List 中读取消息,同时,Redis 会把这个消息再插入到另一个 List(可以叫作备份 List)留存,这样一来,如果消费者程序读了消息但没能正常处理,等它重启后,就可以从备份 List 中重新读取消息并进行处理了。

3、Streams 方案

如果生产者消息发送很快,而消费者处理消息的速度比较慢,会导致 List 中的消息越积越多,给 Redis 的内存带来很大压力,而 List 并不支持多个消费者同时处理。这时候就要用到 Redis 从 5.0 版本开始提供的 Streams 数据类型了。Streams 是 Redis 专门为消息队列设计的数据类型,它提供了丰富的消息队列操作命令:

  • XADD:插入消息,保证有序,可以自动生成全局唯一 ID
  • XREAD:用于读取消息,可以按 ID 读取数据
  • XREADGROUP:按消费组形式读取消息
  • XPENDING:命令可以用来查询每个消费组内所有消费者已读取但尚未确认的消息
  • XACK:命令用于向消息队列确认消息处理已完成

XADD 命令插入新消息的格式是键 - 值对形式,例如往名称为 mqstream 的消息队列中插入一条消息:

XADD mqstream * repo 5
"1599203861727-0"

其中,* 表示让 Redis 为插入的数据自动生成一个全局唯一的 ID,例如“1599203861727-0”。也可以不用 *,直接在消息队列名称后自行设定一个 ID,只要保证全局唯一就行。

自动生成的 ID 由两部分组成,第一部分“1599203861727”是数据插入时,以毫秒为单位计算的当前服务器时间,第二部分表示插入消息在当前毫秒内的消息序号,从 0 开始。例如,“1599203861727-0”就表示在“1599203861727”毫秒内的第 1 条消息。

XREAD 在读取消息时,可以指定一个消息 ID,并从这个消息 ID 的下一条消息开始进行读取。设定 block 配置项,可实现类似于 BRPOP 的阻塞读取操作,单位是毫秒。例如,从 ID 为 1599203861727-0 的消息开始,读取后续的所有消息(共 3 条)

XREAD BLOCK 100 STREAMS  mqstream 1599203861727-0
1) 1) "mqstream"
   2) 1) 1) "1599274912765-0"
         2) 1) "repo"
            2) "3"
      2) 1) "1599274925823-0"
         2) 1) "repo"
            2) "2"
      3) 1) "1599274927910-0"
         2) 1) "repo"
            2) "1"

再看一个例子,命令以 $ 结尾表示读取最新的消息,同时设置了 block 10000 的配置项,表明 XREAD 在读取最新消息时,如果没有消息到来将阻塞 10000 毫秒(即 10 秒),然后再返回。当消息队列 mqstream 中一直没有消息时,XREAD 在 10 秒后返回空值(nil)

XREAD block 10000 streams mqstream $
(nil)
(10.00s)

XGROUP 创建消费组,是区别于 List 的功能,创建后 Streams 可以使用 XREADGROUP 命令让消费组内的消费者读取消息。
例如,我们执行下面的命令,创建一个名为 group1 的消费组,这个消费组消费的消息队列是 mqstream

XGROUP create mqstream group1 0
OK

执行命令,让 group1 消费组里的消费者 consumer1 从 mqstream 中读取所有消息,命令最后的参数“>”,表示从第一条尚未被消费的消息开始读取。在 consumer1 读取消息前,group1 中没有其他消费者读取过消息,所以,consumer1 就得到 mqstream 消息队列中的所有消息共4条。

XREADGROUP group group1 consumer1 streams mqstream >
1) 1) "mqstream"
   2) 1) 1) "1599203861727-0"
         2) 1) "repo"
            2) "5"
      2) 1) "1599274912765-0"
         2) 1) "repo"
            2) "3"
      3) 1) "1599274925823-0"
         2) 1) "repo"
            2) "2"
      4) 1) "1599274927910-0"
         2) 1) "repo"
            2) "1"

如果队列中的消息已经被其他消费者读取,则其他消费者无法读取,例如,再让 group1 内的 consumer2 读取消息时,返回空值。

XREADGROUP group group1 consumer2  streams mqstream 0
1) 1) "mqstream"
   2) (empty list or set)

消费组的目的是让组内的多个消费者共同分担读取,从而实现负载均衡,例如,让 group2 中的 consumer1、2、3 各自读取一条消息

XREADGROUP group group2 consumer1 count 1 streams mqstream >
1) 1) "mqstream"
   2) 1) 1) "1599203861727-0"
         2) 1) "repo"
            2) "5"
XREADGROUP group group2 consumer2 count 1 streams mqstream >
1) 1) "mqstream"
   2) 1) 1) "1599274912765-0"
         2) 1) "repo"
            2) "3"
XREADGROUP group group2 consumer3 count 1 streams mqstream >
1) 1) "mqstream"
   2) 1) 1) "1599274925823-0"
         2) 1) "repo"
            2) "2"

为了保证消费者在发生故障或宕机再次重启后,仍然可以读取未处理完的消息,Streams 会自动使用内部队列(也称为 PENDING List)留存消费组里每个消费者读取的消息,直到消费者使用 XACK 命令通知 Streams“消息已经处理完成”。如果消费者没有成功处理消息,它就不会给 Streams 发送 XACK 命令,消息仍然会留存。此时,消费者可以在重启后,用 XPENDING 命令查看已读取、但尚未确认处理完成的消息。

例如,查看一下 group2 中各个消费者已读取、但尚未确认的消息个数。其中,XPENDING 返回结果的第二、三行分别表示 group2 中所有消费者读取的消息最小 ID 和最大 ID。

XPENDING mqstream group2
1) (integer) 3
2) "1599203861727-0"
3) "1599274925823-0"
4) 1) 1) "consumer1"
      2) "1"
   2) 1) "consumer2"
      2) "1"
   3) 1) "consumer3"
      2) "1"

如果需要进一步查看某个消费者具体读取了哪些数据,可以执行以下命令,consumer2 已读取的消息的 ID 是 1599274912765-0

XPENDING mqstream group2 - + 10 consumer2
1) 1) "1599274912765-0"
   2) "consumer2"
   3) (integer) 513336
   4) (integer) 1

当 1599274912765-0 被 consumer2 处理了,consumer2 就可以使用 XACK 命令通知 Streams,然后这条消息就会被删除。当我们再使用 XPENDING 命令查看时,就可以看到,consumer2 已经没有已读取、但尚未确认处理的消息了。


 XACK mqstream group2 1599274912765-0
(integer) 1
XPENDING mqstream group2 - + 10 consumer2
(empty list or set)

一张表格,汇总了用 List 和 Streams 实现消息队列的特点和区别
Redis 是一个非常轻量级的键值数据库,Kafka、RabbitMQ 是专门面向消息队列场景的重量级软件,例如 Kafka 的运行就需要再部署 ZooKeeper。如果分布式系统中的组件消息通信量不大,那么,Redis 只需要使用有限的内存空间就能满足消息存储的需求,而且,Redis 的高性能特性能支持快速的消息读写,不失为消息队列的一个好的解决方案。

From:https://www.cnblogs.com/WinterSir/p/18044959
本文地址: http://shuzixingkong.net/article/552
0评论
提交 加载更多评论
其他文章 ThinkPHP超证书查询系统任意文件读取漏洞
超证书在线查询系统,资质证书显示网站源码,证书查询自适应手机端,采用的是thinkphp开源内核,无版权可商用。存在任意文件读取漏洞
ThinkPHP超证书查询系统任意文件读取漏洞 ThinkPHP超证书查询系统任意文件读取漏洞 ThinkPHP超证书查询系统任意文件读取漏洞
FindBugs质量管理
1. FindBugs是什么 FindBugs 是一个静态分析工具,它检查类或者 JAR 文件,将字节码与一组缺陷模式进行对比以发现可能的问题。有了静态分析工具,就可以在不实际运行程序的情况对软件进行分析。 FindBugs就是对编译后的class进行扫描,以发现一些隐藏的bug。如果你拥
git篇-- Git在项目实操中常见的使用命令--02
Git是现代软件开发中不可或缺的版本控制工具。它能帮助开发者跟踪项目的所有变更,并与团队成员高效协作。本文将介绍一些在项目实操中常见的Git命令,帮助你更好地管理代码。 1. 初始化和配置 初始化仓库 在一个新的项目目录中,初始化Git仓库: git init 配置用户信息 在提交代码之前,需要配置
搭建自动化 Web 页面性能检测系统 —— 部署篇
我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 本文作者:琉易 liuxianyu.cn 这一篇是系列文章: 搭建自动化 Web 页面性能检测系统 —— 设计篇 搭建自动化 Web 页面性能检测系统 —— 实现篇 作
搭建自动化 Web 页面性能检测系统 —— 部署篇 搭建自动化 Web 页面性能检测系统 —— 部署篇 搭建自动化 Web 页面性能检测系统 —— 部署篇
sharding-jdbc 兼容 MybatisPlus的动态数据源
背景:之前的项目做读写分离的时候用的 MybatisPlus的动态数据做的,很多地方使用的@DS直接指定的读库或者写库实现的业务;随着表数据量越来越大,现在打算把比较大的表进行水平拆分,准备使用 ShardingJDBC实现,但是发现两者配合起来并不是那么顺利,网上大部分文章都是直接把整个Shard
sharding-jdbc 兼容 MybatisPlus的动态数据源 sharding-jdbc 兼容 MybatisPlus的动态数据源 sharding-jdbc 兼容 MybatisPlus的动态数据源
Segment-anything学习到微调系列3_SAM微调decoder
前言 本系列文章是博主在工作中使用SAM模型时的学习笔记,包含三部分: SAM初步理解,简单介绍模型框架,不涉及细节和代码 SAM细节理解,对各模块结合代码进一步分析 SAM微调实例,原始代码涉及隐私,此部分使用公开的VOC2007数据集,Point和Box作为提示进行mask decoder微调讲
Segment-anything学习到微调系列3_SAM微调decoder Segment-anything学习到微调系列3_SAM微调decoder Segment-anything学习到微调系列3_SAM微调decoder
自写Json转换工具
前面写了简单的API测试工具ApiTools,返回的json有时需要做很多转换,于是开发了这个工具。 功能包括 1、json字符串转为表格,可以直观的展示,也可以复制,并支持转换后的表格点击列头进行排序,比较方便地定位数据。 2、表格转为EXCEL,就是导出Excel文件,支持2003和2007格式
自写Json转换工具 自写Json转换工具 自写Json转换工具
基于EasyTcp4Net开发一个功能较为完善的去持久化聊天软件
之前自己写了一篇介绍TCP的一些常用的功能介绍和特征,并且用代码做了示例,最终开发了一个EasyTcp4Net的TCP工具库,其最大的特色就是使用了微软提供的高性能库中的一些数据结构来处理TCP数据。 最近辞职待业在家,也没啥事做,就利用自己写的TCP通讯库基础上开发了一个示例的聊天程序,功能包括,
基于EasyTcp4Net开发一个功能较为完善的去持久化聊天软件 基于EasyTcp4Net开发一个功能较为完善的去持久化聊天软件 基于EasyTcp4Net开发一个功能较为完善的去持久化聊天软件