Commit 1ce2c09f4790924fa985317cf7b1f24a78b69616
1 parent
a2315826
增加 Tenant MQ 的支持
Showing
12 changed files
with
95 additions
and
31 deletions
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.http
| 1 | 1 | ### 请求 /login 接口 => 成功 |
| 2 | 2 | POST {{baseUrl}}/login |
| 3 | 3 | Content-Type: application/json |
| 4 | -tenant-id: 0 | |
| 4 | +tenant-id: 1 | |
| 5 | 5 | |
| 6 | 6 | { |
| 7 | 7 | "username": "admin", |
| ... | ... | @@ -13,10 +13,13 @@ tenant-id: 0 |
| 13 | 13 | ### 请求 /get-permission-info 接口 => 成功 |
| 14 | 14 | GET {{baseUrl}}/get-permission-info |
| 15 | 15 | Authorization: Bearer {{token}} |
| 16 | +tenant-id: 1 | |
| 16 | 17 | |
| 17 | 18 | ### 请求 /list-menus 接口 => 成功 |
| 18 | 19 | GET {{baseUrl}}/list-menus |
| 19 | 20 | Authorization: Bearer {{token}} |
| 21 | +#Authorization: Bearer 0d161f69c9ac4c7f836e1b850715a7b0 | |
| 22 | +tenant-id: 1 | |
| 20 | 23 | |
| 21 | 24 | ### 请求 /druid/xxx 接口 => 失败 TODO 临时测试 |
| 22 | 25 | GET http://127.0.0.1:8080/druid/123 | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/tenant/SysTenantController.java
| ... | ... | @@ -21,7 +21,7 @@ public class SysTenantController { |
| 21 | 21 | @ApiImplicitParam(name = "name", value = "租户名", required = true, example = "芋道源码", dataTypeClass = Long.class) |
| 22 | 22 | public CommonResult<Long> getTenantIdByName(@RequestParam("name") String name) { |
| 23 | 23 | if (Objects.equals("芋道源码", name)) { |
| 24 | - return CommonResult.success(0L); | |
| 24 | + return CommonResult.success(1L); | |
| 25 | 25 | } |
| 26 | 26 | return CommonResult.success(null); |
| 27 | 27 | } | ... | ... |
yudao-admin-server/src/main/resources/application.yaml
| ... | ... | @@ -22,7 +22,7 @@ spring: |
| 22 | 22 | |
| 23 | 23 | # MyBatis Plus 的配置项 |
| 24 | 24 | mybatis-plus: |
| 25 | -# 在 mybatis-config/mybatis-config.xml 中设置 | |
| 25 | +# 在 mybatis-config/mybatis-config.xml 中设置 TODO jason:看看有没其它解决方案 | |
| 26 | 26 | # configuration: |
| 27 | 27 | # map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 |
| 28 | 28 | # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志 | ... | ... |
yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | <settings> |
| 7 | 7 | <setting name="lazyLoadingEnabled" value="false" /> |
| 8 | 8 | <setting name="mapUnderscoreToCamelCase" value="true"/> |
| 9 | + <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/> | |
| 9 | 10 | </settings> |
| 10 | 11 | <typeAliases> |
| 11 | 12 | <typeAlias type="org.activiti.engine.impl.persistence.ByteArrayRefTypeHandler" alias="ByteArrayRefTypeHandler"/> | ... | ... |
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/message/AbstractRedisMessage.java
| 1 | 1 | package cn.iocoder.yudao.framework.mq.core.message; |
| 2 | 2 | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 3 | 5 | import java.util.HashMap; |
| 4 | 6 | import java.util.Map; |
| 5 | 7 | |
| ... | ... | @@ -8,12 +10,13 @@ import java.util.Map; |
| 8 | 10 | * |
| 9 | 11 | * @author 芋道源码 |
| 10 | 12 | */ |
| 13 | +@Data | |
| 11 | 14 | public abstract class AbstractRedisMessage { |
| 12 | 15 | |
| 13 | 16 | /** |
| 14 | 17 | * 头 |
| 15 | 18 | */ |
| 16 | - private final Map<String, String> headers = new HashMap<>(); | |
| 19 | + private Map<String, String> headers = new HashMap<>(); | |
| 17 | 20 | |
| 18 | 21 | public String getHeader(String key) { |
| 19 | 22 | return headers.get(key); | ... | ... |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
| ... | ... | @@ -40,10 +40,11 @@ public class MyBatisUtils { |
| 40 | 40 | * |
| 41 | 41 | * @param interceptor 链 |
| 42 | 42 | * @param inner 拦截器 |
| 43 | + * @param index 位置 | |
| 43 | 44 | */ |
| 44 | - public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner) { | |
| 45 | + public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) { | |
| 45 | 46 | List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors()); |
| 46 | - inners.add(0, inner); | |
| 47 | + inners.add(index, inner); | |
| 47 | 48 | interceptor.setInterceptors(inners); |
| 48 | 49 | } |
| 49 | 50 | ... | ... |
yudao-framework/yudao-spring-boot-starter-tenant/pom.xml
| ... | ... | @@ -38,6 +38,12 @@ |
| 38 | 38 | <groupId>cn.iocoder.boot</groupId> |
| 39 | 39 | <artifactId>yudao-spring-boot-starter-job</artifactId> |
| 40 | 40 | </dependency> |
| 41 | + | |
| 42 | + <!-- 消息队列相关 --> | |
| 43 | + <dependency> | |
| 44 | + <groupId>cn.iocoder.boot</groupId> | |
| 45 | + <artifactId>yudao-spring-boot-starter-mq</artifactId> | |
| 46 | + </dependency> | |
| 41 | 47 | </dependencies> |
| 42 | 48 | |
| 43 | 49 | </project> | ... | ... |
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantDatabaseAutoConfiguration.java
| ... | ... | @@ -4,40 +4,27 @@ import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; |
| 4 | 4 | import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; |
| 5 | 5 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| 6 | 6 | import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; |
| 7 | -import org.springframework.beans.BeansException; | |
| 8 | -import org.springframework.beans.factory.config.BeanPostProcessor; | |
| 9 | 7 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 10 | 8 | import org.springframework.context.annotation.Bean; |
| 9 | +import org.springframework.context.annotation.Configuration; | |
| 11 | 10 | |
| 12 | 11 | /** |
| 13 | 12 | * 多租户针对 DB 的自动配置 |
| 14 | 13 | * |
| 15 | 14 | * @author 芋道源码 |
| 16 | 15 | */ |
| 16 | +@Configuration | |
| 17 | 17 | @EnableConfigurationProperties(TenantProperties.class) |
| 18 | 18 | public class YudaoTenantDatabaseAutoConfiguration { |
| 19 | 19 | |
| 20 | 20 | @Bean |
| 21 | - public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties) { | |
| 22 | - return new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)); | |
| 23 | - } | |
| 24 | - | |
| 25 | - @Bean | |
| 26 | - public BeanPostProcessor mybatisPlusInterceptorBeanPostProcessor(TenantLineInnerInterceptor tenantLineInnerInterceptor) { | |
| 27 | - return new BeanPostProcessor() { | |
| 28 | - | |
| 29 | - @Override | |
| 30 | - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { | |
| 31 | - if (!(bean instanceof MybatisPlusInterceptor)) { | |
| 32 | - return bean; | |
| 33 | - } | |
| 34 | - // 将 TenantDatabaseInterceptor 添加到最前面 | |
| 35 | - MybatisPlusInterceptor interceptor = (MybatisPlusInterceptor) bean; | |
| 36 | - MyBatisUtils.addInterceptor(interceptor, tenantLineInnerInterceptor); | |
| 37 | - return bean; | |
| 38 | - } | |
| 39 | - | |
| 40 | - }; | |
| 21 | + public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties properties, | |
| 22 | + MybatisPlusInterceptor interceptor) { | |
| 23 | + TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new TenantDatabaseInterceptor(properties)); | |
| 24 | + // 添加到 interceptor 中 | |
| 25 | + // 需要加在首个,主要是为了在分页插件前面。这个是 MyBatis Plus 的规定 | |
| 26 | + MyBatisUtils.addInterceptor(interceptor, inner, 0); | |
| 27 | + return inner; | |
| 41 | 28 | } |
| 42 | 29 | |
| 43 | 30 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantMQAutoConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; | |
| 4 | +import org.springframework.context.annotation.Bean; | |
| 5 | +import org.springframework.context.annotation.Configuration; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 多租户针对 MQ 的自动配置 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +@Configuration | |
| 13 | +public class YudaoTenantMQAutoConfiguration { | |
| 14 | + | |
| 15 | + @Bean | |
| 16 | + public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() { | |
| 17 | + return new TenantRedisMessageInterceptor(); | |
| 18 | + } | |
| 19 | + | |
| 20 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/mq/TenantRedisMessageInterceptor.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.mq; | |
| 2 | + | |
| 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; | |
| 6 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 多租户 {@link AbstractRedisMessage} 拦截器 | |
| 10 | + * | |
| 11 | + * 1. Producer 发送消息时,将 {@link TenantContextHolder} 租户编号,添加到消息的 Header 中 | |
| 12 | + * 2. Consumer 消费消息时,将消息的 Header 的租户编号,添加到 {@link TenantContextHolder} 中 | |
| 13 | + * | |
| 14 | + * @author 芋道源码 | |
| 15 | + */ | |
| 16 | +public class TenantRedisMessageInterceptor implements RedisMessageInterceptor { | |
| 17 | + | |
| 18 | + private static final String HEADER_TENANT_ID = "tenant-id"; | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public void sendMessageBefore(AbstractRedisMessage message) { | |
| 22 | + Long tenantId = TenantContextHolder.getTenantId(); | |
| 23 | + if (tenantId != null) { | |
| 24 | + message.addHeader(HEADER_TENANT_ID, tenantId.toString()); | |
| 25 | + } | |
| 26 | + } | |
| 27 | + | |
| 28 | + @Override | |
| 29 | + public void consumeMessageBefore(AbstractRedisMessage message) { | |
| 30 | + String tenantIdStr = message.getHeader(HEADER_TENANT_ID); | |
| 31 | + if (StrUtil.isNotEmpty(tenantIdStr)) { | |
| 32 | + TenantContextHolder.setTenantId(Long.valueOf(tenantIdStr)); | |
| 33 | + } | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Override | |
| 37 | + public void consumeMessageAfter(AbstractRedisMessage message) { | |
| 38 | + // 注意,Consumer 是一个逻辑的入口,所以不考虑原本上下文就存在租户编号的情况 | |
| 39 | + TenantContextHolder.clear(); | |
| 40 | + } | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/package-info.java
| 1 | 1 | /** |
| 2 | 2 | * 多租户,支持如下层面: |
| 3 | 3 | * 1. DB:基于 MyBatis Plus 多租户的功能实现。 |
| 4 | - * 2. Web:请求 HTTP API 时,Header 带上 tenant-id 租户编号。 | |
| 4 | + * 2. Web:请求 HTTP API 时,解析 Header 的 tenant-id 租户编号,添加到租户上下文。 | |
| 5 | 5 | * 3. Job:在 JobHandler 执行任务时,会按照每个租户,都独立并行执行一次。 |
| 6 | - * 4. MQ:TODO | |
| 6 | + * 4. MQ:在 Producer 发送消息时,Header 带上 tenant-id 租户编号;在 Consumer 消费消息时,将 Header 的 tenant-id 租户编号,添加到租户上下文。 | |
| 7 | 7 | * 5. Async:异步需要保证 ThreadLocal 的传递性,通过使用阿里开源的 TransmittableThreadLocal 实现。相关的改造点,可见: |
| 8 | 8 | * 1)Spring Async: |
| 9 | 9 | * {@link cn.iocoder.yudao.framework.quartz.config.YudaoAsyncAutoConfiguration#threadPoolTaskExecutorBeanPostProcessor()} | ... | ... |
yudao-framework/yudao-spring-boot-starter-tenant/src/main/resources/META-INF/spring.factories
| 1 | 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
| 2 | 2 | cn.iocoder.yudao.framework.tenant.config.YudaoTenantDatabaseAutoConfiguration,\ |
| 3 | 3 | cn.iocoder.yudao.framework.tenant.config.YudaoTenantWebAutoConfiguration,\ |
| 4 | - cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration | |
| 4 | + cn.iocoder.yudao.framework.tenant.config.YudaoTenantJobAutoConfiguration,\ | |
| 5 | + cn.iocoder.yudao.framework.tenant.config.YudaoTenantMQAutoConfiguration | ... | ... |