Redis实现简易消息队列的三种方式
消息队列简介
消息队列是一种用于在计算机系统中传递和处理数据的重要工具。如果你完全不了解消息队列,不用担心,我将尽力以简单明了的方式来解释它。
首先,想象一下你正在玩一个游戏,而游戏中有很多任务需要完成,但你不需要立刻完成它们。相反,你可以将这些任务列在一个清单上,然后按照你的时间表逐个完成。消息队列就像这个任务清单一样,只不过它用于计算机程序之间传递任务和数据。
消息队列的基本原理是,一个程序可以将一条消息(或任务)发送到队列中,然后另一个程序可以从队列中取出并处理这条消息。这种方式有几个关键优点:
-  
异步通信:消息队列允许程序之间进行异步通信,也就是说,发送消息的程序不需要等待接收消息的程序立刻响应,它们可以继续执行其他任务。
 -  
解耦合:消息队列有助于解耦合(解除程序之间的依赖关系)。发送消息的程序不需要知道哪个程序将接收消息,而接收消息的程序只需要关注如何处理消息,而不需要担心消息的来源。
 -  
缓冲和负载均衡:消息队列可以用作缓冲区,帮助应对高负载情况。如果一个程序在某个时刻发送了大量消息,接收消息的程序可以按照自己的速度处理这些消息,而不会因为消息过多而崩溃。
 -  
数据持久化:有些消息队列系统还具有数据持久化功能,这意味着消息不会丢失,即使系统发生故障,消息也可以在恢复后重新处理。
 -  
可伸缩性:消息队列是构建分布式系统的重要工具,因为它们可以帮助应对不断增长的负载,而无需重新设计整个系统。
 
最常见的消息队列系统之一是RabbitMQ、Apache Kafka和Amazon SQS等。它们在不同情境下有不同的应用,但基本原理是相似的:它们都允许程序之间以异步、松散耦合的方式交换信息,从而提高系统的可伸缩性、可靠性和性能。
总之,消息队列是一种非常有用的工具,用于在计算机系统中传递和处理数据,帮助程序更高效地协同工作,提供了许多优点,包括异步通信、解耦合、缓冲、数据持久化和可伸缩性。希望这个简单的解释能帮助你理解消息队列的基本概念。
什么是消息队列:字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色:
- 消息队列:存储和管理消息,也被称为消息代理(Message Broker)
 - 生产者:发送消息到消息队列
 - 消费者:从消息队列获取消息并处理消息
 
Redis消息队列-基于List实现消息队列
消息队列(Message Queue),字面意思就是存放消息的队列。而Redis的list数据结构是一个双向链表,很容易模拟出队列效果。
队列是入口和出口不在一边,因此我们可以利用:LPUSH 结合 RPOP、或者 RPUSH 结合 LPOP来实现。
 不过要注意的是,当队列中没有消息时RPOP或LPOP操作会返回null,并不像JVM的阻塞队列那样会阻塞并等待消息。因此这里应该使用BRPOP或者BLPOP来实现阻塞效果。
关于redis list的操作说明:
BLPOP key [key ...] timeout
  summary: Remove and get the first element in a list, or block until one is available
  since: 2.0.0
  BRPOP key [key ...] timeout
  summary: Remove and get the last element in a list, or block until one is available
  since: 2.0.0
  BRPOPLPUSH source destination timeout
  summary: Pop a value from a list, push it to another list and return it; or block until one is available
  since: 2.2.0
  BLPOP key [key ...] timeout
  summary: Remove and get the first element in a list, or block until one is available
  since: 2.0.0
  BRPOP key [key ...] timeout
  summary: Remove and get the last element in a list, or block until one is available
  since: 2.0.0
  LLEN key
  summary: Get the length of a list
  since: 1.0.0
  LPOP key
  summary: Remove and get the first element in a list
  since: 1.0.0
  LPUSH key value [value ...]
  summary: Prepend one or multiple values to a list
  since: 1.0.0
  LPUSHX key value
  summary: Prepend a value to a list, only if the list exists
  since: 2.2.0
  RPOP key
  summary: Remove and get the last element in a list
  since: 1.0.0
  RPOPLPUSH source destination
  summary: Remove the last element in a list, prepend it to another list and return it
  since: 1.2.0
  RPUSH key value [value ...]
  summary: Append one or multiple values to a list
  since: 1.0.0
  RPUSHX key value
  summary: Append a value to a list, only if the list exists
  since: 2.2.0
 
更多详细内容通过命令
help @list
 
查看
通过list的演示代码
127.0.0.1:6379> rpush list1 1 2 3 
(integer) 3
127.0.0.1:6379> lpop list1
"1"
127.0.0.1:6379> lpop list1
"2"
127.0.0.1:6379> lpop list1
"3"
 
阻塞演示:
一个生产者,两个消费者

让两个消费者获取消息
brpop list 123  
 
此时两个消费者都进入阻塞模式

现在生产消息
lpush list test
 
结果:

从图中可以看出,生产者进行了两次生产,消费者只能一个一个的获取消息。
基于List的消息队列有哪些优缺点?
 优点:
- 利用Redis存储,不受限于JVM内存上限
 - 基于Redis的持久化机制,数据安全性有保证
 - 可以满足消息有序性
 
缺点:
- 无法避免消息丢失 (生产者处理消息时丢失,而在List中已经被删除)
 - 只支持单消费者
 
