增加 Tenant MQ 的支持
parent
a231582637
commit
1ce2c09f47
|
@ -1,7 +1,7 @@
|
|||
### 请求 /login 接口 => 成功
|
||||
POST {{baseUrl}}/login
|
||||
Content-Type: application/json
|
||||
tenant-id: 0
|
||||
tenant-id: 1
|
||||
|
||||
{
|
||||
"username": "admin",
|
||||
|
@ -13,10 +13,13 @@ tenant-id: 0
|
|||
### 请求 /get-permission-info 接口 => 成功
|
||||
GET {{baseUrl}}/get-permission-info
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: 1
|
||||
|
||||
### 请求 /list-menus 接口 => 成功
|
||||
GET {{baseUrl}}/list-menus
|
||||
Authorization: Bearer {{token}}
|
||||
#Authorization: Bearer 0d161f69c9ac4c7f836e1b850715a7b0
|
||||
tenant-id: 1
|
||||
|
||||
### 请求 /druid/xxx 接口 => 失败 TODO 临时测试
|
||||
GET http://127.0.0.1:8080/druid/123
|
||||
|
|
|
@ -21,7 +21,7 @@ public class SysTenantController {
|
|||
@ApiImplicitParam(name = "name", value = "租户名", required = true, example = "芋道源码", dataTypeClass = Long.class)
|
||||
public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) {
|
||||
if (Objects.equals("芋道源码", name)) {
|
||||
return CommonResult.success(0L);
|
||||
return CommonResult.success(1L);
|
||||
}
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ spring:
|
|||
|
||||
# MyBatis Plus 的配置项
|
||||
mybatis-plus:
|
||||
# 在 mybatis-config/mybatis-config.xml 中设置
|
||||
# 在 mybatis-config/mybatis-config.xml 中设置 TODO jason:看看有没其它解决方案
|
||||
# configuration:
|
||||
# map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
|
||||
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<settings>
|
||||
<setting name="lazyLoadingEnabled" value="false" />
|
||||
<setting name="mapUnderscoreToCamelCase" value="true"/>
|
||||
<setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
|
||||
</settings>
|
||||
<typeAliases>
|
||||
<typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.mq.core.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -8,12 +10,13 @@ import java.util.Map;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public abstract class AbstractRedisMessage {
|
||||
|
||||
/**
|
||||
* 头
|
||||
*/
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
private Map<String, String> headers = new HashMap<>();
|
||||
|
||||
public String getHeader(String key) {
|
||||
return headers.get(key);
|
||||
|
|
|
@ -40,10 +40,11 @@ public class MyBatisUtils {
|
|||
*
|
||||
* @param interceptor 链
|
||||
* @param inner 拦截器
|
||||
* @param index 位置
|
||||
*/
|
||||
public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner) {
|
||||
public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) {
|
||||
List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
|
||||
inners.add(0, inner);
|
||||
inners.add(index, inner);
|
||||
interceptor.setInterceptors(inners);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,12 @@
|
|||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -4,40 +4,27 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
|||
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 多租户针对 DB 的自动配置
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TenantProperties.class)
|
||||
public class YudaoTenantDatabaseAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties) {
|
||||
return new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor mybatisPlusInterceptorBeanPostProcessor(TenantLineInnerInterceptor tenantLineInnerInterceptor) {
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (!(bean instanceof MybatisPlusInterceptor)) {
|
||||
return bean;
|
||||
}
|
||||
// 将 TenantDatabaseInterceptor 添加到最前面
|
||||
MybatisPlusInterceptor interceptor = (MybatisPlusInterceptor) bean;
|
||||
MyBatisUtils.addInterceptor(interceptor, tenantLineInnerInterceptor);
|
||||
return bean;
|
||||
}
|
||||
|
||||
};
|
||||
public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties,
|
||||
MybatisPlusInterceptor interceptor) {
|
||||
TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties));
|
||||
// 添加到 interceptor 中
|
||||
// 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
|
||||
MyBatisUtils.addInterceptor(interceptor, inner, 0);
|
||||
return inner;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package cn.iocoder.yudao.framework.tenant.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 多租户针对 MQ 的自动配置
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
public class YudaoTenantMQAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() {
|
||||
return new TenantRedisMessageInterceptor();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* 多租户,支持如下层面:
|
||||
* 1. DB:基于 MyBatis Plus 多租户的功能实现。
|
||||
* 2. Web:请求 HTTP API 时,Header 带上 tenant-id 租户编号。
|
||||
* 2. Web:请求 HTTP API 时,解析 Header 的 tenant-id 租户编号,添加到租户上下文。
|
||||
* 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。
|
||||
* 4. MQ:TODO
|
||||
* 4. MQ:在 Producer 发送消息时,Header 带上 tenant-id 租户编号;在 Consumer 消费消息时,将 Header 的 tenant-id 租户编号,添加到租户上下文。
|
||||
* 5. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见:
|
||||
* 1)Spring Async:
|
||||
* {@link cn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration,\
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration,\
|
||||
cn.iocoder.yudao.framework.tenant.config.YudaoTenantMQAutoConfiguration
|
||||
|
|
Loading…
Reference in New Issue