redis实现消息队列
背景
消息队列(Message Queue)是一种常见的软件架构模式,用于在分布式系统中传递和处理异步消息。它解耦了发送消息的应用程序和接收消息的应用程序之间的直接依赖关系,使得消息的发送者和接收者可以独立地演化和扩展。
消息队列的基本原理是发送者将消息发送到一个中间代理(即消息队列),然后接收者从该中间代理中消费消息。中间代理充当了消息的缓冲区,确保消息的可靠传递和持久化存储(根据需要),同时提供了高吞吐量、低延迟和可伸缩性。
相信在做分布式服务开发的时候,或多或少的使用到了消息队列,如主流的kafka
、 rocketMQ
。总结下来,消息队列的优点包括:
- 异步通信:发送者和接收者之间的解耦,使得它们可以独立地操作和演化,无需实时等待回应。
- 应用解耦:消息队列使不同的应用程序能够以独立的方式进行开发、部署和伸缩,降低了系统之间的耦合度。
- 削峰填谷:消息队列可以作为缓冲区,处理突发的请求和高峰期的流量,从而减轻系统的压力。
- 消息持久化:消息队列可以将消息持久化存储,确保在异常情况下不会丢失消息。
- 可靠性和扩展性:消息队列提供了高可靠性和可伸缩性,通过多个消费者处理大量的消息。
总而言之,消息队列是一种强大的软件架构模式,通过解耦应用程序之间的依赖关系,提供了高可靠性、高吞吐量和可伸缩性的消息传递机制。它在构建分布式系统、处理异步任务和解决系统耦合等方面发挥着重要作用。
那今天的案例呢,没有使用到kafka
rocketMQ
, 而是继续我的专题redis
。
redis实现消息队列
list
list这种数据结构天然的支持消息队列,常用的命令如下:
命令 | 描述 |
---|---|
LPUSH key value | 在列表头部插入一个或多个值 |
RPUSH key value | 在列表尾部插入一个或多个值 |
LPOP key | 弹出并返回列表头部的一个值 |
RPOP key | 弹出并返回列表尾部的一个值 |
LRANGE key start stop | 获取列表中指定范围内的所有值 |
LLEN key | 获取列表的长度 |
好的,这个shigen
用Java的代码实现以下:
- 创建消息队列服务类
redisMessageQueueService
主要的是三个方法,发送数据、消费数据和判断消息队列是否为空。
- 消息处理类
messProcessor
这个类或者说是组件主要是处理消息,这里简单的在控制台输出打印。
- 系统的接口
messageQueueController
其实就是通过接口的方式调用messageQueueServie
,实现消息的发送和接受消费。
那最终的效果是什么样的呢》我本地使用的是curl
进行的进一步的测试。
list实现的方式测试效果:
最后,总结一下list实现消息队列的优缺点:
优点:
- 简单易用:Redis的List数据结构操作简单,易于理解和使用。
- 支持多样化操作:List数据结构提供了丰富的操作方法,如插入、删除、获取范围等。
缺点:
消息队列的设计最重要的就是消息的防丢失问题。
- 缺乏消息确认机制:List方式没有内置的消息确认机制,当消费者处理消息失败或发生异常时,消息可能会丢失。
- 不支持消息持久化:Redis的List数据结构默认存储在内存中,当Redis重启或宕机时,消息也会丢失。
- 不适合高并发场景:在高并发情况下,List方式可能存在性能问题,因为LPUSH和BRPOP是单线程操作,无法充分利用多核CPU的优势。
- 不适合多订阅者。现在的list是一对一的模式,不支持一对多的模式。
pub/sub模式
针对list一对一的模式,pub/sub可以实现一对多的模式。
常见的redis操作命令如下:
命令 | 描述 |
---|---|
PUBLISH channel message | 将消息 message 发送到指定的频道 channel |
SUBSCRIBE channel [channel …] | 订阅一个或多个频道,接收这些频道中发布的消息 |
UNSUBSCRIBE [channel [channel …]] | 取消订阅一个或多个频道 |
PSUBSCRIBE pattern [pattern …] | 订阅一个或多个符合给定模式的频道 |
PUNSUBSCRIBE [pattern [pattern …]] | 取消订阅一个或多个符合给定模式的频道 |
PUBSUB subcommand [argument [argument …]] | 获取关于 Redis Pub/Sub 状态的信息 |
我们在控制台测试一下:
那具体的代码如何实现呢?这里依旧选取的是Java代码作为案例的设计。
- 定义消息发布的接口并实现发送消息的操作
MessagePublisherImpl
- 消息订阅者
messageSubscriberImpl
- 配置类中加上
redisMessageListenerContainer
的bean
- controller测试
服务运行,接口测试一下:
订阅多个topic的话,这样设置:
container.addMessageListener(messageListener, new PatternTopic("pub_channel"));
// 监听多个topic
container.addMessageListener(messageListener, new PatternTopic("pub_channel1"));
ok,貌似这种方式也显得很nice,至少比list的实现方式更nice,那它能解决实际的问题吗?我们总结一下这种方式的优缺点:
优点:
- 实现了多个消费者订阅同一个topic
缺点
-
数据不可靠:Redis 的 pub/sub 模式没有任何持久化机制,如果发布的消息在订阅者还没有收到前发生宕机,那么这些消息将会丢失。因此,如果需要确保数据的可靠性和持久化,需要使用 Redis 的其他数据结构或者使用 Redis 的 AOF 或 RDB 持久化机制。
-
消息不能防止重复消费:Redis 的 pub/sub 模式不支持消息的确认和回调机制,因此,当订阅者收到消息时,无法对其进行确认,也就无法防止重复消费
那有什么好的解决方式呢?stream应需求而生。
stream
Redis 的 Stream 是一个基于时间序列的数据结构,用于存储和处理消息。Stream 可以看作是一个由消息组成的日志,每个消息都有一个唯一的 ID(可以是时间戳或其他方式生成),并且可以对消息进行按照时间的顺序和优先级进行排序。
Stream 可以支持多个消费者,并且可以保证每个消费者只能消费一次。Stream 还可以在一个组内进行消费者间负载均衡,以提高系统的可扩展性和高可用性。
常用的API如下:
API | 描述 |
---|---|
XADD | 向指定的 Stream 中添加一个条目(消息)XADD key ID field string [field string …] |
XDEL | 从指定的 Stream 中删除一个或多个条目 |
XRANGE | 获取指定范围内的条目 |
XREVRANGE | 获取指定范围内的逆序条目 |
XLEN | 获取 Stream 中的条目数量 |
XREAD | 从一个或多个 Stream 中读取待处理的条目 |
XGROUP | 创建、管理和操作消费者组 |
XACK | 确认一个或多个已处理的条目 |
XCLAIM | 批量方式对待处理的条目进行声明和处理 |
XPENDING | 获取待处理的条目信息 |
XTRIM | 删除指定范围之外的条目 |
XINFO | 获取 Stream 的相关信息 |
参考文章:基于Redis的Stream类型的完美消息队列解决方案
添加和读取消息的命令测试如下:
shigen
在敲命令的时候也觉得很繁琐,有点麻烦,还是期待Java代码的api去操作消息队列。
参考文章:redis灵魂拷问:如何使用stream实现消息队列 如何在Springboot中使用Redis5的Stream
- 定义生产消息的
messageProcuder
主要是用来实现消息的发送
- 消息的接受
messageReceiver
实现了消息的ack
- 测试接口
测试中发现了如下错误:
使用stream并不适合用jedis
作为连接池。因为我之前的案例都是基于jedis
的,在这里果断的放弃了。
好了,以上就是《redis实现消息队列》的全部内容了。
与shigen
一起,每天不一样!
相关文章:

redis实现消息队列
背景 消息队列(Message Queue)是一种常见的软件架构模式,用于在分布式系统中传递和处理异步消息。它解耦了发送消息的应用程序和接收消息的应用程序之间的直接依赖关系,使得消息的发送者和接收者可以独立地演化和扩展。 消息队列…...
JVM指令集
概述 JVM,Java Virtual Machine,Java虚拟机器,作为一台独立的机器,一般包括独立的指令集、独立的存储体系以及适合机器自身的运算方式,本章节主要是描述JVM指令的功能与作用。 JVM的每个指令的格式是【指令 操作数1操…...

如何用SSH克隆GitHub项目
诸神缄默不语-个人CSDN博文目录 使用场景:由于不可知的网络问题,无法用HTTPS克隆GitHub项目。 报错fatal: unable to access https://github.com/PolarisRisingWar/llm-throught-ages.git/: GnuTLS recv error (-110): The TLS connection was non-pro…...
sqlx库使用指南
sqlx库使用指南 在项目中我们通常可能会使用database/sql连接MySQL数据库。本文借助使用sqlx实现批量插入数据的例子,介绍了sqlx中可能被你忽视了的sqlx.In和DB.NamedExec方法。 sqlx介绍 在项目中我们通常可能会使用database/sql连接MySQL数据库。sqlx可以认为是Go…...
算法篇汇总
文章浏览 I https://leetcode.cn/problems/article-views-i/description/?envTypestudy-plan-v2&envId30-days-of-pandas&langpythondata 我的题解: import pandas as pddef article_views(views: pd.DataFrame) -> pd.DataFrame:dfviews[views[auth…...

typeScript 学习笔记(二)
类接口 TypeScript 入门教程 (xcatliu.com) 十四.类 ① 类 类:定义了一件事物的抽象特点,包含它的属性和方法对象:类的实例,通过new生成面向对象(OOP)的三大特性:封装、继承、多态封装&…...

