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