|
@@ -1,15 +1,21 @@
|
|
|
package cn.iocoder.dashboard.framework.redis.config;
|
|
|
|
|
|
+import cn.hutool.core.net.NetUtil;
|
|
|
import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
|
|
+import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.context.annotation.Bean;
|
|
|
import org.springframework.context.annotation.Configuration;
|
|
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
|
|
+import org.springframework.data.redis.connection.stream.*;
|
|
|
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.serializer.RedisSerializer;
|
|
|
+import org.springframework.data.redis.stream.StreamMessageListenerContainer;
|
|
|
+import org.springframework.util.ErrorHandler;
|
|
|
|
|
|
+import java.time.Duration;
|
|
|
import java.util.List;
|
|
|
|
|
|
/**
|
|
@@ -48,4 +54,52 @@ public class RedisConfig {
|
|
|
return container;
|
|
|
}
|
|
|
|
|
|
+ @Bean(initMethod = "start", destroyMethod = "stop")
|
|
|
+ public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(
|
|
|
+ RedisConnectionFactory factory, List<AbstractStreamMessageListener<?>> listeners) {
|
|
|
+ // 创建配置对象
|
|
|
+ StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>>
|
|
|
+ streamMessageListenerContainerOptions = StreamMessageListenerContainer.StreamMessageListenerContainerOptions
|
|
|
+ .builder()
|
|
|
+ // 一次性最多拉取多少条消息
|
|
|
+ .batchSize(10)
|
|
|
+ // 执行消息轮询的执行器
|
|
|
+ // .executor(this.threadPoolTaskExecutor)
|
|
|
+ // 消息消费异常的handler
|
|
|
+ .errorHandler(new ErrorHandler() {
|
|
|
+ @Override
|
|
|
+ public void handleError(Throwable t) {
|
|
|
+ // throw new RuntimeException(t);
|
|
|
+ t.printStackTrace();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ // 超时时间,设置为0,表示不超时(超时后会抛出异常)
|
|
|
+ .pollTimeout(Duration.ZERO)
|
|
|
+ // 序列化器
|
|
|
+ .serializer(RedisSerializer.string())
|
|
|
+ .targetType(String.class)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 根据配置对象创建监听容器对象
|
|
|
+ StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = StreamMessageListenerContainer
|
|
|
+ .create(factory, streamMessageListenerContainerOptions);
|
|
|
+
|
|
|
+ RedisTemplate<String, Object> redisTemplate = redisTemplate(factory);
|
|
|
+
|
|
|
+ // 使用监听容器对象开始监听消费(使用的是手动确认方式)
|
|
|
+ String consumerName = NetUtil.getLocalHostName(); // TODO 需要优化下,晚点参考下 rocketmq consumer 的
|
|
|
+ for (AbstractStreamMessageListener<?> listener : listeners) {
|
|
|
+ try {
|
|
|
+ redisTemplate.opsForStream().createGroup(listener.getStreamKey(), listener.getGroup());
|
|
|
+ } catch (Exception ignore) {
|
|
|
+// ignore.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ container.receive(Consumer.from(listener.getGroup(), consumerName),
|
|
|
+ StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed()), listener);
|
|
|
+ }
|
|
|
+
|
|
|
+ return container;
|
|
|
+ }
|
|
|
+
|
|
|
}
|