Redis5 Stream

743 阅读1分钟

Redis5 Stream:官方介绍:

redis.io/topics/stre…

SpringBoot整合

一、POM.xml

  <!-- redis -->
        <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
        host: 127.0.0.1
        port: 6379
        password:
        timeout: 2000
        lettuce:
          pool:
            max-active: 8
            max-wait: -1
            max-idle: 8
            min-idle: 0

三、使用StringRedisTemplate往Stream推送消息:


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void sendExchangeMsg(TestDto testDto) {
        // 组装发送消息参数
        try {
            ObjectRecord objectRecord = ObjectRecord.create(ContainerKey.STEAM_KEY, testDto);
            RecordId recordId = this.stringRedisTemplate.opsForStream().add(objectRecord);
         recordId);
        }catch (Exception ex){
            log.error("发送消息异常, testDto={}", testDto, ex);
        }
    }

四、从Stream消费消息

OrderMsgListener 使用 ApplicationRnner,在系统启动以后,初始化监听器。开始监听消费。

@Slf4j
@Component
public class RedisStreamRunner implements ApplicationRunner, DisposableBean {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private OrderMsgListener orderMessageListener;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${server.port}")
    private String port;

    private StreamMessageListenerContainer<String, ObjectRecord<String, TestDto>> streamMessageListenerContainer;

    private StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, TestDto>> options() {
        return StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
                .batchSize(400)
                .executor(this.threadPoolTaskExecutor)
                .errorHandler(t -> log.error("redis msg listener error, e:{}", t.getMessage()))
                .pollTimeout(Duration.ZERO)
                .serializer(new StringRedisSerializer())
                .targetType(TestDto.class)
                .build();
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 初始化容器
        streamMessageListenerContainer = StreamMessageListenerContainer.create(this.redisConnectionFactory, options());
        String streamKey = ContainerKey.STEAM_KEY;
        String groupId = ContainerKey.STEAM_GROOP;
        String consumerName = Inet4Address.getLocalHost().getHostName() + ":" + port;
        log.info("streamKey:{},groupId:{},consumerName{}",streamKey,groupId,consumerName);
        // 判断 stream 是否初始化,未初始化则进行初始化
        if (!Boolean.TRUE.equals(stringRedisTemplate.hasKey(streamKey))) {
            // 往 stream 发送消息,会自动创建 stream
            RecordId recordId = stringRedisTemplate.opsForStream().add(streamKey, Collections.singletonMap("testKey", "testV"));
            // 创建 消费者组
            stringRedisTemplate.opsForStream().createGroup(streamKey, groupId);
            // 删除创建
            stringRedisTemplate.opsForStream().delete(streamKey, recordId);
        }
        // 使用监听容器监听消息,并且自动应答
        streamMessageListenerContainer.receiveAutoAck(
                Consumer.from(groupId, consumerName),
                StreamOffset.create(streamKey, ReadOffset.lastConsumed()),
                orderMessageListener);
        // 启动监听
        this.start();
    }

    /**
     * 订阅容器启动
     */
    public void start() {
        streamMessageListenerContainer.start();
    }

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

OrderMsgListener 实现函数接口 StreamListener<K, V extends Record<K, ?>> ,来自定义消息的消费实现

@Slf4j
@Component
public class OrderMsgListener implements StreamListener<String, ObjectRecord<String, TesyDto>> {


    @Autowired
    private IOrderService iOrderService;

    @Override
    public void onMessage(ObjectRecord<String, TestDto> testDto) {
        // 消息ID
//        RecordId messageId = testDto.getId();
        // 消息的key和value
        TestDto testDto = testDto.getValue();
        try {
            iOrderService.paySuccess(testDto);
        }catch (Exception e){
            log.error("消息处理异常订单号:{},异常信息为{}",testDto.getOrderNo(),e.getMessage());
        }
    }
}