Commit ebb3a04251305d3e3cd5d220f267bd4b45a783c2

Authored by YunaiV
1 parent 6a371f06

优化多租户 Job 的实现,采用 AOP 替代 BeanPostProcessor,提升启动速度

yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
1 package cn.iocoder.yudao.framework.tenant.config; 1 package cn.iocoder.yudao.framework.tenant.config;
2 2
3 -import cn.hutool.core.annotation.AnnotationUtil;  
4 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; 3 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
5 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; 4 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
6 -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;  
7 import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; 5 import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
8 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; 6 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
9 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; 7 import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
10 -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;  
11 -import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator; 8 +import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
12 import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; 9 import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
13 import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; 10 import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager;
14 import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; 11 import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
@@ -20,8 +17,6 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; @@ -20,8 +17,6 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
20 import cn.iocoder.yudao.module.system.api.tenant.TenantApi; 17 import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
21 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 18 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
22 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; 19 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
23 -import org.springframework.beans.BeansException;  
24 -import org.springframework.beans.factory.config.BeanPostProcessor;  
25 import org.springframework.boot.autoconfigure.AutoConfiguration; 20 import org.springframework.boot.autoconfigure.AutoConfiguration;
26 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 21 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
27 import org.springframework.boot.context.properties.EnableConfigurationProperties; 22 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -100,25 +95,8 @@ public class YudaoTenantAutoConfiguration { @@ -100,25 +95,8 @@ public class YudaoTenantAutoConfiguration {
100 // ========== Job ========== 95 // ========== Job ==========
101 96
102 @Bean 97 @Bean
103 - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")  
104 - public BeanPostProcessor jobHandlerBeanPostProcessor(TenantFrameworkService tenantFrameworkService) {  
105 - return new BeanPostProcessor() {  
106 -  
107 - @Override  
108 - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
109 - if (!(bean instanceof JobHandler)) {  
110 - return bean;  
111 - }  
112 - // 有 TenantJob 注解的情况下,才会进行处理  
113 - if (!AnnotationUtil.hasAnnotation(bean.getClass(), TenantJob.class)) {  
114 - return bean;  
115 - }  
116 -  
117 - // 使用 TenantJobHandlerDecorator 装饰  
118 - return new TenantJobHandlerDecorator(tenantFrameworkService, (JobHandler) bean);  
119 - }  
120 -  
121 - }; 98 + public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
  99 + return new TenantJobAspect(tenantFrameworkService);
122 } 100 }
123 101
124 // ========== Redis ========== 102 // ========== Redis ==========
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJob.java
@@ -8,7 +8,7 @@ import java.lang.annotation.Target; @@ -8,7 +8,7 @@ import java.lang.annotation.Target;
8 /** 8 /**
9 * 多租户 Job 注解 9 * 多租户 Job 注解
10 */ 10 */
11 -@Target({ElementType.TYPE}) 11 +@Target({ElementType.METHOD})
12 @Retention(RetentionPolicy.RUNTIME) 12 @Retention(RetentionPolicy.RUNTIME)
13 public @interface TenantJob { 13 public @interface TenantJob {
14 } 14 }
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobHandlerDecorator.java renamed to yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/job/TenantJobAspect.java
1 package cn.iocoder.yudao.framework.tenant.core.job; 1 package cn.iocoder.yudao.framework.tenant.core.job;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.exceptions.ExceptionUtil;
4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
5 -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;  
6 -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;  
7 import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; 6 import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
8 -import lombok.AllArgsConstructor; 7 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  8 +import lombok.RequiredArgsConstructor;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.aspectj.lang.ProceedingJoinPoint;
  11 +import org.aspectj.lang.annotation.Around;
  12 +import org.aspectj.lang.annotation.Aspect;