Redis 消息队列-基于PubSub的消息队列
PubSub(发布订阅)是Redis2.0版本引入的消息传递模型。顾名思义,消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。
SUBSCRIBE channel [channel] :订阅一个或多个频道
 PUBLISH channel msg :向一个频道发送消息
 PSUBSCRIBE pattern[pattern] :订阅与pattern格式匹配的所有频道
使用简介:
 PSUBSCRIBE pattern [pattern ...]
  summary: Listen for messages published to channels matching the given patterns
  since: 2.0.0
  PUBLISH channel message
  summary: Post a message to a channel
  since: 2.0.0
  PUBSUB subcommand [argument [argument ...]]
  summary: Inspect the state of the Pub/Sub subsystem
  since: 2.8.0
  PUNSUBSCRIBE [pattern [pattern ...]]
  summary: Stop listening for messages posted to channels matching the given patterns
  since: 2.0.0
  SUBSCRIBE channel [channel ...]
  summary: Listen for messages published to the given channels
  since: 2.0.0
  UNSUBSCRIBE [channel [channel ...]]
  summary: Stop listening for messages posted to the given channels
  since: 2.0.0
 
使用结果

基于PubSub的消息队列有哪些优缺点?
 优点:
- 采用发布订阅模型,支持多生产、多消费
 
缺点:
- 不支持数据持久化
 - 无法避免消息丢失
 - 消息堆积有上限,超出时数据丢失
 
Redis 消息队列-基于Stream的消息队列
下面是添加消息的方法,其中key是指定stream的关键字,id是唯一的,如果输入*的话由redis自动生成,然后就是添加field string,键值对了
XADD key ID field string [field string ...]
summary: Appends a new entry to a stream
since: 5.0.0
 
下面是读消息的方法,其中COUNT可选字段表示读几条消息,BLOCK表示阻塞读取,其中的milliseconds如果为0表示永久,STREAMS就表示你要看的stream的key是什么,然后就是ID了,ID有两个选项一个0表示读取第一条消息$表示读取最新消息,但是这个读取最新消息可能会出现漏读现象。
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
summary: Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.
since: 5.0.0
 

两个消费者同时进入阻塞读取最新消息

可以看到两个同时读取到了

新增多条消息,只能收取到最新消息

STREAM类型消息队列的XREAD命令特点:
优点:
- 消息可回溯
 - 一个消息可以被多个消费者读取
 - 可以阻塞读取
 
缺点:
- 有消息漏读的风险
 
消费者组解决漏读问题
消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。具备下列特点:
- 消息分流 
  
- 队列中的消息会分流给组内的不同消费者,而不是重复消费,从而加快消息处理的速度
 
 - 消息标示 
  
- 消费者组会维护一个标示,记录最后一个被处理的消息,哪怕消费者宕机重启,还会从标示之后读取消息。确保每一个消息都会被消费
 
 - 消息确认 
  
- 消费者获取消息后,消息处于pending状态,并存入一个pending-list。当处理完成后需要通过XACK来确认消息,标记消息为已处理,才会从pending-List移除。
 
 
创建消费者组
XGROUP CREATE key groupName ID [MKSTREAM]
 
- key : 队列名称
 - groupName: 消费者组名称
 - ID: 起始ID标示 , $代表队列中最后一个消息,0则代表队列中第一个消息
 - MKSTREAM: 队列不存在时自动创建队列
 
删除指定的消费者组
XGROUP DESTORY key groupName
 
给指定的消费者组添加消费者(一般自动添加)
XGROUP CREATECONSUMER key groupName consumername
 
删除消费者组中的指定消费者
XGROUP DELCONSUMER key groupname consumername		
 
从消费者组读取消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ……] ID [ID ……]
 
- group : 消费者组名
 - consumer:消费者名称,如果消费者不存在,会自动创建一个消费者
 - count : 本次查询的最大数量
 - BLICK milliseconds: 当没有消息时最长等待时间
 - NOACK: 无需手动ACK(ACK确认消息,从pending-list中移除),胡渠道消息后自动确认
 - STREAMS key : 指定队列名称
 - ID : 获取雄安锡的起始ID 
  
>: 从下一个未消费的消息开始- 其他:根据指定id从pending-list中获取已消费单位确认的消息,例如0,从pending-list中的第一个消息开始。
 
 
确认消息
 XACK key group ID [ID ...]
 
- key : stream 名称
 - group : 消费者组名称
 - id: 就是消息的ID
 
查看pending-list中的消息信息
XPENDING key group [start end count] [consumer]
 
-  
key: stream 名称
 -  
group : group 名称
 -  
start end count : 可以使用
-代表从第一个+代表对后一个这两个配合就是所有,然后用count表示出几个,示例xpending stream1 g1 - + 10 
在java中的逻辑

STREAM类型消息队列的XREADGROUP命令特点:
- 消息可回溯
 - 可以多消费者争抢消息,加快消费速度
 - 可以阻塞读取
 - 没有消息漏读的风险
 - 有消息确认机制,保证消息至少被消费一次
 







![[GXYCTF2019]BabyUpload - 文件上传+绕过(后缀文件类型文件内容.htaccess)](https://img-blog.csdnimg.cn/27c8366ca4ad4afaba9c8b90b2e7a619.png#pic_center)










