redis stream轻量级消息队列

redis 5.0 之后新推出了stream数据结构,可以实现一个轻量级的消息队列,下面通过自定义注解和springboot使用一下redis stream

1.自定义注解

java 复制代码
@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MsgStreamListener
{
    String stream();

    String group();

    String name();
}

2.定义一个listener的父类,让所有的消费者继承该类

java 复制代码
public abstract class AbstractMsgListener {
}

3.将我们的所有消费者注册到redis stream的listener中

java 复制代码
@Configuration
public class RedisConfig {

    @Autowired
    private List<AbstractMsgListener> msgListeners;

    @Autowired
    private RedisTemplate redisTemplate;

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(stringRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(stringRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public StreamMessageListenerContainer<String, ObjectRecord<String,String>> streamMessageListenerContainer(RedisConnectionFactory connectionFactory)
    {
        StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String,String>> options =
                StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
                        .pollTimeout(java.time.Duration.ofSeconds(1))
                        .targetType(String.class)
                        .build();

        //创建监听redis流的消息监听容器
        StreamMessageListenerContainer<String, ObjectRecord<String,String>> listenerContainer =
                StreamMessageListenerContainer.create(connectionFactory, options);

        //找到所有继承AbstractMsgService的类

        for (AbstractMsgListener service : msgListeners) {

            for (Method method : service.getClass().getMethods()) {

                if(method.isAnnotationPresent(MsgStreamListener.class)){

                    MsgStreamListener annotation = method.getAnnotation(MsgStreamListener.class);

                    String stream = annotation.stream();
                    String group = annotation.group();
                    String name = annotation.name();

                    StreamListener<String,ObjectRecord<String,String>> listener = (StreamListener<String, ObjectRecord<String,String>>) message -> {
                        try {
                            method.invoke(service,message);
                        }catch (Exception e){

                        }
                    };

                    //创建redis流的消息监听器
                    listenerContainer.receive(Consumer.from(group,name),
                            StreamOffset.create(stream, ReadOffset.lastConsumed()),
                            listener);

                    initializeStream(stream,name);
                }

            }
        }

        listenerContainer.start();

        return listenerContainer;
    }

    public void initializeStream(String stream,String group) {

        StreamOperations<String, Object, Object> streamOperations = redisTemplate.opsForStream();

        // 创建一个流
        try {
            streamOperations.createGroup(stream, ReadOffset.from("0"), group);
        } catch (Exception e) {
            // 流可能已存在,忽略异常
        }
    }
}

4.生产者生产消息

java 复制代码
@GetMapping("/add")
    public ResponseEntity<String> addApplication() {
        // 保存应用逻辑...

        // 发布消息到 Redis Stream
        Map<String, Object> messageData = new HashMap<>();
        messageData.put("app", "Hello World");
        RecordId applications_stream = redisTemplate.opsForStream().add("applications_stream", messageData);
        return ResponseEntity.ok("Application added successfully");
    }

5.消费者消费消息,需要继承上面定义的listener的抽象类

java 复制代码
@Component
public class Consumer extends AbstractMsgListener {

    @MsgStreamListener(group = "test1",name = "test1",stream = "applications_stream")
    public void onMessage(ObjectRecord<String, String> message)
    {
        String stream = message.getStream();
        String s = message.getId().toString();
        String value = message.getValue();

        System.out.printf("receive test1 msg stream:%s msgId:%s msgBody:%s",stream,s,value);
    }

}
相关推荐
庄周的大鱼14 分钟前
分析@TransactionalEventListener注解失效
java·spring·springboot·事务监听器·spring 事件机制·事务注解失效解决
史蒂芬_丁35 分钟前
C++深度拷贝例子
java·开发语言·c++
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【4】ReAct 范式与 ReactAgent 核心设计
java·人工智能·spring
「QT(C++)开发工程师」1 小时前
C++11三大核心特性深度解析:类型特征、时间库与原子操作
java·c++·算法
乐分启航1 小时前
SliMamba:十余K参数量刷新SOTA!高光谱分类的“降维打击“来了
java·人工智能·深度学习·算法·机器学习·分类·数据挖掘
yoothey2 小时前
Java字节流与字符流核心笔记(问答+考点复盘)
java·开发语言·笔记
black方块cxy3 小时前
实现一个输入框多个ip以逗号分隔最多20组,且ip不能重复
java·服务器·前端
eggwyw3 小时前
MySQL 与 Redis 的数据一致性问题
数据库·redis·mysql
23.3 小时前
【Java】char字符类型的UTF-16编码解析
java·开发语言·面试
怒放吧德德3 小时前
Spring Boot实战:InfluxDB 2.x简单教程
java·spring boot·后端