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

}
相关推荐
liyunlong-java1 分钟前
敏感词过滤完整指南(DFA 字典树)
java·开发语言·spring
专业程序开发源2 分钟前
SSM笔记本在线销售系统32649-计算机课程设计、毕业设计
java·spring boot·后端·python·django·php·课程设计
FYKJ_20109 分钟前
express绿叶横店短剧推荐与影评分享平台34219-计算机课程设计、毕业设计
java·javascript·vue.js·spring boot·后端·课程设计·express
步行cgn10 分钟前
Spring 事务属性详解
java·数据库·spring
西凉的悲伤12 分钟前
MyBatis 中 association 与 collection 详解
java·mybatis
code2cat25 分钟前
Java进阶篇之AtomicLongArray:大数累加、上限检查与溢出边界
java·开发语言
开开心心_Every30 分钟前
电脑OCR识别软件支持表格识别图片转Excel
java·开发语言·微信·计算机外设·ocr·intellij-idea·excel
弯_弯31 分钟前
企业知识库 RAG 落地:从 Demo 到生产,四条工程链路与参数级自查
android·java·javascript
rannn_11141 分钟前
JVM 垃圾回收面试题全解析:从基础到进阶
java·开发语言·jvm·后端·面试