9 13
10 import java.util.List; 14 import java.util.List;
11 import java.util.Map; 15 import java.util.Map;
12 import java.util.concurrent.ConcurrentHashMap; 16 import java.util.concurrent.ConcurrentHashMap;
13 17
14 /** 18 /**
15 - * 多租户 JobHandler 装饰器 19 + * 多租户 JobHandler AOP
16 * 任务执行时,会按照租户逐个执行 Job 的逻辑 20 * 任务执行时,会按照租户逐个执行 Job 的逻辑
17 * 21 *
18 * 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。 22 * 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。
19 * 23 *
20 * @author 芋道源码 24 * @author 芋道源码
21 */ 25 */
22 -@AllArgsConstructor  
23 -public class TenantJobHandlerDecorator implements JobHandler { 26 +@Aspect
  27 +@RequiredArgsConstructor
  28 +@Slf4j
  29 +public class TenantJobAspect {
24 30
25 private final TenantFrameworkService tenantFrameworkService; 31 private final TenantFrameworkService tenantFrameworkService;
26 - /**  
27 - * 被装饰的 Job  
28 - */  
29 - private final JobHandler jobHandler;  
30 32
31 - @Override  
32 - public final String execute(String param) throws Exception { 33 + @Around("@annotation(tenantJob)")
  34 + public String around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) {
33 // 获得租户列表 35 // 获得租户列表
34 List<Long> tenantIds = tenantFrameworkService.getTenantIds(); 36 List<Long> tenantIds = tenantFrameworkService.getTenantIds();
35 if (CollUtil.isEmpty(tenantIds)) { 37 if (CollUtil.isEmpty(tenantIds)) {
@@ -38,19 +40,15 @@ public class TenantJobHandlerDecorator implements JobHandler { @@ -38,19 +40,15 @@ public class TenantJobHandlerDecorator implements JobHandler {
38 40
39 // 逐个租户,执行 Job 41 // 逐个租户,执行 Job
40 Map<Long, String> results = new ConcurrentHashMap<>(); 42 Map<Long, String> results = new ConcurrentHashMap<>();
41 - tenantIds.parallelStream().forEach(tenantId -> { // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况  
42 - try {  
43 - // 设置租户  
44 - TenantContextHolder.setTenantId(tenantId);  
45 - // 执行 Job  
46 - String result = jobHandler.execute(param);  
47 - // 添加结果  
48 - results.put(tenantId, result);  
49 - } catch (Exception e) {  
50 - throw new RuntimeException(e);  
51 - } finally {  
52 - TenantContextHolder.clear();  
53 - } 43 + tenantIds.parallelStream().forEach(tenantId -> {
  44 + // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况
  45 + TenantUtils.execute(tenantId, () -> {
  46 + try {
  47 + joinPoint.proceed();
  48 + } catch (Throwable e) {
  49 + results.put(tenantId, ExceptionUtil.getRootCauseMessage(e));
  50 + }
  51 + });
54 }); 52 });
55 return JsonUtils.toJsonString(results); 53 return JsonUtils.toJsonString(results);
56 } 54 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java
@@ -11,14 +11,14 @@ import javax.annotation.Resource; @@ -11,14 +11,14 @@ import javax.annotation.Resource;
11 import java.util.List; 11 import java.util.List;
12 12
13 @Component 13 @Component
14 -@TenantJob // 标记多租户  
15 public class DemoJob implements JobHandler { 14 public class DemoJob implements JobHandler {
16 15
17 @Resource 16 @Resource
18 private AdminUserMapper adminUserMapper; 17 private AdminUserMapper adminUserMapper;
19 18
20 @Override 19 @Override
21 - public String execute(String param) throws Exception { 20 + @TenantJob // 标记多租户
  21 + public String execute(String param) {
22 System.out.println("当前租户:" + TenantContextHolder.getTenantId()); 22 System.out.println("当前租户:" + TenantContextHolder.getTenantId());
23 List<AdminUserDO> users = adminUserMapper.selectList(); 23 List<AdminUserDO> users = adminUserMapper.selectList();
24 return "用户数量:" + users.size(); 24 return "用户数量:" + users.size();