All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xyz.migoo.framework.mq.config.MQAutoConfiguration Maven / Gradle / Ivy

package xyz.migoo.framework.mq.config;

import cn.hutool.system.SystemUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
import xyz.migoo.framework.mq.core.pubsub.AbstractChannelMessageListener;
import xyz.migoo.framework.mq.core.stream.AbstractStreamMessageListener;
import xyz.migoo.framework.redis.config.RedisAutoConfiguration;

import java.util.List;

/**
 * @author xiaomi
 * Created on 2021/11/21 14:13
 */
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Slf4j
public class MQAutoConfiguration {

    /**
     * 创建 Redis Pub/Sub 广播消费的容器
     */
    @Bean(initMethod = "start", destroyMethod = "stop")
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory factory, List> listeners) {
        // 创建 RedisMessageListenerContainer 对象
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        // 设置 RedisConnection 工厂。
        container.setConnectionFactory(factory);
        // 添加监听器
        listeners.forEach(listener -> {
            container.addMessageListener(listener, new ChannelTopic(listener.getChannel()));
            log.info("[redisMessageListenerContainer][注册 Channel({}) 对应的监听器({})]",
                    listener.getChannel(), listener.getClass().getName());
        });
        return container;
    }

    /**
     * 创建 Redis Stream 集群消费的容器
     * 

* Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html */ @Bean(initMethod = "start", destroyMethod = "stop") public StreamMessageListenerContainer> redisStreamMessageListenerContainer( RedisTemplate redisTemplate, List> listeners) { // 第一步,创建 StreamMessageListenerContainer 容器 // 创建 options 配置 StreamMessageListenerContainer.StreamMessageListenerContainerOptions> containerOptions = StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder() // 一次性最多拉取多少条消息 .batchSize(10) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化 .targetType(String.class) .build(); // 创建 container 对象 StreamMessageListenerContainer> container = StreamMessageListenerContainer.create( redisTemplate.getRequiredConnectionFactory(), containerOptions); // 第二步,注册监听器,消费对应的 Stream 主题 String consumerName = buildConsumerName(); // String consumerName = "110"; listeners.forEach(listener -> { // 创建 listener 对应的消费者分组 try { redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup()); } catch (Exception ignore) { } // 设置 listener 对应的 redisTemplate listener.setRedisTemplate(redisTemplate); // 创建 Consumer 对象 Consumer consumer = Consumer.from(listener.getGroup(), consumerName); // 设置 Consumer 消费进度,以最小消费进度为准 StreamOffset streamOffset = StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed()); // 设置 Consumer 监听 StreamMessageListenerContainer.StreamReadRequestBuilder builder = StreamMessageListenerContainer.StreamReadRequest .builder(streamOffset).consumer(consumer) // 不自动 ack .autoAcknowledge(false) // 默认配置,发生异常就取消消费,显然不符合预期;因此,我们设置为 false .cancelOnError(throwable -> false); container.register(builder.build(), listener); log.info("[redisStreamMessageListenerContainer][注册 Stream({}) 对应的监听器({})]", listener.getStreamKey(), listener.getClass().getName()); }); return container; } /** * 构建消费者名字,使用本地 IP + 进程编号的方式。 * 参考自 RocketMQ clientId 的实现 * * @return 消费者名字 */ private static String buildConsumerName() { return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy