Commit 497d826ac42a0b7cf2fa7c079fcce437b3bef5d6
Committed by
Gitee
!711 增加 RocketMQ、Kafka、RabbitMQ 消息队列的支持
Merge pull request !711 from 芋道源码/feature/mq-optimize
Showing
57 changed files
with
843 additions
and
229 deletions
yudao-dependencies/pom.xml
| @@ -30,6 +30,8 @@ | @@ -30,6 +30,8 @@ | ||
| 30 | <mybatis-plus-join.version>1.4.7</mybatis-plus-join.version> | 30 | <mybatis-plus-join.version>1.4.7</mybatis-plus-join.version> |
| 31 | <redisson.version>3.18.0</redisson.version> | 31 | <redisson.version>3.18.0</redisson.version> |
| 32 | <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version> | 32 | <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version> |
| 33 | + <!-- 消息队列 --> | ||
| 34 | + <rocketmq-spring.version>2.2.3</rocketmq-spring.version> | ||
| 33 | <!-- 服务保障相关 --> | 35 | <!-- 服务保障相关 --> |
| 34 | <lock4j.version>2.2.5</lock4j.version> | 36 | <lock4j.version>2.2.5</lock4j.version> |
| 35 | <resilience4j.version>1.7.1</resilience4j.version> | 37 | <resilience4j.version>1.7.1</resilience4j.version> |
| @@ -98,11 +100,6 @@ | @@ -98,11 +100,6 @@ | ||
| 98 | </dependency> | 100 | </dependency> |
| 99 | <dependency> | 101 | <dependency> |
| 100 | <groupId>cn.iocoder.boot</groupId> | 102 | <groupId>cn.iocoder.boot</groupId> |
| 101 | - <artifactId>yudao-spring-boot-starter-biz-trade</artifactId> | ||
| 102 | - <version>${revision}</version> | ||
| 103 | - </dependency> | ||
| 104 | - <dependency> | ||
| 105 | - <groupId>cn.iocoder.boot</groupId> | ||
| 106 | <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> | 103 | <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> |
| 107 | <version>${revision}</version> | 104 | <version>${revision}</version> |
| 108 | </dependency> | 105 | </dependency> |
| @@ -260,6 +257,12 @@ | @@ -260,6 +257,12 @@ | ||
| 260 | <version>${revision}</version> | 257 | <version>${revision}</version> |
| 261 | </dependency> | 258 | </dependency> |
| 262 | 259 | ||
| 260 | + <dependency> | ||
| 261 | + <groupId>org.apache.rocketmq</groupId> | ||
| 262 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | ||
| 263 | + <version>${rocketmq-spring.version}</version> | ||
| 264 | + </dependency> | ||
| 265 | + | ||
| 263 | <!-- 服务保障相关 --> | 266 | <!-- 服务保障相关 --> |
| 264 | <dependency> | 267 | <dependency> |
| 265 | <groupId>cn.iocoder.boot</groupId> | 268 | <groupId>cn.iocoder.boot</groupId> |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/core/KeyValue.java
| @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; | @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | import lombok.NoArgsConstructor; | 5 | import lombok.NoArgsConstructor; |
| 6 | 6 | ||
| 7 | +import java.io.Serializable; | ||
| 8 | + | ||
| 7 | /** | 9 | /** |
| 8 | * Key Value 的键值对 | 10 | * Key Value 的键值对 |
| 9 | * | 11 | * |
| @@ -12,7 +14,7 @@ import lombok.NoArgsConstructor; | @@ -12,7 +14,7 @@ import lombok.NoArgsConstructor; | ||
| 12 | @Data | 14 | @Data |
| 13 | @NoArgsConstructor | 15 | @NoArgsConstructor |
| 14 | @AllArgsConstructor | 16 | @AllArgsConstructor |
| 15 | -public class KeyValue<K, V> { | 17 | +public class KeyValue<K, V> implements Serializable { |
| 16 | 18 | ||
| 17 | private K key; | 19 | private K key; |
| 18 | private V value; | 20 | private V value; |
yudao-framework/yudao-spring-boot-starter-biz-tenant/pom.xml
| @@ -48,6 +48,22 @@ | @@ -48,6 +48,22 @@ | ||
| 48 | <dependency> | 48 | <dependency> |
| 49 | <groupId>cn.iocoder.boot</groupId> | 49 | <groupId>cn.iocoder.boot</groupId> |
| 50 | <artifactId>yudao-spring-boot-starter-mq</artifactId> | 50 | <artifactId>yudao-spring-boot-starter-mq</artifactId> |
| 51 | + <optional>true</optional> | ||
| 52 | + </dependency> | ||
| 53 | + <dependency> | ||
| 54 | + <groupId>org.springframework.kafka</groupId> | ||
| 55 | + <artifactId>spring-kafka</artifactId> | ||
| 56 | + <optional>true</optional> | ||
| 57 | + </dependency> | ||
| 58 | + <dependency> | ||
| 59 | + <groupId>org.springframework.amqp</groupId> | ||
| 60 | + <artifactId>spring-rabbit</artifactId> | ||
| 61 | + <optional>true</optional> | ||
| 62 | + </dependency> | ||
| 63 | + <dependency> | ||
| 64 | + <groupId>org.apache.rocketmq</groupId> | ||
| 65 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | ||
| 66 | + <optional>true</optional> | ||
| 51 | </dependency> | 67 | </dependency> |
| 52 | 68 | ||
| 53 | <!-- Test 测试相关 --> | 69 | <!-- Test 测试相关 --> |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
| @@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; | @@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; | ||
| 6 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; | 6 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; |
| 7 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; | 7 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; |
| 8 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; | 8 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; |
| 9 | -import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; | 9 | +import cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq.TenantRabbitMQInitializer; |
| 10 | +import cn.iocoder.yudao.framework.tenant.core.mq.redis.TenantRedisMessageInterceptor; | ||
| 11 | +import cn.iocoder.yudao.framework.tenant.core.mq.rocketmq.TenantRocketMQInitializer; | ||
| 10 | import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; | 12 | import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; |
| 11 | import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; | 13 | import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; |
| 12 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | 14 | import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; |
| @@ -18,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.tenant.TenantApi; | @@ -18,6 +20,7 @@ import cn.iocoder.yudao.module.system.api.tenant.TenantApi; | ||
| 18 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; | 20 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| 19 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; | 21 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; |
| 20 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 22 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 23 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
| 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 22 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 25 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 23 | import org.springframework.boot.web.servlet.FilterRegistrationBean; | 26 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| @@ -92,6 +95,18 @@ public class YudaoTenantAutoConfiguration { | @@ -92,6 +95,18 @@ public class YudaoTenantAutoConfiguration { | ||
| 92 | return new TenantRedisMessageInterceptor(); | 95 | return new TenantRedisMessageInterceptor(); |
| 93 | } | 96 | } |
| 94 | 97 | ||
| 98 | + @Bean | ||
| 99 | + @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") | ||
| 100 | + public TenantRabbitMQInitializer tenantRabbitMQInitializer() { | ||
| 101 | + return new TenantRabbitMQInitializer(); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + @Bean | ||
| 105 | + @ConditionalOnClass(name = "org.apache.rocketmq.spring.core.RocketMQTemplate") | ||
| 106 | + public TenantRocketMQInitializer tenantRocketMQInitializer() { | ||
| 107 | + return new TenantRocketMQInitializer(); | ||
| 108 | + } | ||
| 109 | + | ||
| 95 | // ========== Job ========== | 110 | // ========== Job ========== |
| 96 | 111 | ||
| 97 | @Bean | 112 | @Bean |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaEnvironmentPostProcessor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.kafka; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import lombok.extern.slf4j.Slf4j; | ||
| 5 | +import org.springframework.boot.SpringApplication; | ||
| 6 | +import org.springframework.boot.env.EnvironmentPostProcessor; | ||
| 7 | +import org.springframework.core.env.ConfigurableEnvironment; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 多租户的 Kafka 的 {@link EnvironmentPostProcessor} 实现类 | ||
| 11 | + * | ||
| 12 | + * Kafka Producer 发送消息时,增加 {@link TenantKafkaProducerInterceptor} 拦截器 | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +@Slf4j | ||
| 17 | +public class TenantKafkaEnvironmentPostProcessor implements EnvironmentPostProcessor { | ||
| 18 | + | ||
| 19 | + private static final String PROPERTY_KEY_INTERCEPTOR_CLASSES = "spring.kafka.producer.properties.interceptor.classes"; | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { | ||
| 23 | + // 添加 TenantKafkaProducerInterceptor 拦截器 | ||
| 24 | + try { | ||
| 25 | + String value = environment.getProperty(PROPERTY_KEY_INTERCEPTOR_CLASSES); | ||
| 26 | + if (StrUtil.isEmpty(value)) { | ||
| 27 | + value = TenantKafkaProducerInterceptor.class.getName(); | ||
| 28 | + } else { | ||
| 29 | + value += "," + TenantKafkaProducerInterceptor.class.getName(); | ||
| 30 | + } | ||
| 31 | + environment.getSystemProperties().put(PROPERTY_KEY_INTERCEPTOR_CLASSES, value); | ||
| 32 | + } catch (NoClassDefFoundError ignore) { | ||
| 33 | + // 如果触发 NoClassDefFoundError 异常,说明 TenantKafkaProducerInterceptor 类不存在,即没引入 kafka-spring 依赖 | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | +} |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/kafka/TenantKafkaProducerInterceptor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.kafka; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ReflectUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 5 | +import org.apache.kafka.clients.producer.ProducerInterceptor; | ||
| 6 | +import org.apache.kafka.clients.producer.ProducerRecord; | ||
| 7 | +import org.apache.kafka.clients.producer.RecordMetadata; | ||
| 8 | +import org.apache.kafka.common.header.Headers; | ||
| 9 | +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; | ||
| 10 | + | ||
| 11 | +import java.util.Map; | ||
| 12 | + | ||
| 13 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * Kafka 消息队列的多租户 {@link ProducerInterceptor} 实现类 | ||
| 17 | + * | ||
| 18 | + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | ||
| 19 | + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 | ||
| 20 | + * | ||
| 21 | + * @author 芋道源码 | ||
| 22 | + */ | ||
| 23 | +public class TenantKafkaProducerInterceptor implements ProducerInterceptor<Object, Object> { | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public ProducerRecord<Object, Object> onSend(ProducerRecord<Object, Object> record) { | ||
| 27 | + Long tenantId = TenantContextHolder.getTenantId(); | ||
| 28 | + if (tenantId != null) { | ||
| 29 | + Headers headers = (Headers) ReflectUtil.getFieldValue(record, "headers"); // private 属性,没有 get 方法,智能反射 | ||
| 30 | + headers.add(HEADER_TENANT_ID, tenantId.toString().getBytes()); | ||
| 31 | + } | ||
| 32 | + return record; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public void onAcknowledgement(RecordMetadata metadata, Exception exception) { | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + @Override | ||
| 40 | + public void close() { | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Override | ||
| 44 | + public void configure(Map<String, ?> configs) { | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | +} |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQInitializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq; | ||
| 2 | + | ||
| 3 | +import org.springframework.amqp.rabbit.core.RabbitTemplate; | ||
| 4 | +import org.springframework.beans.BeansException; | ||
| 5 | +import org.springframework.beans.factory.config.BeanPostProcessor; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 多租户的 RabbitMQ 初始化器 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +public class TenantRabbitMQInitializer implements BeanPostProcessor { | ||
| 13 | + | ||
| 14 | + @Override | ||
| 15 | + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { | ||
| 16 | + if (bean instanceof RabbitTemplate) { | ||
| 17 | + RabbitTemplate rabbitTemplate = (RabbitTemplate) bean; | ||
| 18 | + rabbitTemplate.addBeforePublishPostProcessors(new TenantRabbitMQMessagePostProcessor()); | ||
| 19 | + } | ||
| 20 | + return bean; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | +} | ||
| 0 | \ No newline at end of file | 24 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rabbitmq/TenantRabbitMQMessagePostProcessor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rabbitmq; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 4 | +import org.apache.kafka.clients.producer.ProducerInterceptor; | ||
| 5 | +import org.springframework.amqp.AmqpException; | ||
| 6 | +import org.springframework.amqp.core.Message; | ||
| 7 | +import org.springframework.amqp.core.MessagePostProcessor; | ||
| 8 | +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; | ||
| 9 | + | ||
| 10 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * RabbitMQ 消息队列的多租户 {@link ProducerInterceptor} 实现类 | ||
| 14 | + * | ||
| 15 | + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | ||
| 16 | + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 | ||
| 17 | + * | ||
| 18 | + * @author 芋道源码 | ||
| 19 | + */ | ||
| 20 | +public class TenantRabbitMQMessagePostProcessor implements MessagePostProcessor { | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public Message postProcessMessage(Message message) throws AmqpException { | ||
| 24 | + Long tenantId = TenantContextHolder.getTenantId(); | ||
| 25 | + if (tenantId != null) { | ||
| 26 | + message.getMessageProperties().getHeaders().put(HEADER_TENANT_ID, tenantId); | ||
| 27 | + } | ||
| 28 | + return message; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | +} |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java renamed to yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/redis/TenantRedisMessageInterceptor.java
| 1 | -package cn.iocoder.yudao.framework.tenant.core.mq; | 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.redis; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.StrUtil; | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 5 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | 4 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | ||
| 6 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | 6 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 7 | 7 | ||
| 8 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | 8 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQConsumeMessageHook.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 6 | +import org.apache.rocketmq.client.hook.ConsumeMessageContext; | ||
| 7 | +import org.apache.rocketmq.client.hook.ConsumeMessageHook; | ||
| 8 | +import org.apache.rocketmq.common.message.MessageExt; | ||
| 9 | +import org.springframework.messaging.handler.invocation.InvocableHandlerMethod; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * RocketMQ 消息队列的多租户 {@link ConsumeMessageHook} 实现类 | ||
| 17 | + * | ||
| 18 | + * Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中,通过 {@link InvocableHandlerMethod} 实现 | ||
| 19 | + * | ||
| 20 | + * @author 芋道源码 | ||
| 21 | + */ | ||
| 22 | +public class TenantRocketMQConsumeMessageHook implements ConsumeMessageHook { | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public String hookName() { | ||
| 26 | + return getClass().getSimpleName(); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public void consumeMessageBefore(ConsumeMessageContext context) { | ||
| 31 | + // 校验,消息必须是单条,不然设置租户可能不正确 | ||
| 32 | + List<MessageExt> messages = context.getMsgList(); | ||
| 33 | + Assert.isTrue(messages.size() == 1, "消息条数({})不正确", messages.size()); | ||
| 34 | + // 设置租户编号 | ||
| 35 | + String tenantId = messages.get(0).getUserProperty(HEADER_TENANT_ID); | ||
| 36 | + if (StrUtil.isNotEmpty(tenantId)) { | ||
| 37 | + TenantContextHolder.setTenantId(Long.parseLong(tenantId)); | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Override | ||
| 42 | + public void consumeMessageAfter(ConsumeMessageContext context) { | ||
| 43 | + TenantContextHolder.clear(); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | +} |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQInitializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; | ||
| 2 | + | ||
| 3 | +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; | ||
| 4 | +import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; | ||
| 5 | +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; | ||
| 6 | +import org.apache.rocketmq.client.producer.DefaultMQProducer; | ||
| 7 | +import org.apache.rocketmq.spring.core.RocketMQTemplate; | ||
| 8 | +import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; | ||
| 9 | +import org.springframework.beans.BeansException; | ||
| 10 | +import org.springframework.beans.factory.config.BeanPostProcessor; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 多租户的 RocketMQ 初始化器 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +public class TenantRocketMQInitializer implements BeanPostProcessor { | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { | ||
| 21 | + if (bean instanceof DefaultRocketMQListenerContainer) { | ||
| 22 | + DefaultRocketMQListenerContainer container = (DefaultRocketMQListenerContainer) bean; | ||
| 23 | + initTenantConsumer(container.getConsumer()); | ||
| 24 | + } else if (bean instanceof RocketMQTemplate) { | ||
| 25 | + RocketMQTemplate template = (RocketMQTemplate) bean; | ||
| 26 | + initTenantProducer(template.getProducer()); | ||
| 27 | + } | ||
| 28 | + return bean; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + private void initTenantProducer(DefaultMQProducer producer) { | ||
| 32 | + if (producer == null) { | ||
| 33 | + return; | ||
| 34 | + } | ||
| 35 | + DefaultMQProducerImpl producerImpl = producer.getDefaultMQProducerImpl(); | ||
| 36 | + if (producerImpl == null) { | ||
| 37 | + return; | ||
| 38 | + } | ||
| 39 | + producerImpl.registerSendMessageHook(new TenantRocketMQSendMessageHook()); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + private void initTenantConsumer(DefaultMQPushConsumer consumer) { | ||
| 43 | + if (consumer == null) { | ||
| 44 | + return; | ||
| 45 | + } | ||
| 46 | + DefaultMQPushConsumerImpl consumerImpl = consumer.getDefaultMQPushConsumerImpl(); | ||
| 47 | + if (consumerImpl == null) { | ||
| 48 | + return; | ||
| 49 | + } | ||
| 50 | + consumerImpl.registerConsumeMessageHook(new TenantRocketMQConsumeMessageHook()); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | +} | ||
| 0 | \ No newline at end of file | 54 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/rocketmq/TenantRocketMQSendMessageHook.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq.rocketmq; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 4 | +import org.apache.rocketmq.client.hook.SendMessageContext; | ||
| 5 | +import org.apache.rocketmq.client.hook.SendMessageHook; | ||
| 6 | + | ||
| 7 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * RocketMQ 消息队列的多租户 {@link SendMessageHook} 实现类 | ||
| 11 | + * | ||
| 12 | + * Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +public class TenantRocketMQSendMessageHook implements SendMessageHook { | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public String hookName() { | ||
| 20 | + return getClass().getSimpleName(); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void sendMessageBefore(SendMessageContext sendMessageContext) { | ||
| 25 | + Long tenantId = TenantContextHolder.getTenantId(); | ||
| 26 | + if (tenantId == null) { | ||
| 27 | + return; | ||
| 28 | + } | ||
| 29 | + sendMessageContext.getMessage().putUserProperty(HEADER_TENANT_ID, tenantId.toString()); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public void sendMessageAfter(SendMessageContext sendMessageContext) { | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | +} |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright 2002-2021 the original author or authors. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * https://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package org.springframework.messaging.handler.invocation; | ||
| 18 | + | ||
| 19 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 20 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 21 | +import org.springframework.core.DefaultParameterNameDiscoverer; | ||
| 22 | +import org.springframework.core.MethodParameter; | ||
| 23 | +import org.springframework.core.ParameterNameDiscoverer; | ||
| 24 | +import org.springframework.core.ResolvableType; | ||
| 25 | +import org.springframework.lang.Nullable; | ||
| 26 | +import org.springframework.messaging.Message; | ||
| 27 | +import org.springframework.messaging.handler.HandlerMethod; | ||
| 28 | +import org.springframework.util.ObjectUtils; | ||
| 29 | + | ||
| 30 | +import java.lang.reflect.InvocationTargetException; | ||
| 31 | +import java.lang.reflect.Method; | ||
| 32 | +import java.lang.reflect.Type; | ||
| 33 | +import java.util.Arrays; | ||
| 34 | + | ||
| 35 | +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | ||
| 36 | + | ||
| 37 | +/** | ||
| 38 | + * Extension of {@link HandlerMethod} that invokes the underlying method with | ||
| 39 | + * argument values resolved from the current HTTP request through a list of | ||
| 40 | + * {@link HandlerMethodArgumentResolver}. | ||
| 41 | + * | ||
| 42 | + * 针对 rabbitmq-spring 和 kafka-spring,不存在合适的拓展点,可以实现 Consumer 消费前,读取 Header 中的 tenant-id 设置到 {@link TenantContextHolder} 中 | ||
| 43 | + * TODO 芋艿:持续跟进,看看有没新的拓展点 | ||
| 44 | + * | ||
| 45 | + * @author Rossen Stoyanchev | ||
| 46 | + * @author Juergen Hoeller | ||
| 47 | + * @since 4.0 | ||
| 48 | + */ | ||
| 49 | +public class InvocableHandlerMethod extends HandlerMethod { | ||
| 50 | + | ||
| 51 | + private static final Object[] EMPTY_ARGS = new Object[0]; | ||
| 52 | + | ||
| 53 | + private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite(); | ||
| 54 | + | ||
| 55 | + private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * Create an instance from a {@code HandlerMethod}. | ||
| 59 | + */ | ||
| 60 | + public InvocableHandlerMethod(HandlerMethod handlerMethod) { | ||
| 61 | + super(handlerMethod); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * Create an instance from a bean instance and a method. | ||
| 66 | + */ | ||
| 67 | + public InvocableHandlerMethod(Object bean, Method method) { | ||
| 68 | + super(bean, method); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * Construct a new handler method with the given bean instance, method name and parameters. | ||
| 73 | + * @param bean the object bean | ||
| 74 | + * @param methodName the method name | ||
| 75 | + * @param parameterTypes the method parameter types | ||
| 76 | + * @throws NoSuchMethodException when the method cannot be found | ||
| 77 | + */ | ||
| 78 | + public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) | ||
| 79 | + throws NoSuchMethodException { | ||
| 80 | + | ||
| 81 | + super(bean, methodName, parameterTypes); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * Set {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} to use for resolving method argument values. | ||
| 86 | + */ | ||
| 87 | + public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) { | ||
| 88 | + this.resolvers = argumentResolvers; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + /** | ||
| 92 | + * Set the ParameterNameDiscoverer for resolving parameter names when needed | ||
| 93 | + * (e.g. default request attribute name). | ||
| 94 | + * <p>Default is a {@link DefaultParameterNameDiscoverer}. | ||
| 95 | + */ | ||
| 96 | + public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { | ||
| 97 | + this.parameterNameDiscoverer = parameterNameDiscoverer; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + /** | ||
| 101 | + * Invoke the method after resolving its argument values in the context of the given message. | ||
| 102 | + * <p>Argument values are commonly resolved through | ||
| 103 | + * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. | ||
| 104 | + * The {@code providedArgs} parameter however may supply argument values to be used directly, | ||
| 105 | + * i.e. without argument resolution. | ||
| 106 | + * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the | ||
| 107 | + * resolved arguments. | ||
| 108 | + * @param message the current message being processed | ||
| 109 | + * @param providedArgs "given" arguments matched by type, not resolved | ||
| 110 | + * @return the raw value returned by the invoked method | ||
| 111 | + * @throws Exception raised if no suitable argument resolver can be found, | ||
| 112 | + * or if the method raised an exception | ||
| 113 | + * @see #getMethodArgumentValues | ||
| 114 | + * @see #doInvoke | ||
| 115 | + */ | ||
| 116 | + @Nullable | ||
| 117 | + public Object invoke(Message<?> message, Object... providedArgs) throws Exception { | ||
| 118 | + Object[] args = getMethodArgumentValues(message, providedArgs); | ||
| 119 | + if (logger.isTraceEnabled()) { | ||
| 120 | + logger.trace("Arguments: " + Arrays.toString(args)); | ||
| 121 | + } | ||
| 122 | + // 注意:如下是本类的改动点!!! | ||
| 123 | + // 情况一:无租户编号的情况 | ||
| 124 | + Long tenantId= parseTenantId(message); | ||
| 125 | + if (tenantId == null) { | ||
| 126 | + return doInvoke(args); | ||
| 127 | + } | ||
| 128 | + // 情况二:有租户的情况下 | ||
| 129 | + return TenantUtils.execute(tenantId, () -> doInvoke(args)); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + private Long parseTenantId(Message<?> message) { | ||
| 133 | + Object tenantId = message.getHeaders().get(HEADER_TENANT_ID); | ||
| 134 | + if (tenantId == null) { | ||
| 135 | + return null; | ||
| 136 | + } | ||
| 137 | + if (tenantId instanceof Long) { | ||
| 138 | + return (Long) tenantId; | ||
| 139 | + } | ||
| 140 | + if (tenantId instanceof Number) { | ||
| 141 | + return ((Number) tenantId).longValue(); | ||
| 142 | + } | ||
| 143 | + if (tenantId instanceof String) { | ||
| 144 | + return Long.parseLong((String) tenantId); | ||
| 145 | + } | ||
| 146 | + if (tenantId instanceof byte[]) { | ||
| 147 | + return Long.parseLong(new String((byte[]) tenantId)); | ||
| 148 | + } | ||
| 149 | + throw new IllegalArgumentException("未知的数据类型:" + tenantId); | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + /** | ||
| 153 | + * Get the method argument values for the current message, checking the provided | ||
| 154 | + * argument values and falling back to the configured argument resolvers. | ||
| 155 | + * <p>The resulting array will be passed into {@link #doInvoke}. | ||
| 156 | + * @since 5.1.2 | ||
| 157 | + */ | ||
| 158 | + protected Object[] getMethodArgumentValues(Message<?> message, Object... providedArgs) throws Exception { | ||
| 159 | + MethodParameter[] parameters = getMethodParameters(); | ||
| 160 | + if (ObjectUtils.isEmpty(parameters)) { | ||
| 161 | + return EMPTY_ARGS; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + Object[] args = new Object[parameters.length]; | ||
| 165 | + for (int i = 0; i < parameters.length; i++) { | ||
| 166 | + MethodParameter parameter = parameters[i]; | ||
| 167 | + parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); | ||
| 168 | + args[i] = findProvidedArgument(parameter, providedArgs); | ||
| 169 | + if (args[i] != null) { | ||
| 170 | + continue; | ||
| 171 | + } | ||
| 172 | + if (!this.resolvers.supportsParameter(parameter)) { | ||
| 173 | + throw new MethodArgumentResolutionException( | ||
| 174 | + message, parameter, formatArgumentError(parameter, "No suitable resolver")); | ||
| 175 | + } | ||
| 176 | + try { | ||
| 177 | + args[i] = this.resolvers.resolveArgument(parameter, message); | ||
| 178 | + } | ||
| 179 | + catch (Exception ex) { | ||
| 180 | + // Leave stack trace for later, exception may actually be resolved and handled... | ||
| 181 | + if (logger.isDebugEnabled()) { | ||
| 182 | + String exMsg = ex.getMessage(); | ||
| 183 | + if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { | ||
| 184 | + logger.debug(formatArgumentError(parameter, exMsg)); | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + throw ex; | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + return args; | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + /** | ||
| 194 | + * Invoke the handler method with the given argument values. | ||
| 195 | + */ | ||
| 196 | + @Nullable | ||
| 197 | + protected Object doInvoke(Object... args) throws Exception { | ||
| 198 | + try { | ||
| 199 | + return getBridgedMethod().invoke(getBean(), args); | ||
| 200 | + } | ||
| 201 | + catch (IllegalArgumentException ex) { | ||
| 202 | + assertTargetBean(getBridgedMethod(), getBean(), args); | ||
| 203 | + String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); | ||
| 204 | + throw new IllegalStateException(formatInvokeError(text, args), ex); | ||
| 205 | + } | ||
| 206 | + catch (InvocationTargetException ex) { | ||
| 207 | + // Unwrap for HandlerExceptionResolvers ... | ||
| 208 | + Throwable targetException = ex.getTargetException(); | ||
| 209 | + if (targetException instanceof RuntimeException) { | ||
| 210 | + throw (RuntimeException) targetException; | ||
| 211 | + } | ||
| 212 | + else if (targetException instanceof Error) { | ||
| 213 | + throw (Error) targetException; | ||
| 214 | + } | ||
| 215 | + else if (targetException instanceof Exception) { | ||
| 216 | + throw (Exception) targetException; | ||
| 217 | + } | ||
| 218 | + else { | ||
| 219 | + throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException); | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + MethodParameter getAsyncReturnValueType(@Nullable Object returnValue) { | ||
| 225 | + return new AsyncResultMethodParameter(returnValue); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + private class AsyncResultMethodParameter extends HandlerMethodParameter { | ||
| 229 | + | ||
| 230 | + @Nullable | ||
| 231 | + private final Object returnValue; | ||
| 232 | + | ||
| 233 | + private final ResolvableType returnType; | ||
| 234 | + | ||
| 235 | + public AsyncResultMethodParameter(@Nullable Object returnValue) { | ||
| 236 | + super(-1); | ||
| 237 | + this.returnValue = returnValue; | ||
| 238 | + this.returnType = ResolvableType.forType(super.getGenericParameterType()).getGeneric(); | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + protected AsyncResultMethodParameter(AsyncResultMethodParameter original) { | ||
| 242 | + super(original); | ||
| 243 | + this.returnValue = original.returnValue; | ||
| 244 | + this.returnType = original.returnType; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + @Override | ||
| 248 | + public Class<?> getParameterType() { | ||
| 249 | + if (this.returnValue != null) { | ||
| 250 | + return this.returnValue.getClass(); | ||
| 251 | + } | ||
| 252 | + if (!ResolvableType.NONE.equals(this.returnType)) { | ||
| 253 | + return this.returnType.toClass(); | ||
| 254 | + } | ||
| 255 | + return super.getParameterType(); | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + @Override | ||
| 259 | + public Type getGenericParameterType() { | ||
| 260 | + return this.returnType.getType(); | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + @Override | ||
| 264 | + public AsyncResultMethodParameter clone() { | ||
| 265 | + return new AsyncResultMethodParameter(this); | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | +} |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/resources/META-INF/spring.factories
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/pom.xml
| @@ -12,7 +12,7 @@ | @@ -12,7 +12,7 @@ | ||
| 12 | <packaging>jar</packaging> | 12 | <packaging>jar</packaging> |
| 13 | 13 | ||
| 14 | <name>${project.artifactId}</name> | 14 | <name>${project.artifactId}</name> |
| 15 | - <description>消息队列,基于 Redis Pub/Sub 实现广播消费,基于 Stream 实现集群消费</description> | 15 | + <description>消息队列,支持 Redis、RocketMQ、RabbitMQ、Kafka 四种</description> |
| 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 16 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 17 | 17 | ||
| 18 | <dependencies> | 18 | <dependencies> |
| @@ -21,6 +21,23 @@ | @@ -21,6 +21,23 @@ | ||
| 21 | <groupId>cn.iocoder.boot</groupId> | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | <artifactId>yudao-spring-boot-starter-redis</artifactId> | 22 | <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| 23 | </dependency> | 23 | </dependency> |
| 24 | + | ||
| 25 | + <!-- 消息队列相关 --> | ||
| 26 | + <dependency> | ||
| 27 | + <groupId>org.springframework.kafka</groupId> | ||
| 28 | + <artifactId>spring-kafka</artifactId> | ||
| 29 | + <optional>true</optional> | ||
| 30 | + </dependency> | ||
| 31 | + <dependency> | ||
| 32 | + <groupId>org.springframework.amqp</groupId> | ||
| 33 | + <artifactId>spring-rabbit</artifactId> | ||
| 34 | + <optional>true</optional> | ||
| 35 | + </dependency> | ||
| 36 | + <dependency> | ||
| 37 | + <groupId>org.apache.rocketmq</groupId> | ||
| 38 | + <artifactId>rocketmq-spring-boot-starter</artifactId> | ||
| 39 | + <optional>true</optional> | ||
| 40 | + </dependency> | ||
| 24 | </dependencies> | 41 | </dependencies> |
| 25 | 42 | ||
| 26 | -</project> | 43 | -</project> |
| 44 | +</project> | ||
| 27 | \ No newline at end of file | 45 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/package-info.java
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/config/YudaoRabbitMQAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.mq.rabbitmq.config; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ReflectUtil; | ||
| 4 | +import lombok.extern.slf4j.Slf4j; | ||
| 5 | +import org.springframework.amqp.utils.SerializationUtils; | ||
| 6 | +import org.springframework.boot.autoconfigure.AutoConfiguration; | ||
| 7 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
| 8 | + | ||
| 9 | +import java.lang.reflect.Field; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * RabbitMQ 消息队列配置类 | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +@AutoConfiguration | ||
| 17 | +@Slf4j | ||
| 18 | +@ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate") | ||
| 19 | +public class YudaoRabbitMQAutoConfiguration { | ||
| 20 | + | ||
| 21 | + static { | ||
| 22 | + // 强制设置 SerializationUtils 的 TRUST_ALL 为 true,避免 RabbitMQ Consumer 反序列化消息报错 | ||
| 23 | + // 为什么不通过设置 spring.amqp.deserialization.trust.all 呢?因为可能在 SerializationUtils static 初始化后 | ||
| 24 | + Field trustAllField = ReflectUtil.getField(SerializationUtils.class, "TRUST_ALL"); | ||
| 25 | + ReflectUtil.removeFinalModify(trustAllField); | ||
| 26 | + ReflectUtil.setFieldValue(SerializationUtils.class, trustAllField, true); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | +} |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/core/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/rabbitmq/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQAutoConfiguration.java
| 1 | -package cn.iocoder.yudao.framework.mq.config; | 1 | +package cn.iocoder.yudao.framework.mq.redis.config; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.map.MapUtil; | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | import cn.hutool.system.SystemUtil; | 5 | import cn.hutool.system.SystemUtil; |
| 6 | import cn.iocoder.yudao.framework.common.enums.DocumentEnum; | 6 | import cn.iocoder.yudao.framework.common.enums.DocumentEnum; |
| 7 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 8 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 9 | -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; | ||
| 10 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | ||
| 11 | -import cn.iocoder.yudao.framework.mq.job.RedisPendingMessageResendJob; | 7 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; |
| 8 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | ||
| 9 | +import cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob; | ||
| 10 | +import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener; | ||
| 11 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; | ||
| 12 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; | 12 | import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration; |
| 13 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | import org.redisson.api.RedissonClient; | 14 | import org.redisson.api.RedissonClient; |
| 15 | import org.springframework.beans.factory.annotation.Value; | 15 | import org.springframework.beans.factory.annotation.Value; |
| 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
| 18 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
| 19 | import org.springframework.context.annotation.Bean; | 18 | import org.springframework.context.annotation.Bean; |
| 20 | import org.springframework.data.redis.connection.RedisServerCommands; | 19 | import org.springframework.data.redis.connection.RedisServerCommands; |
| 21 | import org.springframework.data.redis.connection.stream.Consumer; | 20 | import org.springframework.data.redis.connection.stream.Consumer; |
| @@ -27,7 +26,6 @@ import org.springframework.data.redis.core.RedisTemplate; | @@ -27,7 +26,6 @@ import org.springframework.data.redis.core.RedisTemplate; | ||
| 27 | import org.springframework.data.redis.core.StringRedisTemplate; | 26 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 28 | import org.springframework.data.redis.listener.ChannelTopic; | 27 | import org.springframework.data.redis.listener.ChannelTopic; |
| 29 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; | 28 | import org.springframework.data.redis.listener.RedisMessageListenerContainer; |
| 30 | -import org.springframework.data.redis.stream.DefaultStreamMessageListenerContainerX; | ||
| 31 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; | 29 | import org.springframework.data.redis.stream.StreamMessageListenerContainer; |
| 32 | import org.springframework.scheduling.annotation.EnableScheduling; | 30 | import org.springframework.scheduling.annotation.EnableScheduling; |
| 33 | 31 | ||
| @@ -42,7 +40,7 @@ import java.util.Properties; | @@ -42,7 +40,7 @@ import java.util.Properties; | ||
| 42 | @Slf4j | 40 | @Slf4j |
| 43 | @EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 | 41 | @EnableScheduling // 启用定时任务,用于 RedisPendingMessageResendJob 重发消息 |
| 44 | @AutoConfiguration(after = YudaoRedisAutoConfiguration.class) | 42 | @AutoConfiguration(after = YudaoRedisAutoConfiguration.class) |
| 45 | -public class YudaoMQAutoConfiguration { | 43 | +public class YudaoRedisMQAutoConfiguration { |
| 46 | 44 | ||
| 47 | @Bean | 45 | @Bean |
| 48 | public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, | 46 | public RedisMQTemplate redisMQTemplate(StringRedisTemplate redisTemplate, |
| @@ -59,10 +57,9 @@ public class YudaoMQAutoConfiguration { | @@ -59,10 +57,9 @@ public class YudaoMQAutoConfiguration { | ||
| 59 | * 创建 Redis Pub/Sub 广播消费的容器 | 57 | * 创建 Redis Pub/Sub 广播消费的容器 |
| 60 | */ | 58 | */ |
| 61 | @Bean(initMethod = "start", destroyMethod = "stop") | 59 | @Bean(initMethod = "start", destroyMethod = "stop") |
| 62 | - @ConditionalOnBean(AbstractChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | ||
| 63 | - @ConditionalOnProperty(prefix = "yudao.mq.redis.pubsub", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.pubsub.enable=false 禁用多租户 | 60 | + @ConditionalOnBean(AbstractRedisChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候,才需要注册 Redis pubsub 监听 |
| 64 | public RedisMessageListenerContainer redisMessageListenerContainer( | 61 | public RedisMessageListenerContainer redisMessageListenerContainer( |
| 65 | - RedisMQTemplate redisMQTemplate, List<AbstractChannelMessageListener<?>> listeners) { | 62 | + RedisMQTemplate redisMQTemplate, List<AbstractRedisChannelMessageListener<?>> listeners) { |
| 66 | // 创建 RedisMessageListenerContainer 对象 | 63 | // 创建 RedisMessageListenerContainer 对象 |
| 67 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | 64 | RedisMessageListenerContainer container = new RedisMessageListenerContainer(); |
| 68 | // 设置 RedisConnection 工厂。 | 65 | // 设置 RedisConnection 工厂。 |
| @@ -81,9 +78,8 @@ public class YudaoMQAutoConfiguration { | @@ -81,9 +78,8 @@ public class YudaoMQAutoConfiguration { | ||
| 81 | * 创建 Redis Stream 重新消费的任务 | 78 | * 创建 Redis Stream 重新消费的任务 |
| 82 | */ | 79 | */ |
| 83 | @Bean | 80 | @Bean |
| 84 | - @ConditionalOnBean(AbstractStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | ||
| 85 | - @ConditionalOnProperty(prefix = "yudao.mq.redis.stream", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.stream.enable=false 禁用多租户 | ||
| 86 | - public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractStreamMessageListener<?>> listeners, | 81 | + @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 |
| 82 | + public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners, | ||
| 87 | RedisMQTemplate redisTemplate, | 83 | RedisMQTemplate redisTemplate, |
| 88 | @Value("${spring.application.name}") String groupName, | 84 | @Value("${spring.application.name}") String groupName, |
| 89 | RedissonClient redissonClient) { | 85 | RedissonClient redissonClient) { |
| @@ -92,14 +88,13 @@ public class YudaoMQAutoConfiguration { | @@ -92,14 +88,13 @@ public class YudaoMQAutoConfiguration { | ||
| 92 | 88 | ||
| 93 | /** | 89 | /** |
| 94 | * 创建 Redis Stream 集群消费的容器 | 90 | * 创建 Redis Stream 集群消费的容器 |
| 95 | - * <p> | ||
| 96 | - * Redis Stream 的 xreadgroup 命令:https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html | 91 | + * |
| 92 | + * 基础知识:<a href="https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html">Redis Stream 的 xreadgroup 命令</a> | ||
| 97 | */ | 93 | */ |
| 98 | @Bean(initMethod = "start", destroyMethod = "stop") | 94 | @Bean(initMethod = "start", destroyMethod = "stop") |
| 99 | - @ConditionalOnBean(AbstractStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 | ||
| 100 | - @ConditionalOnProperty(prefix = "yudao.mq.redis.stream", value = "enable", matchIfMissing = true) // 允许使用 yudao.mq.redis.stream.enable=false 禁用多租户 | 95 | + @ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候,才需要注册 Redis pubsub 监听 |
| 101 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( | 96 | public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer( |
| 102 | - RedisMQTemplate redisMQTemplate, List<AbstractStreamMessageListener<?>> listeners) { | 97 | + RedisMQTemplate redisMQTemplate, List<AbstractRedisStreamMessageListener<?>> listeners) { |
| 103 | RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); | 98 | RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate(); |
| 104 | checkRedisVersion(redisTemplate); | 99 | checkRedisVersion(redisTemplate); |
| 105 | // 第一步,创建 StreamMessageListenerContainer 容器 | 100 | // 第一步,创建 StreamMessageListenerContainer 容器 |
| @@ -111,8 +106,7 @@ public class YudaoMQAutoConfiguration { | @@ -111,8 +106,7 @@ public class YudaoMQAutoConfiguration { | ||
| 111 | .build(); | 106 | .build(); |
| 112 | // 创建 container 对象 | 107 | // 创建 container 对象 |
| 113 | StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = | 108 | StreamMessageListenerContainer<String, ObjectRecord<String, String>> container = |
| 114 | -// StreamMessageListenerContainer.create(redisTemplate.getRequiredConnectionFactory(), containerOptions); | ||
| 115 | - DefaultStreamMessageListenerContainerX.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); | 109 | + StreamMessageListenerContainer.create(redisMQTemplate.getRedisTemplate().getRequiredConnectionFactory(), containerOptions); |
| 116 | 110 | ||
| 117 | // 第二步,注册监听器,消费对应的 Stream 主题 | 111 | // 第二步,注册监听器,消费对应的 Stream 主题 |
| 118 | String consumerName = buildConsumerName(); | 112 | String consumerName = buildConsumerName(); |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/RedisMQTemplate.java
| 1 | -package cn.iocoder.yudao.framework.mq.core; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 5 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | ||
| 6 | -import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; | ||
| 7 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | 4 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | ||
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessage; | ||
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessage; | ||
| 8 | import lombok.AllArgsConstructor; | 8 | import lombok.AllArgsConstructor; |
| 9 | import lombok.Getter; | 9 | import lombok.Getter; |
| 10 | import org.springframework.data.redis.connection.stream.RecordId; | 10 | import org.springframework.data.redis.connection.stream.RecordId; |
| @@ -35,7 +35,7 @@ public class RedisMQTemplate { | @@ -35,7 +35,7 @@ public class RedisMQTemplate { | ||
| 35 | * | 35 | * |
| 36 | * @param message 消息 | 36 | * @param message 消息 |
| 37 | */ | 37 | */ |
| 38 | - public <T extends AbstractChannelMessage> void send(T message) { | 38 | + public <T extends AbstractRedisChannelMessage> void send(T message) { |
| 39 | try { | 39 | try { |
| 40 | sendMessageBefore(message); | 40 | sendMessageBefore(message); |
| 41 | // 发送消息 | 41 | // 发送消息 |
| @@ -51,7 +51,7 @@ public class RedisMQTemplate { | @@ -51,7 +51,7 @@ public class RedisMQTemplate { | ||
| 51 | * @param message 消息 | 51 | * @param message 消息 |
| 52 | * @return 消息记录的编号对象 | 52 | * @return 消息记录的编号对象 |
| 53 | */ | 53 | */ |
| 54 | - public <T extends AbstractStreamMessage> RecordId send(T message) { | 54 | + public <T extends AbstractRedisStreamMessage> RecordId send(T message) { |
| 55 | try { | 55 | try { |
| 56 | sendMessageBefore(message); | 56 | sendMessageBefore(message); |
| 57 | // 发送消息 | 57 | // 发送消息 |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/interceptor/RedisMessageInterceptor.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.interceptor; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core.interceptor; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | 3 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
| 6 | * {@link AbstractRedisMessage} 消息拦截器 | 6 | * {@link AbstractRedisMessage} 消息拦截器 |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/job/RedisPendingMessageResendJob.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java
| 1 | -package cn.iocoder.yudao.framework.mq.job; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core.job; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 5 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | 4 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; |
| 5 | +import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener; | ||
| 6 | import lombok.AllArgsConstructor; | 6 | import lombok.AllArgsConstructor; |
| 7 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | import org.redisson.api.RLock; | 8 | import org.redisson.api.RLock; |
| @@ -33,7 +33,7 @@ public class RedisPendingMessageResendJob { | @@ -33,7 +33,7 @@ public class RedisPendingMessageResendJob { | ||
| 33 | */ | 33 | */ |
| 34 | private static final int EXPIRE_TIME = 5 * 60; | 34 | private static final int EXPIRE_TIME = 5 * 60; |
| 35 | 35 | ||
| 36 | - private final List<AbstractStreamMessageListener<?>> listeners; | 36 | + private final List<AbstractRedisStreamMessageListener<?>> listeners; |
| 37 | private final RedisMQTemplate redisTemplate; | 37 | private final RedisMQTemplate redisTemplate; |
| 38 | private final String groupName; | 38 | private final String groupName; |
| 39 | private final RedissonClient redissonClient; | 39 | private final RedissonClient redissonClient; |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/message/AbstractRedisMessage.java
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessage.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.pubsub; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core.pubsub; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | 3 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; |
| 4 | import com.fasterxml.jackson.annotation.JsonIgnore; | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | ||
| 8 | * | 8 | * |
| 9 | * @author 芋道源码 | 9 | * @author 芋道源码 |
| 10 | */ | 10 | */ |
| 11 | -public abstract class AbstractChannelMessage extends AbstractRedisMessage { | 11 | +public abstract class AbstractRedisChannelMessage extends AbstractRedisMessage { |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | - * 获得 Redis Channel | 14 | + * 获得 Redis Channel,默认使用类名 |
| 15 | * | 15 | * |
| 16 | * @return Channel | 16 | * @return Channel |
| 17 | */ | 17 | */ |
| 18 | @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 | 18 | @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。 |
| 19 | - public abstract String getChannel(); | 19 | + public String getChannel() { |
| 20 | + return getClass().getSimpleName(); | ||
| 21 | + } | ||
| 20 | 22 | ||
| 21 | } | 23 | } |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/pubsub/AbstractRedisChannelMessageListener.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.pubsub; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core.pubsub; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.TypeUtil; | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 6 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 7 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | 5 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; |
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | ||
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | ||
| 8 | import lombok.Setter; | 8 | import lombok.Setter; |
| 9 | import lombok.SneakyThrows; | 9 | import lombok.SneakyThrows; |
| 10 | import org.springframework.data.redis.connection.Message; | 10 | import org.springframework.data.redis.connection.Message; |
| @@ -20,7 +20,7 @@ import java.util.List; | @@ -20,7 +20,7 @@ import java.util.List; | ||
| 20 | * | 20 | * |
| 21 | * @author 芋道源码 | 21 | * @author 芋道源码 |
| 22 | */ | 22 | */ |
| 23 | -public abstract class AbstractChannelMessageListener<T extends AbstractChannelMessage> implements MessageListener { | 23 | +public abstract class AbstractRedisChannelMessageListener<T extends AbstractRedisChannelMessage> implements MessageListener { |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| 26 | * 消息类型 | 26 | * 消息类型 |
| @@ -37,7 +37,7 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | @@ -37,7 +37,7 @@ public abstract class AbstractChannelMessageListener<T extends AbstractChannelMe | ||
| 37 | private RedisMQTemplate redisMQTemplate; | 37 | private RedisMQTemplate redisMQTemplate; |
| 38 | 38 | ||
| 39 | @SneakyThrows | 39 | @SneakyThrows |
| 40 | - protected AbstractChannelMessageListener() { | 40 | + protected AbstractRedisChannelMessageListener() { |
| 41 | this.messageType = getMessageClass(); | 41 | this.messageType = getMessageClass(); |
| 42 | this.channel = messageType.getDeclaredConstructor().newInstance().getChannel(); | 42 | this.channel = messageType.getDeclaredConstructor().newInstance().getChannel(); |
| 43 | } | 43 | } |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessage.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.stream; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core.stream; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | 3 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; |
| 4 | import com.fasterxml.jackson.annotation.JsonIgnore; | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | @@ -8,14 +8,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore; | ||
| 8 | * | 8 | * |
| 9 | * @author 芋道源码 | 9 | * @author 芋道源码 |
| 10 | */ | 10 | */ |
| 11 | -public abstract class AbstractStreamMessage extends AbstractRedisMessage { | 11 | +public abstract class AbstractRedisStreamMessage extends AbstractRedisMessage { |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | - * 获得 Redis Stream Key | 14 | + * 获得 Redis Stream Key,默认使用类名 |
| 15 | * | 15 | * |
| 16 | * @return Channel | 16 | * @return Channel |
| 17 | */ | 17 | */ |
| 18 | @JsonIgnore // 避免序列化 | 18 | @JsonIgnore // 避免序列化 |
| 19 | - public abstract String getStreamKey(); | 19 | + public String getStreamKey() { |
| 20 | + return getClass().getSimpleName(); | ||
| 21 | + } | ||
| 20 | 22 | ||
| 21 | } | 23 | } |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessageListener.java renamed to yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/stream/AbstractRedisStreamMessageListener.java
| 1 | -package cn.iocoder.yudao.framework.mq.core.stream; | 1 | +package cn.iocoder.yudao.framework.mq.redis.core.stream; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.TypeUtil; | 3 | import cn.hutool.core.util.TypeUtil; |
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 6 | -import cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor; | ||
| 7 | -import cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage; | 5 | +import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate; |
| 6 | +import cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor; | ||
| 7 | +import cn.iocoder.yudao.framework.mq.redis.core.message.AbstractRedisMessage; | ||
| 8 | import lombok.Getter; | 8 | import lombok.Getter; |
| 9 | import lombok.Setter; | 9 | import lombok.Setter; |
| 10 | import lombok.SneakyThrows; | 10 | import lombok.SneakyThrows; |
| @@ -22,7 +22,7 @@ import java.util.List; | @@ -22,7 +22,7 @@ import java.util.List; | ||
| 22 | * | 22 | * |
| 23 | * @author 芋道源码 | 23 | * @author 芋道源码 |
| 24 | */ | 24 | */ |
| 25 | -public abstract class AbstractStreamMessageListener<T extends AbstractStreamMessage> | 25 | +public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedisStreamMessage> |
| 26 | implements StreamListener<String, ObjectRecord<String, String>> { | 26 | implements StreamListener<String, ObjectRecord<String, String>> { |
| 27 | 27 | ||
| 28 | /** | 28 | /** |
| @@ -48,7 +48,7 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | @@ -48,7 +48,7 @@ public abstract class AbstractStreamMessageListener<T extends AbstractStreamMess | ||
| 48 | private RedisMQTemplate redisMQTemplate; | 48 | private RedisMQTemplate redisMQTemplate; |
| 49 | 49 | ||
| 50 | @SneakyThrows | 50 | @SneakyThrows |
| 51 | - protected AbstractStreamMessageListener() { | 51 | + protected AbstractRedisStreamMessageListener() { |
| 52 | this.messageType = getMessageClass(); | 52 | this.messageType = getMessageClass(); |
| 53 | this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); | 53 | this.streamKey = messageType.getDeclaredConstructor().newInstance().getStreamKey(); |
| 54 | } | 54 | } |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/package-info.java
0 → 100644
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/org/springframework/data/redis/stream/DefaultStreamMessageListenerContainerX.java deleted
| 1 | -package org.springframework.data.redis.stream; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.util.ReflectUtil; | ||
| 4 | -import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
| 5 | -import org.springframework.data.redis.connection.stream.ByteRecord; | ||
| 6 | -import org.springframework.data.redis.connection.stream.ReadOffset; | ||
| 7 | -import org.springframework.data.redis.connection.stream.Record; | ||
| 8 | -import org.springframework.util.Assert; | ||
| 9 | - | ||
| 10 | -import java.util.Collections; | ||
| 11 | -import java.util.List; | ||
| 12 | -import java.util.function.Function; | ||
| 13 | - | ||
| 14 | -/** | ||
| 15 | - * 拓展 DefaultStreamMessageListenerContainer 实现,解决 Spring Data Redis + Redisson 结合使用时,Redisson 在 Stream 获得不到数据时,返回 null 而不是空 List,导致 NPE 异常。 | ||
| 16 | - * 对应 issue:https://github.com/spring-projects/spring-data-redis/issues/2147 和 https://github.com/redisson/redisson/issues/4006 | ||
| 17 | - * 目前看下来 Spring Data Redis 不肯加 null 判断,Redisson 暂时也没改返回 null 到空 List 的打算,所以暂时只能自己改,哽咽! | ||
| 18 | - * | ||
| 19 | - * @author 芋道源码 | ||
| 20 | - */ | ||
| 21 | -public class DefaultStreamMessageListenerContainerX<K, V extends Record<K, ?>> extends DefaultStreamMessageListenerContainer<K, V> { | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - * 参考 {@link StreamMessageListenerContainer#create(RedisConnectionFactory, StreamMessageListenerContainerOptions)} 的实现 | ||
| 25 | - */ | ||
| 26 | - public static <K, V extends Record<K, ?>> StreamMessageListenerContainer<K, V> create(RedisConnectionFactory connectionFactory, StreamMessageListenerContainer.StreamMessageListenerContainerOptions<K, V> options) { | ||
| 27 | - Assert.notNull(connectionFactory, "RedisConnectionFactory must not be null!"); | ||
| 28 | - Assert.notNull(options, "StreamMessageListenerContainerOptions must not be null!"); | ||
| 29 | - return new DefaultStreamMessageListenerContainerX<>(connectionFactory, options); | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | - public DefaultStreamMessageListenerContainerX(RedisConnectionFactory connectionFactory, StreamMessageListenerContainerOptions<K, V> containerOptions) { | ||
| 33 | - super(connectionFactory, containerOptions); | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * 参考 {@link DefaultStreamMessageListenerContainer#register(StreamReadRequest, StreamListener)} 的实现 | ||
| 38 | - */ | ||
| 39 | - @Override | ||
| 40 | - public Subscription register(StreamReadRequest<K> streamRequest, StreamListener<K, V> listener) { | ||
| 41 | - return this.doRegisterX(getReadTaskX(streamRequest, listener)); | ||
| 42 | - } | ||
| 43 | - | ||
| 44 | - @SuppressWarnings("unchecked") | ||
| 45 | - private StreamPollTask<K, V> getReadTaskX(StreamReadRequest<K> streamRequest, StreamListener<K, V> listener) { | ||
| 46 | - StreamPollTask<K, V> task = ReflectUtil.invoke(this, "getReadTask", streamRequest, listener); | ||
| 47 | - // 修改 readFunction 方法 | ||
| 48 | - Function<ReadOffset, List<ByteRecord>> readFunction = (Function<ReadOffset, List<ByteRecord>>) ReflectUtil.getFieldValue(task, "readFunction"); | ||
| 49 | - ReflectUtil.setFieldValue(task, "readFunction", (Function<ReadOffset, List<ByteRecord>>) readOffset -> { | ||
| 50 | - List<ByteRecord> records = readFunction.apply(readOffset); | ||
| 51 | - //【重点】保证 records 不是空,避免 NPE 的问题!!! | ||
| 52 | - return records != null ? records : Collections.emptyList(); | ||
| 53 | - }); | ||
| 54 | - return task; | ||
| 55 | - } | ||
| 56 | - | ||
| 57 | - private Subscription doRegisterX(Task task) { | ||
| 58 | - return ReflectUtil.invoke(this, "doRegister", task); | ||
| 59 | - } | ||
| 60 | - | ||
| 61 | -} | ||
| 62 | - |
yudao-framework/yudao-spring-boot-starter-mq/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
| 1 | -cn.iocoder.yudao.framework.mq.config.YudaoMQAutoConfiguration | ||
| 2 | \ No newline at end of file | 1 | \ No newline at end of file |
| 2 | +cn.iocoder.yudao.framework.mq.redis.config.YudaoRedisMQAutoConfiguration | ||
| 3 | +cn.iocoder.yudao.framework.mq.rabbitmq.config.YudaoRabbitMQAutoConfiguration | ||
| 3 | \ No newline at end of file | 4 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 事件机制 Event 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/RocketMQ/?yudao> |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 Kafka 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/Kafka/?yudao> |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/RabbitMQ/?yudao> |
yudao-framework/yudao-spring-boot-starter-mq/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/RocketMQ/?yudao> |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/coupon/UserCreateConsumer.java renamed to yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/coupon/CouponTakeByRegisterConsumer.java
| 1 | package cn.iocoder.yudao.module.promotion.mq.consumer.coupon; | 1 | package cn.iocoder.yudao.module.promotion.mq.consumer.coupon; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | ||
| 4 | -import cn.iocoder.yudao.module.promotion.mq.message.coupon.UserCreateMessage; | 3 | +import cn.iocoder.yudao.module.member.message.user.MemberUserCreateMessage; |
| 5 | import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; | 4 | import cn.iocoder.yudao.module.promotion.service.coupon.CouponService; |
| 6 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 6 | +import org.springframework.context.event.EventListener; | ||
| 7 | +import org.springframework.scheduling.annotation.Async; | ||
| 7 | import org.springframework.stereotype.Component; | 8 | import org.springframework.stereotype.Component; |
| 8 | 9 | ||
| 9 | import javax.annotation.Resource; | 10 | import javax.annotation.Resource; |
| 10 | 11 | ||
| 11 | /** | 12 | /** |
| 12 | - * 针对 {@link UserCreateMessage} 的消费者 | 13 | + * 用户注册时,发送优惠劵的消费者,基 {@link MemberUserCreateMessage} 消息 |
| 13 | * | 14 | * |
| 14 | * @author owen | 15 | * @author owen |
| 15 | */ | 16 | */ |
| 16 | @Component | 17 | @Component |
| 17 | @Slf4j | 18 | @Slf4j |
| 18 | -public class UserCreateConsumer extends AbstractStreamMessageListener<UserCreateMessage> { | 19 | +public class CouponTakeByRegisterConsumer { |
| 19 | 20 | ||
| 20 | @Resource | 21 | @Resource |
| 21 | private CouponService couponService; | 22 | private CouponService couponService; |
| 22 | 23 | ||
| 23 | - @Override | ||
| 24 | - public void onMessage(UserCreateMessage message) { | 24 | + @EventListener |
| 25 | + @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 | ||
| 26 | + public void onMessage(MemberUserCreateMessage message) { | ||
| 25 | log.info("[onMessage][消息内容({})]", message); | 27 | log.info("[onMessage][消息内容({})]", message); |
| 26 | couponService.takeCouponByRegister(message.getUserId()); | 28 | couponService.takeCouponByRegister(message.getUserId()); |
| 27 | } | 29 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/consumer/package-info.java
0 → 100644
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/message/package-info.java
0 → 100644
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/producer/package-info.java
0 → 100644
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/message/package-info.java
0 → 100644
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/mq/message/coupon/UserCreateMessage.java renamed to yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/message/user/MemberUserCreateMessage.java
| 1 | -package cn.iocoder.yudao.module.promotion.mq.message.coupon; | 1 | +package cn.iocoder.yudao.module.member.message.user; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | ||
| 4 | import lombok.Data; | 3 | import lombok.Data; |
| 5 | -import lombok.EqualsAndHashCode; | ||
| 6 | 4 | ||
| 7 | import javax.validation.constraints.NotNull; | 5 | import javax.validation.constraints.NotNull; |
| 8 | 6 | ||
| @@ -12,8 +10,7 @@ import javax.validation.constraints.NotNull; | @@ -12,8 +10,7 @@ import javax.validation.constraints.NotNull; | ||
| 12 | * @author owen | 10 | * @author owen |
| 13 | */ | 11 | */ |
| 14 | @Data | 12 | @Data |
| 15 | -@EqualsAndHashCode(callSuper = true) | ||
| 16 | -public class UserCreateMessage extends AbstractStreamMessage { | 13 | +public class MemberUserCreateMessage { |
| 17 | 14 | ||
| 18 | /** | 15 | /** |
| 19 | * 用户编号 | 16 | * 用户编号 |
| @@ -21,9 +18,4 @@ public class UserCreateMessage extends AbstractStreamMessage { | @@ -21,9 +18,4 @@ public class UserCreateMessage extends AbstractStreamMessage { | ||
| 21 | @NotNull(message = "用户编号不能为空") | 18 | @NotNull(message = "用户编号不能为空") |
| 22 | private Long userId; | 19 | private Long userId; |
| 23 | 20 | ||
| 24 | - @Override | ||
| 25 | - public String getStreamKey() { | ||
| 26 | - return "member.user.create"; | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | } | 21 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
| @@ -4,7 +4,7 @@ Content-Type: application/json | @@ -4,7 +4,7 @@ Content-Type: application/json | ||
| 4 | tenant-id: {{appTenentId}} | 4 | tenant-id: {{appTenentId}} |
| 5 | 5 | ||
| 6 | { | 6 | { |
| 7 | - "mobile": "15601691300", | 7 | + "mobile": "15601691388", |
| 8 | "password": "admin123" | 8 | "password": "admin123" |
| 9 | } | 9 | } |
| 10 | 10 | ||
| @@ -14,7 +14,7 @@ Content-Type: application/json | @@ -14,7 +14,7 @@ Content-Type: application/json | ||
| 14 | tenant-id: {{appTenentId}} | 14 | tenant-id: {{appTenentId}} |
| 15 | 15 | ||
| 16 | { | 16 | { |
| 17 | - "mobile": "15601691399", | 17 | + "mobile": "15601691388", |
| 18 | "scene": 1 | 18 | "scene": 1 |
| 19 | } | 19 | } |
| 20 | 20 | ||
| @@ -22,9 +22,10 @@ tenant-id: {{appTenentId}} | @@ -22,9 +22,10 @@ tenant-id: {{appTenentId}} | ||
| 22 | POST {{appApi}}/member/auth/sms-login | 22 | POST {{appApi}}/member/auth/sms-login |
| 23 | Content-Type: application/json | 23 | Content-Type: application/json |
| 24 | tenant-id: {{appTenentId}} | 24 | tenant-id: {{appTenentId}} |
| 25 | +terminal: 30 | ||
| 25 | 26 | ||
| 26 | { | 27 | { |
| 27 | - "mobile": "15601691301", | 28 | + "mobile": "15601691388", |
| 28 | "code": 9999 | 29 | "code": 9999 |
| 29 | } | 30 | } |
| 30 | 31 |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/consumer/package-info.java
0 → 100644
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/message/package-info.java
0 → 100644
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/message/user/MemberUserCreateMessage.java deleted
| 1 | -package cn.iocoder.yudao.module.member.mq.message.user; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | ||
| 4 | -import lombok.Data; | ||
| 5 | -import lombok.EqualsAndHashCode; | ||
| 6 | - | ||
| 7 | -import javax.validation.constraints.NotNull; | ||
| 8 | - | ||
| 9 | -/** | ||
| 10 | - * 会员用户创建消息 | ||
| 11 | - * | ||
| 12 | - * @author owen | ||
| 13 | - */ | ||
| 14 | -@Data | ||
| 15 | -@EqualsAndHashCode(callSuper = true) | ||
| 16 | -public class MemberUserCreateMessage extends AbstractStreamMessage { | ||
| 17 | - | ||
| 18 | - /** | ||
| 19 | - * 用户编号 | ||
| 20 | - */ | ||
| 21 | - @NotNull(message = "用户编号不能为空") | ||
| 22 | - private Long userId; | ||
| 23 | - | ||
| 24 | - @Override | ||
| 25 | - public String getStreamKey() { | ||
| 26 | - return "member.user.create"; | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | -} |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/producer/package-info.java
0 → 100644
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/mq/producer/user/MemberUserProducer.java
| 1 | package cn.iocoder.yudao.module.member.mq.producer.user; | 1 | package cn.iocoder.yudao.module.member.mq.producer.user; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 4 | -import cn.iocoder.yudao.module.member.mq.message.user.MemberUserCreateMessage; | 3 | +import cn.iocoder.yudao.module.member.message.user.MemberUserCreateMessage; |
| 5 | import lombok.extern.slf4j.Slf4j; | 4 | import lombok.extern.slf4j.Slf4j; |
| 5 | +import org.springframework.context.ApplicationContext; | ||
| 6 | import org.springframework.stereotype.Component; | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ||
| 8 | import javax.annotation.Resource; | 8 | import javax.annotation.Resource; |
| @@ -17,7 +17,7 @@ import javax.annotation.Resource; | @@ -17,7 +17,7 @@ import javax.annotation.Resource; | ||
| 17 | public class MemberUserProducer { | 17 | public class MemberUserProducer { |
| 18 | 18 | ||
| 19 | @Resource | 19 | @Resource |
| 20 | - private RedisMQTemplate redisMQTemplate; | 20 | + private ApplicationContext applicationContext; |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| 23 | * 发送 {@link MemberUserCreateMessage} 消息 | 23 | * 发送 {@link MemberUserCreateMessage} 消息 |
| @@ -25,7 +25,7 @@ public class MemberUserProducer { | @@ -25,7 +25,7 @@ public class MemberUserProducer { | ||
| 25 | * @param userId 用户编号 | 25 | * @param userId 用户编号 |
| 26 | */ | 26 | */ |
| 27 | public void sendUserCreateMessage(Long userId) { | 27 | public void sendUserCreateMessage(Long userId) { |
| 28 | - redisMQTemplate.send(new MemberUserCreateMessage().setUserId(userId)); | 28 | + applicationContext.publishEvent(new MemberUserCreateMessage().setUserId(userId)); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | } | 31 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http
| @@ -6,9 +6,9 @@ tenant-id: {{adminTenentId}} | @@ -6,9 +6,9 @@ tenant-id: {{adminTenentId}} | ||
| 6 | 6 | ||
| 7 | { | 7 | { |
| 8 | "templateCode": "test_01", | 8 | "templateCode": "test_01", |
| 9 | - "mobile": "156016913900", | ||
| 10 | - "params": { | ||
| 11 | - "key01": "value01", | ||
| 12 | - "key02": "value02" | 9 | + "mobile": "15601691390", |
| 10 | + "templateParams": { | ||
| 11 | + "operation": "value01", | ||
| 12 | + "code": "value02" | ||
| 13 | } | 13 | } |
| 14 | } | 14 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java
| 1 | package cn.iocoder.yudao.module.system.mq.consumer.mail; | 1 | package cn.iocoder.yudao.module.system.mq.consumer.mail; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | ||
| 4 | import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; | 3 | import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; |
| 5 | -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; | ||
| 6 | import cn.iocoder.yudao.module.system.service.mail.MailSendService; | 4 | import cn.iocoder.yudao.module.system.service.mail.MailSendService; |
| 7 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 6 | +import org.springframework.context.event.EventListener; | ||
| 7 | +import org.springframework.scheduling.annotation.Async; | ||
| 8 | import org.springframework.stereotype.Component; | 8 | import org.springframework.stereotype.Component; |
| 9 | 9 | ||
| 10 | import javax.annotation.Resource; | 10 | import javax.annotation.Resource; |
| @@ -16,12 +16,13 @@ import javax.annotation.Resource; | @@ -16,12 +16,13 @@ import javax.annotation.Resource; | ||
| 16 | */ | 16 | */ |
| 17 | @Component | 17 | @Component |
| 18 | @Slf4j | 18 | @Slf4j |
| 19 | -public class MailSendConsumer extends AbstractStreamMessageListener<MailSendMessage> { | 19 | +public class MailSendConsumer { |
| 20 | 20 | ||
| 21 | @Resource | 21 | @Resource |
| 22 | private MailSendService mailSendService; | 22 | private MailSendService mailSendService; |
| 23 | 23 | ||
| 24 | - @Override | 24 | + @EventListener |
| 25 | + @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 | ||
| 25 | public void onMessage(MailSendMessage message) { | 26 | public void onMessage(MailSendMessage message) { |
| 26 | log.info("[onMessage][消息内容({})]", message); | 27 | log.info("[onMessage][消息内容({})]", message); |
| 27 | mailSendService.doSendMail(message); | 28 | mailSendService.doSendMail(message); |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java
| @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.system.mq.consumer.sms; | @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.system.mq.consumer.sms; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; | 3 | import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; |
| 4 | import cn.iocoder.yudao.module.system.service.sms.SmsSendService; | 4 | import cn.iocoder.yudao.module.system.service.sms.SmsSendService; |
| 5 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener; | ||
| 6 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 6 | +import org.springframework.context.event.EventListener; | ||
| 7 | +import org.springframework.scheduling.annotation.Async; | ||
| 7 | import org.springframework.stereotype.Component; | 8 | import org.springframework.stereotype.Component; |
| 8 | 9 | ||
| 9 | import javax.annotation.Resource; | 10 | import javax.annotation.Resource; |
| @@ -15,12 +16,13 @@ import javax.annotation.Resource; | @@ -15,12 +16,13 @@ import javax.annotation.Resource; | ||
| 15 | */ | 16 | */ |
| 16 | @Component | 17 | @Component |
| 17 | @Slf4j | 18 | @Slf4j |
| 18 | -public class SmsSendConsumer extends AbstractStreamMessageListener<SmsSendMessage> { | 19 | +public class SmsSendConsumer { |
| 19 | 20 | ||
| 20 | @Resource | 21 | @Resource |
| 21 | private SmsSendService smsSendService; | 22 | private SmsSendService smsSendService; |
| 22 | 23 | ||
| 23 | - @Override | 24 | + @EventListener |
| 25 | + @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 | ||
| 24 | public void onMessage(SmsSendMessage message) { | 26 | public void onMessage(SmsSendMessage message) { |
| 25 | log.info("[onMessage][消息内容({})]", message); | 27 | log.info("[onMessage][消息内容({})]", message); |
| 26 | smsSendService.doSendSms(message); | 28 | smsSendService.doSendSms(message); |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
| 1 | package cn.iocoder.yudao.module.system.mq.message.mail; | 1 | package cn.iocoder.yudao.module.system.mq.message.mail; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | ||
| 4 | import lombok.Data; | 3 | import lombok.Data; |
| 5 | -import lombok.EqualsAndHashCode; | ||
| 6 | 4 | ||
| 7 | import javax.validation.constraints.NotEmpty; | 5 | import javax.validation.constraints.NotEmpty; |
| 8 | import javax.validation.constraints.NotNull; | 6 | import javax.validation.constraints.NotNull; |
| @@ -13,8 +11,7 @@ import javax.validation.constraints.NotNull; | @@ -13,8 +11,7 @@ import javax.validation.constraints.NotNull; | ||
| 13 | * @author 芋道源码 | 11 | * @author 芋道源码 |
| 14 | */ | 12 | */ |
| 15 | @Data | 13 | @Data |
| 16 | -@EqualsAndHashCode(callSuper = true) | ||
| 17 | -public class MailSendMessage extends AbstractStreamMessage { | 14 | +public class MailSendMessage { |
| 18 | 15 | ||
| 19 | /** | 16 | /** |
| 20 | * 邮件日志编号 | 17 | * 邮件日志编号 |
| @@ -47,9 +44,4 @@ public class MailSendMessage extends AbstractStreamMessage { | @@ -47,9 +44,4 @@ public class MailSendMessage extends AbstractStreamMessage { | ||
| 47 | @NotEmpty(message = "邮件内容不能为空") | 44 | @NotEmpty(message = "邮件内容不能为空") |
| 48 | private String content; | 45 | private String content; |
| 49 | 46 | ||
| 50 | - @Override | ||
| 51 | - public String getStreamKey() { | ||
| 52 | - return "system.mail.send"; | ||
| 53 | - } | ||
| 54 | - | ||
| 55 | } | 47 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java
| 1 | package cn.iocoder.yudao.module.system.mq.message.sms; | 1 | package cn.iocoder.yudao.module.system.mq.message.sms; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.core.KeyValue; | 3 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage; | ||
| 5 | import lombok.Data; | 4 | import lombok.Data; |
| 6 | -import lombok.EqualsAndHashCode; | ||
| 7 | 5 | ||
| 8 | import javax.validation.constraints.NotNull; | 6 | import javax.validation.constraints.NotNull; |
| 9 | import java.util.List; | 7 | import java.util.List; |
| @@ -14,8 +12,7 @@ import java.util.List; | @@ -14,8 +12,7 @@ import java.util.List; | ||
| 14 | * @author 芋道源码 | 12 | * @author 芋道源码 |
| 15 | */ | 13 | */ |
| 16 | @Data | 14 | @Data |
| 17 | -@EqualsAndHashCode(callSuper = true) | ||
| 18 | -public class SmsSendMessage extends AbstractStreamMessage { | 15 | +public class SmsSendMessage { |
| 19 | 16 | ||
| 20 | /** | 17 | /** |
| 21 | * 短信日志编号 | 18 | * 短信日志编号 |
| @@ -42,9 +39,4 @@ public class SmsSendMessage extends AbstractStreamMessage { | @@ -42,9 +39,4 @@ public class SmsSendMessage extends AbstractStreamMessage { | ||
| 42 | */ | 39 | */ |
| 43 | private List<KeyValue<String, Object>> templateParams; | 40 | private List<KeyValue<String, Object>> templateParams; |
| 44 | 41 | ||
| 45 | - @Override | ||
| 46 | - public String getStreamKey() { | ||
| 47 | - return "system.sms.send"; | ||
| 48 | - } | ||
| 49 | - | ||
| 50 | } | 42 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java
| 1 | package cn.iocoder.yudao.module.system.mq.producer.mail; | 1 | package cn.iocoder.yudao.module.system.mq.producer.mail; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 4 | import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; | 3 | import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; |
| 5 | import lombok.extern.slf4j.Slf4j; | 4 | import lombok.extern.slf4j.Slf4j; |
| 5 | +import org.springframework.context.ApplicationContext; | ||
| 6 | import org.springframework.stereotype.Component; | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ||
| 8 | import javax.annotation.Resource; | 8 | import javax.annotation.Resource; |
| @@ -18,7 +18,7 @@ import javax.annotation.Resource; | @@ -18,7 +18,7 @@ import javax.annotation.Resource; | ||
| 18 | public class MailProducer { | 18 | public class MailProducer { |
| 19 | 19 | ||
| 20 | @Resource | 20 | @Resource |
| 21 | - private RedisMQTemplate redisMQTemplate; | 21 | + private ApplicationContext applicationContext; |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | * 发送 {@link MailSendMessage} 消息 | 24 | * 发送 {@link MailSendMessage} 消息 |
| @@ -35,7 +35,7 @@ public class MailProducer { | @@ -35,7 +35,7 @@ public class MailProducer { | ||
| 35 | MailSendMessage message = new MailSendMessage() | 35 | MailSendMessage message = new MailSendMessage() |
| 36 | .setLogId(sendLogId).setMail(mail).setAccountId(accountId) | 36 | .setLogId(sendLogId).setMail(mail).setAccountId(accountId) |
| 37 | .setNickname(nickname).setTitle(title).setContent(content); | 37 | .setNickname(nickname).setTitle(title).setContent(content); |
| 38 | - redisMQTemplate.send(message); | 38 | + applicationContext.publishEvent(message); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | } | 41 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java
| 1 | package cn.iocoder.yudao.module.system.mq.producer.sms; | 1 | package cn.iocoder.yudao.module.system.mq.producer.sms; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.core.KeyValue; | 3 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 4 | -import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||
| 5 | import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; | 4 | import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; |
| 6 | import lombok.extern.slf4j.Slf4j; | 5 | import lombok.extern.slf4j.Slf4j; |
| 6 | +import org.springframework.context.ApplicationContext; | ||
| 7 | import org.springframework.stereotype.Component; | 7 | import org.springframework.stereotype.Component; |
| 8 | 8 | ||
| 9 | import javax.annotation.Resource; | 9 | import javax.annotation.Resource; |
| @@ -20,7 +20,7 @@ import java.util.List; | @@ -20,7 +20,7 @@ import java.util.List; | ||
| 20 | public class SmsProducer { | 20 | public class SmsProducer { |
| 21 | 21 | ||
| 22 | @Resource | 22 | @Resource |
| 23 | - private RedisMQTemplate redisMQTemplate; | 23 | + private ApplicationContext applicationContext; |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| 26 | * 发送 {@link SmsSendMessage} 消息 | 26 | * 发送 {@link SmsSendMessage} 消息 |
| @@ -35,7 +35,7 @@ public class SmsProducer { | @@ -35,7 +35,7 @@ public class SmsProducer { | ||
| 35 | Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) { | 35 | Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) { |
| 36 | SmsSendMessage message = new SmsSendMessage().setLogId(logId).setMobile(mobile); | 36 | SmsSendMessage message = new SmsSendMessage().setLogId(logId).setMobile(mobile); |
| 37 | message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams); | 37 | message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams); |
| 38 | - redisMQTemplate.send(message); | 38 | + applicationContext.publishEvent(message); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | } | 41 | } |
yudao-server/src/main/resources/application-dev.yaml
| @@ -94,6 +94,23 @@ spring: | @@ -94,6 +94,23 @@ spring: | ||
| 94 | jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 | 94 | jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 |
| 95 | initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 | 95 | initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 |
| 96 | 96 | ||
| 97 | +--- #################### 消息队列相关 #################### | ||
| 98 | + | ||
| 99 | +# rocketmq 配置项,对应 RocketMQProperties 配置类 | ||
| 100 | +rocketmq: | ||
| 101 | + name-server: 127.0.0.1:9876 # RocketMQ Namesrv | ||
| 102 | + | ||
| 103 | +spring: | ||
| 104 | + # RabbitMQ 配置项,对应 RabbitProperties 配置类 | ||
| 105 | + rabbitmq: | ||
| 106 | + host: 127.0.0.1 # RabbitMQ 服务的地址 | ||
| 107 | + port: 5672 # RabbitMQ 服务的端口 | ||
| 108 | + username: guest # RabbitMQ 服务的账号 | ||
| 109 | + password: guest # RabbitMQ 服务的密码 | ||
| 110 | + # Kafka 配置项,对应 KafkaProperties 配置类 | ||
| 111 | + kafka: | ||
| 112 | + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 | ||
| 113 | + | ||
| 97 | --- #################### 服务保障相关配置 #################### | 114 | --- #################### 服务保障相关配置 #################### |
| 98 | 115 | ||
| 99 | # Lock4j 配置项 | 116 | # Lock4j 配置项 |
yudao-server/src/main/resources/application-local.yaml
| @@ -109,6 +109,23 @@ spring: | @@ -109,6 +109,23 @@ spring: | ||
| 109 | jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 | 109 | jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 |
| 110 | initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 | 110 | initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 |
| 111 | 111 | ||
| 112 | +--- #################### 消息队列相关 #################### | ||
| 113 | + | ||
| 114 | +# rocketmq 配置项,对应 RocketMQProperties 配置类 | ||
| 115 | +rocketmq: | ||
| 116 | + name-server: 127.0.0.1:9876 # RocketMQ Namesrv | ||
| 117 | + | ||
| 118 | +spring: | ||
| 119 | + # RabbitMQ 配置项,对应 RabbitProperties 配置类 | ||
| 120 | + rabbitmq: | ||
| 121 | + host: 127.0.0.1 # RabbitMQ 服务的地址 | ||
| 122 | + port: 5672 # RabbitMQ 服务的端口 | ||
| 123 | + username: guest # RabbitMQ 服务的账号 | ||
| 124 | + password: guest # RabbitMQ 服务的密码 | ||
| 125 | + # Kafka 配置项,对应 KafkaProperties 配置类 | ||
| 126 | + kafka: | ||
| 127 | + bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址,可以设置多个,以逗号分隔 | ||
| 128 | + | ||
| 112 | --- #################### 服务保障相关配置 #################### | 129 | --- #################### 服务保障相关配置 #################### |
| 113 | 130 | ||
| 114 | # Lock4j 配置项 | 131 | # Lock4j 配置项 |
yudao-server/src/main/resources/application.yaml
| @@ -106,6 +106,32 @@ aj: | @@ -106,6 +106,32 @@ aj: | ||
| 106 | req-check-minute-limit: 60 # check 接口一分钟内请求数限制 | 106 | req-check-minute-limit: 60 # check 接口一分钟内请求数限制 |
| 107 | req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制 | 107 | req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制 |
| 108 | 108 | ||
| 109 | +--- #################### 消息队列相关 #################### | ||
| 110 | + | ||
| 111 | +# rocketmq 配置项,对应 RocketMQProperties 配置类 | ||
| 112 | +rocketmq: | ||
| 113 | + # Producer 配置项 | ||
| 114 | + producer: | ||
| 115 | + group: ${spring.application.name}_PRODUCER # 生产者分组 | ||
| 116 | + | ||
| 117 | +spring: | ||
| 118 | + # Kafka 配置项,对应 KafkaProperties 配置类 | ||
| 119 | + kafka: | ||
| 120 | + # Kafka Producer 配置项 | ||
| 121 | + producer: | ||
| 122 | + acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。 | ||
| 123 | + retries: 3 # 发送失败时,重试发送的次数 | ||
| 124 | + value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化 | ||
| 125 | + # Kafka Consumer 配置项 | ||
| 126 | + consumer: | ||
| 127 | + auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解 | ||
| 128 | + value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer | ||
| 129 | + properties: | ||
| 130 | + spring.json.trusted.packages: '*' | ||
| 131 | + # Kafka Consumer Listener 监听器配置 | ||
| 132 | + listener: | ||
| 133 | + missing-topics-fatal: false # 消费监听接口监听的主题不存在时,默认会报错。所以通过设置为 false ,解决报错 | ||
| 134 | + | ||
| 109 | --- #################### 芋道相关配置 #################### | 135 | --- #################### 芋道相关配置 #################### |
| 110 | 136 | ||
| 111 | yudao: | 137 | yudao: |
| @@ -145,12 +171,6 @@ yudao: | @@ -145,12 +171,6 @@ yudao: | ||
| 145 | - cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants | 171 | - cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants |
| 146 | - cn.iocoder.yudao.module.system.enums.ErrorCodeConstants | 172 | - cn.iocoder.yudao.module.system.enums.ErrorCodeConstants |
| 147 | - cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants | 173 | - cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants |
| 148 | - mq: | ||
| 149 | - redis: | ||
| 150 | - pubsub: | ||
| 151 | - enable: false # 是否开启 Redis pubsub 广播消费,默认为 true。这里设置成 false,可以按需开启 | ||
| 152 | - stream: | ||
| 153 | - enable: false # 是否开启 Redis stream 集群消费,默认为 true。这里设置成 false,可以按需开启 | ||
| 154 | tenant: # 多租户相关配置项 | 174 | tenant: # 多租户相关配置项 |
| 155 | enable: true | 175 | enable: true |
| 156 | ignore-urls: | 176 | ignore-urls: |