Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Y
yudao-cloud
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
hblj
yudao-cloud
Commits
4381d938
提交
4381d938
authored
6月 22, 2022
作者:
YunaiV
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1. 接入 spring cloud stream,支持多租户
2. 弱化 spring cloud dubbo 集成,可通过加入依赖自动实现
上级
4807547d
显示空白字符变更
内嵌
并排
正在显示
21 个修改的文件
包含
125 行增加
和
474 行删除
+125
-474
pom.xml
yudao-dependencies/pom.xml
+7
-0
YudaoTenantAutoConfiguration.java
...framework/tenant/config/YudaoTenantAutoConfiguration.java
+12
-4
TenantChannelInterceptor.java
...ao/framework/tenant/core/mq/TenantChannelInterceptor.java
+32
-0
TenantFunctionAroundWrapper.java
...framework/tenant/core/mq/TenantFunctionAroundWrapper.java
+36
-0
TenantRedisMessageInterceptor.java
...amework/tenant/core/mq/TenantRedisMessageInterceptor.java
+0
-42
TenantUtils.java
...iocoder/yudao/framework/tenant/core/util/TenantUtils.java
+25
-0
pom.xml
yudao-framework/yudao-spring-boot-starter-mq/pom.xml
+0
-6
YudaoMQAutoConfiguration.java
...r/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
+0
-16
RedisMQTemplate.java
...a/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java
+0
-87
RedisMessageInterceptor.java
...ramework/mq/core/interceptor/RedisMessageInterceptor.java
+0
-26
AbstractRedisMessage.java
...yudao/framework/mq/core/message/AbstractRedisMessage.java
+0
-29
package-info.java
...java/cn/iocoder/yudao/framework/mq/core/package-info.java
+4
-0
AbstractChannelMessage.java
...udao/framework/mq/core/pubsub/AbstractChannelMessage.java
+0
-21
AbstractChannelMessageListener.java
...mework/mq/core/pubsub/AbstractChannelMessageListener.java
+0
-103
AbstractStreamMessage.java
...yudao/framework/mq/core/stream/AbstractStreamMessage.java
+0
-21
AbstractStreamMessageListener.java
...amework/mq/core/stream/AbstractStreamMessageListener.java
+0
-113
pom.xml
yudao-framework/yudao-spring-boot-starter-rpc/pom.xml
+7
-2
application.yaml
...udao-module-infra-biz/src/main/resources/application.yaml
+1
-0
MailSendConsumer.java
...udao/module/system/mq/consumer/mail/MailSendConsumer.java
+0
-1
MailSendMessage.java
.../yudao/module/system/mq/message/mail/MailSendMessage.java
+0
-3
application.yaml
...dao-module-system-biz/src/main/resources/application.yaml
+1
-0
没有找到文件。
yudao-dependencies/pom.xml
浏览文件 @
4381d938
...
...
@@ -29,6 +29,8 @@
<mybatis-plus-generator.version>
3.5.2
</mybatis-plus-generator.version>
<dynamic-datasource.version>
3.5.0
</dynamic-datasource.version>
<redisson.version>
3.17.3
</redisson.version>
<!-- RPC 相关 -->
<dubbo.version>
2.7.15
</dubbo.version>
<!-- Config 配置中心相关 -->
<apollo.version>
1.9.2
</apollo.version>
<!-- Job 定时任务相关 -->
...
...
@@ -233,6 +235,11 @@
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>
org.apache.dubbo
</groupId>
<artifactId>
dubbo-common
</artifactId>
<!-- 兜底,保证在不引入 spring-cloud-starter-dubbo 时,注解等不报错 -->
<version>
${dubbo.version}
</version>
</dependency>
<dependency>
<groupId>
cn.iocoder.cloud
</groupId>
<artifactId>
yudao-spring-boot-starter-rpc
</artifactId>
...
...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
浏览文件 @
4381d938
...
...
@@ -8,7 +8,8 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
import
cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor
;
import
cn.iocoder.yudao.framework.tenant.core.job.TenantJob
;
import
cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator
;
import
cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor
;
import
cn.iocoder.yudao.framework.tenant.core.mq.TenantChannelInterceptor
;
import
cn.iocoder.yudao.framework.tenant.core.mq.TenantFunctionAroundWrapper
;
import
cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter
;
import
cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService
;
import
cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl
;
...
...
@@ -23,8 +24,10 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
;
import
org.springframework.boot.context.properties.EnableConfigurationProperties
;
import
org.springframework.boot.web.servlet.FilterRegistrationBean
;
import
org.springframework.cloud.function.context.catalog.FunctionAroundWrapper
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.integration.config.GlobalChannelInterceptor
;
@Configuration
@ConditionalOnProperty
(
prefix
=
"yudao.tenant"
,
value
=
"enable"
,
matchIfMissing
=
true
)
// 允许使用 yudao.tenant.enable=false 禁用多租户
...
...
@@ -82,14 +85,19 @@ public class YudaoTenantAutoConfiguration {
// ========== MQ ==========
@Bean
public
TenantRedisMessageInterceptor
tenantRedisMessageInterceptor
()
{
return
new
TenantRedisMessageInterceptor
();
@GlobalChannelInterceptor
// 必须添加在方法上,否则无法生效
public
TenantChannelInterceptor
tenantChannelInterceptor
()
{
return
new
TenantChannelInterceptor
();
}
@Bean
public
FunctionAroundWrapper
functionAroundWrapper
()
{
return
new
TenantFunctionAroundWrapper
();
}
// ========== Job ==========
@Bean
@SuppressWarnings
(
"SpringJavaInjectionPointsAutowiringInspection"
)
public
BeanPostProcessor
jobHandlerBeanPostProcessor
(
TenantFrameworkService
tenantFrameworkService
)
{
return
new
BeanPostProcessor
()
{
...
...
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantChannelInterceptor.java
0 → 100644
浏览文件 @
4381d938
package
cn
.
iocoder
.
yudao
.
framework
.
tenant
.
core
.
mq
;
import
cn.hutool.core.util.ReflectUtil
;
import
cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder
;
import
org.springframework.messaging.Message
;
import
org.springframework.messaging.MessageChannel
;
import
org.springframework.messaging.support.ChannelInterceptor
;
import
java.util.Map
;
import
static
cn
.
iocoder
.
yudao
.
framework
.
web
.
core
.
util
.
WebFrameworkUtils
.
HEADER_TENANT_ID
;
/**
* 多租户的 {@link ChannelInterceptor} 实现类
* 发送消息时,设置租户编号到 Header 上
*
* @author 芋道源码
*/
public
class
TenantChannelInterceptor
implements
ChannelInterceptor
{
@Override
@SuppressWarnings
({
"unchecked"
,
"NullableProblems"
})
public
Message
<?>
preSend
(
Message
<?>
message
,
MessageChannel
channel
)
{
Long
tenantId
=
TenantContextHolder
.
getTenantId
();
if
(
tenantId
!=
null
)
{
Map
<
String
,
Object
>
headers
=
(
Map
<
String
,
Object
>)
ReflectUtil
.
getFieldValue
(
message
.
getHeaders
(),
"headers"
);
headers
.
put
(
HEADER_TENANT_ID
,
tenantId
);
}
return
message
;
}
}
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantFunctionAroundWrapper.java
0 → 100644
浏览文件 @
4381d938
package
cn
.
iocoder
.
yudao
.
framework
.
tenant
.
core
.
mq
;
import
cn.hutool.core.map.MapUtil
;
import
cn.iocoder.yudao.framework.tenant.core.util.TenantUtils
;
import
org.springframework.cloud.function.context.catalog.FunctionAroundWrapper
;
import
org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry
;
import
org.springframework.messaging.Message
;
import
static
cn
.
iocoder
.
yudao
.
framework
.
web
.
core
.
util
.
WebFrameworkUtils
.
HEADER_TENANT_ID
;
/**
* 多租户 FunctionAroundWrapper 实现类
* 消费消息时,设置租户编号到 Context 上
*
* @author 芋道源码
*/
public
class
TenantFunctionAroundWrapper
extends
FunctionAroundWrapper
{
@Override
protected
Object
doApply
(
Object
input
,
SimpleFunctionRegistry
.
FunctionInvocationWrapper
targetFunction
)
{
// 如果不是 MQ 消息,则直接跳过
if
(!(
input
instanceof
Message
))
{
return
targetFunction
.
apply
(
input
);
}
// 如果没有多租户,则直接跳过
Message
<?>
message
=
(
Message
<?>)
input
;
Long
tenantId
=
MapUtil
.
getLong
(
message
.
getHeaders
(),
HEADER_TENANT_ID
);
if
(
tenantId
==
null
)
{
return
targetFunction
.
apply
(
input
);
}
// 如果有多租户,则使用多租户上下文
return
TenantUtils
.
execute
(
tenantId
,
()
->
targetFunction
.
apply
(
input
));
}
}
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
tenant
.
core
.
mq
;
import
cn.hutool.core.util.StrUtil
;
import
cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
import
cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder
;
/**
* 多租户 {@link AbstractRedisMessage} 拦截器
*
* 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中
* 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中
*
* @author 芋道源码
*/
public
class
TenantRedisMessageInterceptor
implements
RedisMessageInterceptor
{
private
static
final
String
HEADER_TENANT_ID
=
"tenant-id"
;
@Override
public
void
sendMessageBefore
(
AbstractRedisMessage
message
)
{
Long
tenantId
=
TenantContextHolder
.
getTenantId
();
if
(
tenantId
!=
null
)
{
message
.
addHeader
(
HEADER_TENANT_ID
,
tenantId
.
toString
());
}
}
@Override
public
void
consumeMessageBefore
(
AbstractRedisMessage
message
)
{
String
tenantIdStr
=
message
.
getHeader
(
HEADER_TENANT_ID
);
if
(
StrUtil
.
isNotEmpty
(
tenantIdStr
))
{
TenantContextHolder
.
setTenantId
(
Long
.
valueOf
(
tenantIdStr
));
}
}
@Override
public
void
consumeMessageAfter
(
AbstractRedisMessage
message
)
{
// 注意,Consumer 是一个逻辑的入口,所以不考虑原本上下文就存在租户编号的情况
TenantContextHolder
.
clear
();
}
}
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
浏览文件 @
4381d938
...
...
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.tenant.core.util;
import
cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder
;
import
java.util.function.Supplier
;
/**
* 多租户 Util
*
...
...
@@ -32,4 +34,27 @@ public class TenantUtils {
}
}
/**
* 使用指定租户,执行对应的逻辑
*
* 注意,如果当前是忽略租户的情况下,会被强制设置成不忽略租户
* 当然,执行完成后,还是会恢复回去
*
* @param tenantId 租户编号
* @param supplier 逻辑
*/
public
static
<
T
>
T
execute
(
Long
tenantId
,
Supplier
<
T
>
supplier
)
{
Long
oldTenantId
=
TenantContextHolder
.
getTenantId
();
Boolean
oldIgnore
=
TenantContextHolder
.
isIgnore
();
try
{
TenantContextHolder
.
setTenantId
(
tenantId
);
TenantContextHolder
.
setIgnore
(
false
);
// 执行逻辑
return
supplier
.
get
();
}
finally
{
TenantContextHolder
.
setTenantId
(
oldTenantId
);
TenantContextHolder
.
setIgnore
(
oldIgnore
);
}
}
}
yudao-framework/yudao-spring-boot-starter-mq/pom.xml
浏览文件 @
4381d938
...
...
@@ -20,12 +20,6 @@
<url>
https://github.com/YunaiV/ruoyi-vue-pro
</url>
<dependencies>
<!-- DB 相关 -->
<dependency>
<groupId>
cn.iocoder.cloud
</groupId>
<artifactId>
yudao-spring-boot-starter-redis
</artifactId>
</dependency>
<!-- MQ 相关 -->
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
...
...
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/config/YudaoMQAutoConfiguration.java
浏览文件 @
4381d938
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
config
;
import
cn.iocoder.yudao.framework.mq.core.RedisMQTemplate
;
import
cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor
;
import
cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration
;
import
com.alibaba.cloud.stream.binder.rocketmq.convert.RocketMQMessageConverter
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.boot.autoconfigure.AutoConfigureAfter
;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.messaging.converter.*
;
import
java.util.ArrayList
;
...
...
@@ -21,19 +16,8 @@ import java.util.List;
* @author 芋道源码
*/
@Configuration
@AutoConfigureAfter
(
YudaoRedisAutoConfiguration
.
class
)
@Slf4j
public
class
YudaoMQAutoConfiguration
{
@Bean
public
RedisMQTemplate
redisMQTemplate
(
StringRedisTemplate
redisTemplate
,
List
<
RedisMessageInterceptor
>
interceptors
)
{
RedisMQTemplate
redisMQTemplate
=
new
RedisMQTemplate
(
redisTemplate
);
// 添加拦截器
interceptors
.
forEach
(
redisMQTemplate:
:
addInterceptor
);
return
redisMQTemplate
;
}
/**
* 覆盖 {@link RocketMQMessageConverter} 的配置,去掉 fastjson 的转换器,解决不兼容的问题
*/
...
...
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/RedisMQTemplate.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
;
import
cn.iocoder.yudao.framework.common.util.json.JsonUtils
;
import
cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
import
cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage
;
import
cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage
;
import
lombok.AllArgsConstructor
;
import
lombok.Getter
;
import
org.springframework.data.redis.connection.stream.RecordId
;
import
org.springframework.data.redis.connection.stream.StreamRecords
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* Redis MQ 操作模板类
*
* @author 芋道源码
*/
@AllArgsConstructor
public
class
RedisMQTemplate
{
@Getter
private
final
RedisTemplate
<
String
,
?>
redisTemplate
;
/**
* 拦截器数组
*/
@Getter
private
final
List
<
RedisMessageInterceptor
>
interceptors
=
new
ArrayList
<>();
/**
* 发送 Redis 消息,基于 Redis pub/sub 实现
*
* @param message 消息
*/
public
<
T
extends
AbstractChannelMessage
>
void
send
(
T
message
)
{
try
{
sendMessageBefore
(
message
);
// 发送消息
redisTemplate
.
convertAndSend
(
message
.
getChannel
(),
JsonUtils
.
toJsonString
(
message
));
}
finally
{
sendMessageAfter
(
message
);
}
}
/**
* 发送 Redis 消息,基于 Redis Stream 实现
*
* @param message 消息
* @return 消息记录的编号对象
*/
public
<
T
extends
AbstractStreamMessage
>
RecordId
send
(
T
message
)
{
try
{
sendMessageBefore
(
message
);
// 发送消息
return
redisTemplate
.
opsForStream
().
add
(
StreamRecords
.
newRecord
()
.
ofObject
(
JsonUtils
.
toJsonString
(
message
))
// 设置内容
.
withStreamKey
(
message
.
getStreamKey
()));
// 设置 stream key
}
finally
{
sendMessageAfter
(
message
);
}
}
/**
* 添加拦截器
*
* @param interceptor 拦截器
*/
public
void
addInterceptor
(
RedisMessageInterceptor
interceptor
)
{
interceptors
.
add
(
interceptor
);
}
private
void
sendMessageBefore
(
AbstractRedisMessage
message
)
{
// 正序
interceptors
.
forEach
(
interceptor
->
interceptor
.
sendMessageBefore
(
message
));
}
private
void
sendMessageAfter
(
AbstractRedisMessage
message
)
{
// 倒序
for
(
int
i
=
interceptors
.
size
()
-
1
;
i
>=
0
;
i
--)
{
interceptors
.
get
(
i
).
sendMessageAfter
(
message
);
}
}
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/interceptor/RedisMessageInterceptor.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
.
interceptor
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
/**
* {@link AbstractRedisMessage} 消息拦截器
* 通过拦截器,作为插件机制,实现拓展。
* 例如说,多租户场景下的 MQ 消息处理
*
* @author 芋道源码
*/
public
interface
RedisMessageInterceptor
{
default
void
sendMessageBefore
(
AbstractRedisMessage
message
)
{
}
default
void
sendMessageAfter
(
AbstractRedisMessage
message
)
{
}
default
void
consumeMessageBefore
(
AbstractRedisMessage
message
)
{
}
default
void
consumeMessageAfter
(
AbstractRedisMessage
message
)
{
}
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
.
message
;
import
lombok.Data
;
import
java.util.HashMap
;
import
java.util.Map
;
/**
* Redis 消息抽象基类
*
* @author 芋道源码
*/
@Data
public
abstract
class
AbstractRedisMessage
{
/**
* 头
*/
private
Map
<
String
,
String
>
headers
=
new
HashMap
<>();
public
String
getHeader
(
String
key
)
{
return
headers
.
get
(
key
);
}
public
void
addHeader
(
String
key
,
String
value
)
{
headers
.
put
(
key
,
value
);
}
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/package-info.java
0 → 100644
浏览文件 @
4381d938
/**
* TODO 芋艿,后续删除,临时占位
*/
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
;
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
.
pubsub
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
import
com.fasterxml.jackson.annotation.JsonIgnore
;
/**
* Redis Channel Message 抽象类
*
* @author 芋道源码
*/
public
abstract
class
AbstractChannelMessage
extends
AbstractRedisMessage
{
/**
* 获得 Redis Channel
*
* @return Channel
*/
@JsonIgnore
// 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。
public
abstract
String
getChannel
();
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessageListener.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
.
pubsub
;
import
cn.hutool.core.util.TypeUtil
;
import
cn.iocoder.yudao.framework.common.util.json.JsonUtils
;
import
cn.iocoder.yudao.framework.mq.core.RedisMQTemplate
;
import
cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
import
lombok.Setter
;
import
lombok.SneakyThrows
;
import
org.springframework.data.redis.connection.Message
;
import
org.springframework.data.redis.connection.MessageListener
;
import
java.lang.reflect.Type
;
import
java.util.List
;
/**
* Redis Pub/Sub 监听器抽象类,用于实现广播消费
*
* @param <T> 消息类型。一定要填写噢,不然会报错
*
* @author 芋道源码
*/
public
abstract
class
AbstractChannelMessageListener
<
T
extends
AbstractChannelMessage
>
implements
MessageListener
{
/**
* 消息类型
*/
private
final
Class
<
T
>
messageType
;
/**
* Redis Channel
*/
private
final
String
channel
;
/**
* RedisMQTemplate
*/
@Setter
private
RedisMQTemplate
redisMQTemplate
;
@SneakyThrows
protected
AbstractChannelMessageListener
()
{
this
.
messageType
=
getMessageClass
();
this
.
channel
=
messageType
.
newInstance
().
getChannel
();
}
/**
* 获得 Sub 订阅的 Redis Channel 通道
*
* @return channel
*/
public
final
String
getChannel
()
{
return
channel
;
}
@Override
public
final
void
onMessage
(
Message
message
,
byte
[]
bytes
)
{
T
messageObj
=
JsonUtils
.
parseObject
(
message
.
getBody
(),
messageType
);
try
{
consumeMessageBefore
(
messageObj
);
// 消费消息
this
.
onMessage
(
messageObj
);
}
finally
{
consumeMessageAfter
(
messageObj
);
}
}
/**
* 处理消息
*
* @param message 消息
*/
public
abstract
void
onMessage
(
T
message
);
/**
* 通过解析类上的泛型,获得消息类型
*
* @return 消息类型
*/
@SuppressWarnings
(
"unchecked"
)
private
Class
<
T
>
getMessageClass
()
{
Type
type
=
TypeUtil
.
getTypeArgument
(
getClass
(),
0
);
if
(
type
==
null
)
{
throw
new
IllegalStateException
(
String
.
format
(
"类型(%s) 需要设置消息类型"
,
getClass
().
getName
()));
}
return
(
Class
<
T
>)
type
;
}
private
void
consumeMessageBefore
(
AbstractRedisMessage
message
)
{
assert
redisMQTemplate
!=
null
;
List
<
RedisMessageInterceptor
>
interceptors
=
redisMQTemplate
.
getInterceptors
();
// 正序
interceptors
.
forEach
(
interceptor
->
interceptor
.
consumeMessageBefore
(
message
));
}
private
void
consumeMessageAfter
(
AbstractRedisMessage
message
)
{
assert
redisMQTemplate
!=
null
;
List
<
RedisMessageInterceptor
>
interceptors
=
redisMQTemplate
.
getInterceptors
();
// 倒序
for
(
int
i
=
interceptors
.
size
()
-
1
;
i
>=
0
;
i
--)
{
interceptors
.
get
(
i
).
consumeMessageAfter
(
message
);
}
}
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessage.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
.
stream
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
import
com.fasterxml.jackson.annotation.JsonIgnore
;
/**
* Redis Stream Message 抽象类
*
* @author 芋道源码
*/
public
abstract
class
AbstractStreamMessage
extends
AbstractRedisMessage
{
/**
* 获得 Redis Stream Key
*
* @return Channel
*/
@JsonIgnore
// 避免序列化
public
abstract
String
getStreamKey
();
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/stream/AbstractStreamMessageListener.java
deleted
100644 → 0
浏览文件 @
4807547d
package
cn
.
iocoder
.
yudao
.
framework
.
mq
.
core
.
stream
;
import
cn.hutool.core.util.TypeUtil
;
import
cn.iocoder.yudao.framework.common.util.json.JsonUtils
;
import
cn.iocoder.yudao.framework.mq.core.RedisMQTemplate
;
import
cn.iocoder.yudao.framework.mq.core.interceptor.RedisMessageInterceptor
;
import
cn.iocoder.yudao.framework.mq.core.message.AbstractRedisMessage
;
import
lombok.Getter
;
import
lombok.Setter
;
import
lombok.SneakyThrows
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.data.redis.connection.stream.ObjectRecord
;
import
org.springframework.data.redis.stream.StreamListener
;
import
java.lang.reflect.Type
;
import
java.util.List
;
/**
* Redis Stream 监听器抽象类,用于实现集群消费
*
* @param <T> 消息类型。一定要填写噢,不然会报错
*
* @author 芋道源码
*/
public
abstract
class
AbstractStreamMessageListener
<
T
extends
AbstractStreamMessage
>
implements
StreamListener
<
String
,
ObjectRecord
<
String
,
String
>>
{
/**
* 消息类型
*/
private
final
Class
<
T
>
messageType
;
/**
* Redis Channel
*/
@Getter
private
final
String
streamKey
;
/**
* Redis 消费者分组,默认使用 spring.application.name 名字
*/
@Value
(
"${spring.application.name}"
)
@Getter
private
String
group
;
/**
* RedisMQTemplate
*/
@Setter
private
RedisMQTemplate
redisMQTemplate
;
@SneakyThrows
protected
AbstractStreamMessageListener
()
{
this
.
messageType
=
getMessageClass
();
this
.
streamKey
=
messageType
.
newInstance
().
getStreamKey
();
}
@Override
public
void
onMessage
(
ObjectRecord
<
String
,
String
>
message
)
{
// 消费消息
T
messageObj
=
JsonUtils
.
parseObject
(
message
.
getValue
(),
messageType
);
try
{
consumeMessageBefore
(
messageObj
);
// 消费消息
this
.
onMessage
(
messageObj
);
// ack 消息消费完成
redisMQTemplate
.
getRedisTemplate
().
opsForStream
().
acknowledge
(
group
,
message
);
// TODO 芋艿:需要额外考虑以下几个点:
// 1. 处理异常的情况
// 2. 发送日志;以及事务的结合
// 3. 消费日志;以及通用的幂等性
// 4. 消费失败的重试,https://zhuanlan.zhihu.com/p/60501638
}
finally
{
consumeMessageAfter
(
messageObj
);
}
}
/**
* 处理消息
*
* @param message 消息
*/
public
abstract
void
onMessage
(
T
message
);
/**
* 通过解析类上的泛型,获得消息类型
*
* @return 消息类型
*/
@SuppressWarnings
(
"unchecked"
)
private
Class
<
T
>
getMessageClass
()
{
Type
type
=
TypeUtil
.
getTypeArgument
(
getClass
(),
0
);
if
(
type
==
null
)
{
throw
new
IllegalStateException
(
String
.
format
(
"类型(%s) 需要设置消息类型"
,
getClass
().
getName
()));
}
return
(
Class
<
T
>)
type
;
}
private
void
consumeMessageBefore
(
AbstractRedisMessage
message
)
{
assert
redisMQTemplate
!=
null
;
List
<
RedisMessageInterceptor
>
interceptors
=
redisMQTemplate
.
getInterceptors
();
// 正序
interceptors
.
forEach
(
interceptor
->
interceptor
.
consumeMessageBefore
(
message
));
}
private
void
consumeMessageAfter
(
AbstractRedisMessage
message
)
{
assert
redisMQTemplate
!=
null
;
List
<
RedisMessageInterceptor
>
interceptors
=
redisMQTemplate
.
getInterceptors
();
// 倒序
for
(
int
i
=
interceptors
.
size
()
-
1
;
i
>=
0
;
i
--)
{
interceptors
.
get
(
i
).
consumeMessageAfter
(
message
);
}
}
}
yudao-framework/yudao-spring-boot-starter-rpc/pom.xml
浏览文件 @
4381d938
...
...
@@ -35,8 +35,13 @@
</dependency>
<dependency>
<groupId>
com.alibaba.cloud
</groupId>
<artifactId>
spring-cloud-starter-dubbo
</artifactId
>
<groupId>
org.apache.dubbo
</groupId>
<artifactId>
dubbo-common
</artifactId>
<!-- 兜底,保证在不引入 spring-cloud-starter-dubbo 时,注解等不报错 --
>
</dependency>
<!-- -->
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-dubbo</artifactId>-->
<!-- </dependency>-->
</dependencies>
</project>
yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml
浏览文件 @
4381d938
spring
:
main
:
allow-circular-references
:
true
# 允许循环依赖,因为项目是三层架构,无法避免这个情况。
allow-bean-definition-overriding
:
true
# 允许 Bean 覆盖,例如说 Dubbo 或者 Feign 等会存在重复定义的服务
# Servlet 配置
servlet
:
...
...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java
浏览文件 @
4381d938
package
cn
.
iocoder
.
yudao
.
module
.
system
.
mq
.
consumer
.
mail
;
import
cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener
;
import
cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.stereotype.Component
;
...
...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
浏览文件 @
4381d938
package
cn
.
iocoder
.
yudao
.
module
.
system
.
mq
.
message
.
mail
;
import
cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
javax.validation.constraints.NotNull
;
import
java.util.Map
;
...
...
@@ -39,5 +37,4 @@ public class MailSendMessage {
*/
private
Integer
userType
;
}
yudao-module-system/yudao-module-system-biz/src/main/resources/application.yaml
浏览文件 @
4381d938
spring
:
main
:
allow-circular-references
:
true
# 允许循环依赖,因为项目是三层架构,无法避免这个情况。
allow-bean-definition-overriding
:
true
# 允许 Bean 覆盖,例如说 Dubbo 或者 Feign 等会存在重复定义的服务
# Servlet 配置
servlet
:
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论