redis集群架构详解
一、集群架构搭建 1、配置 在一台机器上模拟多台机器搭建redis集群,一个集群代表一台物理机 集群1路径: /usr/local/redis/redis-cluster/cluster1/9001/redis.conf/usr/local/redis/redis-cluster/cluster1/9004/redis.conf/usr/local/redis/redis-…...
nodejs设置镜像
1、npm镜像地址配置 -- 查看 npm 安装目录 npm root -g-- 查看 npm 配置信息 npm config list-- 查询当前镜像配置 npm get registry-- 或者仅修改 npm 命令镜像 -- 设置为淘宝镜像 npm config set registry https://registry.npmmirror.com -- 修改为官方镜像 npm config set…...
CSS中如何在table中隐藏表格中从第4个开始的多个 <tr> 元素
隐藏指定行 使用 CSS 的 nth-child 选择器来选择表格中的特定行,并隐藏它们。 以下是一个示例 CSS 规则,用于隐藏表格中的第 4 个和第 5 个行(索引从 1 开始): table tr:nth-child(4), table tr:nth-child(5) {displ…...

【类和对象】③友元类
文章目录 1.初始化列表2.static静态成员3.友元 1.初始化列表 我们知道在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。虽然调用构造函数之后,对象中已经有了一个初始值,但是不能将其称为对对象中成…...
算法通关村第十六关:黄金挑战:滑动窗口与堆结合
黄金挑战:滑动窗口与堆结合 堆的大小一般是有限的,能直接返回当前位置下的最大值或者最小值 该特征与滑动窗口结合,可以解决一些特定场景的问题 1. 滑动窗口与堆问题的结合 LeetCode239 https://leetcode.cn/problems/sliding-window-maxi…...

6.2.2 【MySQL】InnoDB中的索引方案
上边之所以称为一个简易的索引方案,是因为我们为了在根据主键值进行查找时使用二分法快速定位具体的目录项而假设所有目录项都可以在物理存储器上连续存储,但是这样做有几个问题: InnoDB 是使用页来作为管理存储空间的基本单位,也…...

划片机实现装片、对准、切割、清洗到卸片的自动化操作
划片机是一种用于切割和分离材料的设备,通常用于光学和医疗、IC、QFN、DFN、半导体集成电路、GPP/LED氮化镓等芯片分立器件、LED封装、光通讯器件、声表器件、MEMS等行业。划片机可以实现从装片、对准、切割、清洗到卸片的自动化操作。 以下是划片机实现这些操作的步…...

OpenCV(二十五):边缘检测(一)
目录 1.边缘检测原理 2.Sobel算子边缘检测 3.Scharr算子边缘检测 4.两种算子的生成getDerivKernels() 1.边缘检测原理 其原理是基于图像中灰度值的变化来捕捉图像中的边界和轮廓。梯度则表示了图像中像素强度变化的强弱和方向。 所以沿梯度方向找到有最大梯度值的像素&…...

上行取消指示 DCI format 2_4
上篇介绍了DCI format 2_1的DL传输中断的内容,这篇就看下DCI format 2_4有关的UL 传输取消机制,值得注意的是这里的UL传输针对的是PUSCH和SRS传输。 UL cancellation DCI format 2_4相关机制引入的背景与DCI format 2_1一样,都是因为URLLC和e…...

百望云蝉联2023「Cloud 100 China 」榜单 综合实力再获认可
9月7日,2023 Cloud 100 China 榜单于上海中心正式发布,榜单由靖亚资本与崔牛会联合推出,百望云凭借着过硬的综合实力与卓越的技术创新能力,再次荣登榜单,位居第六位。 本届评选,Top 100 企业的数据指标的权…...
力扣刷题班第1节:Python语法常遗漏的知识
以下仅仅记录和后面力扣刷题相关的、且平常会遗漏的语法知识。 下面这些笔记都是点到为止,不进行深入解释。大多数学过python的朋友看到就知道什么意思的,我就不解释了 字符串 str "I am a cook"# 按照空格切分 str.split(" ") …...

GET 和 POST请求的区别是什么
GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二。 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。 你轻轻松松的给出了一个“标准答案”: GET在浏览器回退时是无害的…...

Python数据分析实战-表连接-merge四种连接方式用法(附源码和实现效果)
实现功能 表连接-merge四种连接方式用法, 将两个pandas表根据一个或者多个键(列)值进行连接。 实现代码 import pandas as pddf1 pd.DataFrame({key: [a, b, d],data1: range(3)}) print(df1)df2 pd.DataFrame({key: [a, b, c, a, b],dat…...

NFTScan 浏览器再升级:优质数据服务新体验来袭
当前,高质量的 NFT 数据服务已成为区块链用户和开发者的必需。为满足用户数据需求,NFTScan 主站近日进行全面升级,优化了数据服务板块的页面结构,实现更清晰简洁的布局和交互。 NFTScan 的改版充分考虑用户和开发者的数据体验&am…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...