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);
    }

}
相关推荐
Java林间1 分钟前
IDEA远程Debug调试
java·ide·intellij-idea
uhakadotcom12 分钟前
Langflow 1.3.0 安全漏洞详解及利用示例,教你如何防范远程代码执行攻击
面试·架构·github
极客先躯17 分钟前
高级java每日一道面试题-2025年4月08日-微服务篇[Nacos篇]-生产环境中部署Nacos的最佳实践有哪些?
java·开发语言·微服务
莓事哒20 分钟前
在IDEA里面建立maven项目(便于java web使用)
java·maven·intellij-idea
小袁拒绝摆烂42 分钟前
分布式锁+秒杀异步优化
redis·分布式
云之兕1 小时前
MyBatis 详解
java·开发语言·mybatis
Katherine_lin1 小时前
JAVA:线程的状态与生命周期
java·开发语言
钮钴禄·爱因斯晨1 小时前
深入理解 Java 内存区域与内存溢出异常
java·开发语言
北辰浮光1 小时前
[SpringMVC]上手案例
java·开发语言
西门吹雪分身2 小时前
Redis之RedLock算法以及底层原理
数据库·redis·算法