RedisTemplate-opsForStream实现消息队列,主要演示 xgroup,xreadgroup,xpending,xack,xinfo的用法

stream 更多详细命令使用,可查看博文
redis基于Stream类型实现消息队列,命令操作,术语概念,个人总结等-CSDN博客

1 springboot整合redis 就不多说了
2 有用到hutool工具类,添加下 pom 依赖

<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
            <scope>compile</scope>
 </dependency>

3 写个pojo类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class SeckillOrder {

    private String userId;
    private String goodsId;
    private String orderId;

}

4 往redis 写入些测试数据,如下

del order_stream

keys *

# 往消息队列添加消息
xadd order_stream * userId user_01, goodsId goodsId_01, orderId order_01
xadd order_stream * userId user_02, goodsId goodsId_02, orderId order_02
xadd order_stream * userId user_03, goodsId goodsId_03, orderId order_03
xadd order_stream * userId user_04, goodsId goodsId_04, orderId order_04
xadd order_stream * userId user_05, goodsId goodsId_05, orderId order_05
xadd order_stream * userId user_06, goodsId goodsId_06, orderId order_06
xadd order_stream * userId user_07, goodsId goodsId_07, orderId order_07
xadd order_stream * userId user_08, goodsId goodsId_08, orderId order_08
xadd order_stream * userId user_09, goodsId goodsId_09, orderId order_09
xadd order_stream * userId user_10, goodsId goodsId_10, orderId order_10

# 获取流中的数据(- 表示最小值,+表示最大值)
xrange order_stream - +

创建g1消费者组,从头开始消费
XGROUP CREATE order_stream g1 0-0

创建g2消费者组,从尾部开始消费
XGROUP CREATE order_stream g2 $

# 通过 xreadgroup 的一个消费者(consumer1)读取流(order_stream)中的1条消息
xreadgroup group g1 consumer1 count 1 streams order_stream >

# 显示g1消费者组 待处理消息的相关信息
xpending order_stream g1

# 打印流信息
xinfo stream order_stream
# 打印消费组信息
xinfo groups order_stream

5 核心类,主要演示 xreadgroup,xpending,xack的用法

import cn.hutool.core.bean.BeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.example.service_a.domain.SeckillOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;


@Component
@Slf4j
public class Redis_Stream_Test {

    @Autowired
    private StringRedisTemplate redisTemplate;

    // 定义流的键名
    public static final String stream_key = "order_stream";
    // 定义消费者组名
    public static final String group_str = "g1";
    // 定义消费者名称
    public static final String consumer_str = "c1";

    /**
     * 消费流中的消息
     * 该方法会不断读取指定流中的消息并处理,直到没有消息为止。
     * 用到命令 xreadgroup
     * 每个消费者组执行这个方法后,假如队列有5条消息,第1次执行,就返回5条消息,再次执行此方法,就返回为空
     * 如果还想读这5条消息,就换个消费者组名称就可以了
     */
    @EnableScheduling //添加到主启动类上
    @Scheduled(cron="0/5 * *  * * ? ")   //每5秒执行一次
    public void read_message() {
        try {
            if (!redisTemplate.hasKey(stream_key)) {
                return;
            }
            // 检查是否已创建消费者组,若未创建则创建
            Set<StreamInfo.XInfoGroup> collect = redisTemplate.opsForStream().groups(stream_key)
                    .stream().filter(infoGroup -> infoGroup.groupName().equals(group_str)).collect(Collectors.toSet());
            if (collect.isEmpty()) {
                // 创建消费者组,****从头开始读****
                // XGROUP CREATE order_stream g1 0-0
                String group = redisTemplate.opsForStream().createGroup(stream_key, ReadOffset.from("0-0"), group_str);
                log.info("消费者组添加成功,group = {}", group);
            }

            // 读取消息,可以不阻塞读,也可以阻塞方式来读 如 xreadgroup group g1 c1 count 5 block 60000 order_stream >
            List<MapRecord<String, Object, Object>> read = redisTemplate.opsForStream().read(
                    Consumer.from(group_str, consumer_str),
                    StreamReadOptions.empty().count(5),
                    StreamOffset.create(stream_key, ReadOffset.lastConsumed())
            );

            // 若无消息则返回
            if (read.isEmpty()) {
                return;
            }

            // 记录读取到的消息数量
            log.info("读取到消息,size = {}", read.size());
            // 处理每条消息
            read.forEach(mapRecord -> {
                // 记录消息ID和内容
                RecordId recordId = mapRecord.getId();
                Map<Object, Object> value = mapRecord.getValue();
                log.info("消息id = {}", mapRecord.getId());
                log.info("消息内容 = {}", mapRecord.getValue());
                // 将消息内容转换为实体对象,用hutool
                SeckillOrder seckillOrder = BeanUtil.fillBeanWithMap(value, new SeckillOrder(), false);
                log.info("消息内容转成实体查看 = {}", seckillOrder);
                // 进行业务逻辑处理等
            });
        } catch (Exception e) {
            // 记录读取消息失败的日志
            log.error("读取消息失败,e = {}", e);
        }
    }

    /**
     * 确认消息处理完成
     * 该方法会读取并确认指定消费者组下指定消费者处理完成的消息。
     * 用到命令 xpending , xack
     */
    public void ack_message() {
        try {
            // 获取待处理的消息
            PendingMessages pending = redisTemplate.opsForStream().pending(
                    stream_key,
                    Consumer.from(group_str, consumer_str)
            );
            log.info("待处理消息 = {}", pending);

            // 遍历并确认每条待处理消息
            pending.forEach(pendingMessage -> {
                // 记录待处理消息的元数据
                log.info("待处理消息元数据 = {}", pendingMessage);
                RecordId recordId = pendingMessage.getId();
                log.info("待处理消息id = {}", recordId);
                // 提交消息确认
                redisTemplate.opsForStream().acknowledge(stream_key, group_str, recordId);
                log.info("消息提交ack成功,id = {}", recordId);
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 消费者组 ,消费者,队列的信息
     */
    public void xInfo() {

        StreamInfo.XInfoGroups groups = redisTemplate.opsForStream().groups(stream_key);
        log.info("获取组的信息:{}", groups);
        System.out.println();

        StreamInfo.XInfoConsumers consumers = redisTemplate.opsForStream().consumers(stream_key, group_str);
        log.info("获取消费者的信息:{}", consumers);
        System.out.println();

        StreamInfo.XInfoStream infoStream = redisTemplate.opsForStream().info(stream_key);
        log.info("获取流的信息:{}", infoStream);
    }
}
相关推荐
林的快手14 分钟前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
weisian1511 小时前
Redis篇--常见问题篇8--缓存一致性3(注解式缓存Spring Cache)
redis·spring·缓存
向阳12181 小时前
mybatis 缓存
java·缓存·mybatis
HEU_firejef1 小时前
Redis——缓存预热+缓存雪崩+缓存击穿+缓存穿透
数据库·redis·缓存
上等猿1 小时前
函数式编程&Lambda表达式
java
蓝染-惣右介1 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
秋恬意2 小时前
IBatis和MyBatis在细节上的不同有哪些
java·mybatis
weisian1512 小时前
Redis篇--常见问题篇7--缓存一致性2(分布式事务框架Seata)
redis·分布式·缓存
白云coy2 小时前
Redis 安装部署[主从、哨兵、集群](linux版)
linux·redis