Redis Stream消息队列使用

Redis命令

生产消息

127.0.0.1:6379> XADD stream:queue * name Jobs
"1712041645839-0"

拉取消息

127.0.0.1:6379> XREAD COUNT 5 STREAMS stream:queue 0-0
1) 1) "stream:queue"
   2) 1) 1) "1712046205751-0"
         2) 1) "name"
            2) "Jobs"

阻塞式拉取消息

127.0.0.1:6379> XREAD COUNT 5 BLOCK 0 STREAMS stream:queue 0-0
1) 1) "stream:queue"
   2) 1) 1) "1712046321183-0"
         2) 1) "name"
            2) "Jobs"

发布订阅

127.0.0.1:6379> XGROUP CREATE stream:queue stream:queue:group 0-0
OK

消费

127.0.0.1:6379> XREADGROUP GROUP stream:queue:group C-1 COUNT 5 BLOCK 0 STREAMS stream:queue >
1) 1) "stream:queue"
   2) 1) 1) "1712046452492-0"
         2) 1) "name"
            2) "Jobs"

SpringBoot集成

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

配置属性

spring.redis.database=0
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379
spring.redis.connect-timeout=5s
spring.redis.timeout=5s
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms

自定义RedisTemplate

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import static org.springframework.data.redis.serializer.StringRedisSerializer.UTF_8;

@Configuration
public class AppConfig {

    @Bean
    RedisTemplate<String, Object> provideRedisTemplate(final RedisConnectionFactory connectionFactory) {
        final RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        final GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
        serializer.configure(mapper -> {
            // 将日期序列化为可读字符串
            mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            // 注册模块
            mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
        });
        template.setKeySerializer(UTF_8);
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(UTF_8);
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }

}

绑定队列、消费者

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.ObjectRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StreamOperations;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
import org.springframework.stereotype.Component;

import java.time.Duration;

@Component
public class RedisStreamMessageQueueRunner implements ApplicationRunner, DisposableBean {

    private static final String STREAM_KEY = "stream:queue";
    private static final String STREAM_QUEUE_GROUP = "stream:queue:group";

    private final RedisTemplate<String, Object> redisTemplate;

    private StreamMessageListenerContainer<String, ObjectRecord<String, String>> container;

    public RedisStreamMessageQueueRunner(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void destroy() throws Exception {
        this.container.stop();
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        final StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> options = StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
                .pollTimeout(Duration.ZERO)
                .targetType(String.class)
                .build();
        final StreamMessageListenerContainer<String, ObjectRecord<String, String>> streamMessageListenerContainer = StreamMessageListenerContainer.create(
                this.redisTemplate.getConnectionFactory(),
                options);

        streamMessageListenerContainer.receive(
                Consumer.from(STREAM_QUEUE_GROUP, "消费者-1"),
                StreamOffset.create(STREAM_KEY, ReadOffset.lastConsumed()),
                new StreamMessageListener1(this.redisTemplate)
        );

        this.container = streamMessageListenerContainer;
        this.container.start();
    }

    private static class StreamMessageListener1 implements StreamListener<String, ObjectRecord<String, String>> {

        private final Logger log = LoggerFactory.getLogger(StreamMessageListener1.class);
        private final RedisTemplate<String, Object> redisTemplate;

        public StreamMessageListener1(RedisTemplate<String, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }

        @Override
        public void onMessage(ObjectRecord<String, String> message) {
            log.info("来自STREAM的消息 message.getId() => {}", message.getId());
            log.info("来自STREAM的消息 message.getStream() => {}", message.getStream());
            log.info("来自STREAM的消息 message.getValue() => {}", message.getValue());

            final StreamOperations<String, Object, Object> streamOperations = this.redisTemplate.opsForStream();
            streamOperations.acknowledge(STREAM_KEY, STREAM_QUEUE_GROUP, message.getId());
            streamOperations.delete(STREAM_KEY, message.getId());
        }
    }

}

Redis相关数据初始化

服务启动前先创建消费组,否则启动服务时会出错

127.0.0.1:6379> XADD stream:queue * name Jobs
"1712046994392-0"
127.0.0.1:6379> XDEL stream:queue 1712046994392-0
(integer) 1
127.0.0.1:6379> XGROUP CREATE stream:queue stream:queue:group 0-0
OK

生产消息放入队列

  redisTemplate.opsForStream().add(ObjectRecord.create("stream:queue", UUID.randomUUID().toString()));
posted @ 2024-04-02 16:49  Bruce.Chang.Lee  阅读(258)  评论(0)    收藏  举报