项目结构调整 x 20 : 拆分出独立的 yudao-spring-boot-starter-mq 组件,整理更加清晰
This commit is contained in:
parent
8e6fa3db55
commit
0b5662b698
@ -65,6 +65,12 @@
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 服务保障相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
@ -104,6 +110,10 @@
|
||||
<groupId>cn.smallbun.screw</groupId>
|
||||
<artifactId>screw-core</artifactId> <!-- 实现数据库文档 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.infra.mq.consumer.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.apollo.internals.DBConfigRepository;
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.infra.mq.message.config.InfConfigRefreshMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.infra.mq.message.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.infra.mq.producer.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.infra.mq.message.config.InfConfigRefreshMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.dept.SysDeptRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.dict;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.dict.SysDictDataRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.dict.SysDictDataService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.mail;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.stream.AbstractStreamMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.mail.SysMailSendMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.permission.SysMenuRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysMenuService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.permission.SysRoleMenuRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.permission.SysRoleRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysRoleService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsChannelRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.sms.SysSmsChannelService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.stream.AbstractStreamMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsSendMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.sms.SysSmsService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.consumer.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsTemplateRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.service.sms.SysSmsTemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.dict;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.mail;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.stream.StreamMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.stream.StreamMessage;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.redis.core.stream.StreamMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.stream.StreamMessage;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.message.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.producer.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.dept.SysDeptRefreshMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.producer.dict;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.dict.SysDictDataRefreshMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.producer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.permission.SysMenuRefreshMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.producer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.permission.SysRoleMenuRefreshMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.producer.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.permission.SysRoleRefreshMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.adminserver.modules.system.mq.producer.sms;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsChannelRefreshMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsSendMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsTemplateRefreshMessage;
|
||||
|
@ -6,7 +6,7 @@ import cn.iocoder.yudao.adminserver.modules.system.mq.consumer.mail.SysMailSendC
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.consumer.sms.SysSmsSendConsumer;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.mail.SysMailSendMessage;
|
||||
import cn.iocoder.yudao.adminserver.modules.system.mq.message.sms.SysSmsSendMessage;
|
||||
import cn.iocoder.yudao.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.yudao.framework.mq.core.util.RedisMessageUtils;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
@ -193,6 +193,13 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mq</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 服务保障相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
|
@ -15,12 +15,16 @@
|
||||
<module>yudao-spring-boot-starter-redis</module>
|
||||
<module>yudao-spring-boot-starter-web</module>
|
||||
<module>yudao-spring-boot-starter-security</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-monitor</module>
|
||||
<module>yudao-spring-boot-starter-protection</module>
|
||||
<module>yudao-spring-boot-starter-config</module>
|
||||
<module>yudao-spring-boot-starter-job</module>
|
||||
<module>yudao-spring-boot-starter-mq</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-excel</module>
|
||||
<module>yudao-spring-boot-starter-test</module>
|
||||
|
||||
<module>yudao-spring-boot-starter-biz-operatelog</module>
|
||||
<module>yudao-spring-boot-starter-biz-dict</module>
|
||||
<module>yudao-spring-boot-starter-biz-sms</module>
|
||||
|
26
yudao-framework/yudao-spring-boot-starter-mq/pom.xml
Normal file
26
yudao-framework/yudao-spring-boot-starter-mq/pom.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-spring-boot-starter-mq</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${artifactId}</name>
|
||||
<description>消息队列,基于 Redis Pub/Sub 实现广播消费,基于 Stream 实现集群消费</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<dependencies>
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,105 @@
|
||||
package cn.iocoder.yudao.framework.mq.config;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
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.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 java.util.List;
|
||||
|
||||
/**
|
||||
* 消息队列配置类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(YudaoMQAutoConfiguration.class)
|
||||
@Slf4j
|
||||
public class YudaoMQAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 创建 Redis Pub/Sub 广播消费的容器
|
||||
*/
|
||||
@Bean
|
||||
public RedisMessageListenerContainer redisMessageListenerContainer(
|
||||
RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> 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<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(
|
||||
RedisTemplate<String, Object> redisTemplate, List<AbstractStreamMessageListener<?>> listeners) {
|
||||
// 第一步,创建 StreamMessageListenerContainer 容器
|
||||
// 创建 options 配置
|
||||
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions =
|
||||
StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
|
||||
.batchSize(10) // 一次性最多拉取多少条消息
|
||||
.targetType(String.class) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化
|
||||
.build();
|
||||
// 创建 container 对象
|
||||
StreamMessageListenerContainer<String, ObjectRecord<String, String>> 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<String> streamOffset = StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed());
|
||||
// 设置 Consumer 监听
|
||||
StreamMessageListenerContainer.StreamReadRequestBuilder<String> builder = StreamMessageListenerContainer.StreamReadRequest
|
||||
.builder(streamOffset).consumer(consumer)
|
||||
.autoAcknowledge(false) // 不自动 ack
|
||||
.cancelOnError(throwable -> false); // 默认配置,发生异常就取消消费,显然不符合预期;因此,我们设置为 false
|
||||
container.register(builder.build(), listener);
|
||||
});
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建消费者名字,使用本地 IP + 进程编号的方式。
|
||||
* 参考自 RocketMQ clientId 的实现
|
||||
*
|
||||
* @return 消费者名字
|
||||
*/
|
||||
private static String buildConsumerName() {
|
||||
return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.redis.core.pubsub;
|
||||
package cn.iocoder.yudao.framework.mq.core.pubsub;
|
||||
|
||||
import cn.hutool.core.util.TypeUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.redis.core.pubsub;
|
||||
package cn.iocoder.yudao.framework.mq.core.pubsub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.redis.core.stream;
|
||||
package cn.iocoder.yudao.framework.mq.core.stream;
|
||||
|
||||
import cn.hutool.core.util.TypeUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.redis.core.stream;
|
||||
package cn.iocoder.yudao.framework.mq.core.stream;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.framework.redis.core.util;
|
||||
package cn.iocoder.yudao.framework.mq.core.util;
|
||||
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.redis.core.stream.StreamMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.yudao.framework.mq.core.stream.StreamMessage;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import org.springframework.data.redis.connection.stream.RecordId;
|
||||
import org.springframework.data.redis.connection.stream.StreamRecords;
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 消息队列,基于 Redis 提供:
|
||||
* 1. 基于 Pub/Sub 实现广播消费
|
||||
* 2. 基于 Stream 实现集群消费
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.mq;
|
@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.yudao.framework.mq.config.YudaoMQAutoConfiguration
|
@ -1,23 +1,11 @@
|
||||
package cn.iocoder.yudao.framework.redis.config;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.yudao.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.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.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Redis 配置类
|
||||
@ -44,76 +32,4 @@ public class YudaoRedisAutoConfiguration {
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Redis Pub/Sub 广播消费的容器
|
||||
*/
|
||||
@Bean
|
||||
public RedisMessageListenerContainer redisMessageListenerContainer(
|
||||
RedisConnectionFactory factory, List<AbstractChannelMessageListener<?>> 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<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(
|
||||
RedisTemplate<String, Object> redisTemplate, List<AbstractStreamMessageListener<?>> listeners) {
|
||||
// 第一步,创建 StreamMessageListenerContainer 容器
|
||||
// 创建 options 配置
|
||||
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> containerOptions =
|
||||
StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
|
||||
.batchSize(10) // 一次性最多拉取多少条消息
|
||||
.targetType(String.class) // 目标类型。统一使用 String,通过自己封装的 AbstractStreamMessageListener 去反序列化
|
||||
.build();
|
||||
// 创建 container 对象
|
||||
StreamMessageListenerContainer<String, ObjectRecord<String, String>> 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<String> streamOffset = StreamOffset.create(listener.getStreamKey(), ReadOffset.lastConsumed());
|
||||
// 设置 Consumer 监听
|
||||
StreamMessageListenerContainer.StreamReadRequestBuilder<String> builder = StreamMessageListenerContainer.StreamReadRequest
|
||||
.builder(streamOffset).consumer(consumer)
|
||||
.autoAcknowledge(false) // 不自动 ack
|
||||
.cancelOnError(throwable -> false); // 默认配置,发生异常就取消消费,显然不符合预期;因此,我们设置为 false
|
||||
container.register(builder.build(), listener);
|
||||
});
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建消费者名字,使用本地 IP + 进程编号的方式。
|
||||
* 参考自 RocketMQ clientId 的实现
|
||||
*
|
||||
* @return 消费者名字
|
||||
*/
|
||||
private static String buildConsumerName() {
|
||||
return String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user