mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-10-31 18:28:43 +08:00 
			
		
		
		
	优化多租户 Job 的实现,采用 AOP 替代 BeanPostProcessor,提升启动速度
This commit is contained in:
		| @@ -1,14 +1,11 @@ | ||||
| package cn.iocoder.yudao.framework.tenant.config; | ||||
|  | ||||
| import cn.hutool.core.annotation.AnnotationUtil; | ||||
| import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; | ||||
| import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; | ||||
| import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | ||||
| import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; | ||||
| 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.job.TenantJobAspect; | ||||
| import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor; | ||||
| import cn.iocoder.yudao.framework.tenant.core.redis.TenantRedisCacheManager; | ||||
| import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; | ||||
| @@ -20,8 +17,6 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | ||||
| import cn.iocoder.yudao.module.system.api.tenant.TenantApi; | ||||
| 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.autoconfigure.AutoConfiguration; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
| @@ -100,25 +95,8 @@ public class YudaoTenantAutoConfiguration { | ||||
|     // ========== Job ========== | ||||
|  | ||||
|     @Bean | ||||
|     @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") | ||||
|     public BeanPostProcessor jobHandlerBeanPostProcessor(TenantFrameworkService tenantFrameworkService) { | ||||
|         return new BeanPostProcessor() { | ||||
|  | ||||
|             @Override | ||||
|             public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { | ||||
|                 if (!(bean instanceof JobHandler)) { | ||||
|                     return bean; | ||||
|                 } | ||||
|                 // 有 TenantJob 注解的情况下,才会进行处理 | ||||
|                 if (!AnnotationUtil.hasAnnotation(bean.getClass(), TenantJob.class)) { | ||||
|                     return bean; | ||||
|                 } | ||||
|  | ||||
|                 // 使用 TenantJobHandlerDecorator 装饰 | ||||
|                 return new TenantJobHandlerDecorator(tenantFrameworkService, (JobHandler) bean); | ||||
|             } | ||||
|  | ||||
|         }; | ||||
|     public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) { | ||||
|         return new TenantJobAspect(tenantFrameworkService); | ||||
|     } | ||||
|  | ||||
|     // ========== Redis ========== | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import java.lang.annotation.Target; | ||||
| /** | ||||
|  * 多租户 Job 注解 | ||||
|  */ | ||||
| @Target({ElementType.TYPE}) | ||||
| @Target({ElementType.METHOD}) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| public @interface TenantJob { | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,56 @@ | ||||
| package cn.iocoder.yudao.framework.tenant.core.job; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.hutool.core.exceptions.ExceptionUtil; | ||||
| import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||||
| import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | ||||
| import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.aspectj.lang.ProceedingJoinPoint; | ||||
| import org.aspectj.lang.annotation.Around; | ||||
| import org.aspectj.lang.annotation.Aspect; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
|  | ||||
| /** | ||||
|  * 多租户 JobHandler AOP | ||||
|  * 任务执行时,会按照租户逐个执行 Job 的逻辑 | ||||
|  * | ||||
|  * 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @Aspect | ||||
| @RequiredArgsConstructor | ||||
| @Slf4j | ||||
| public class TenantJobAspect { | ||||
|  | ||||
|     private final TenantFrameworkService tenantFrameworkService; | ||||
|  | ||||
|     @Around("@annotation(tenantJob)") | ||||
|     public String around(ProceedingJoinPoint joinPoint, TenantJob tenantJob) { | ||||
|         // 获得租户列表 | ||||
|         List<Long> tenantIds = tenantFrameworkService.getTenantIds(); | ||||
|         if (CollUtil.isEmpty(tenantIds)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // 逐个租户,执行 Job | ||||
|         Map<Long, String> results = new ConcurrentHashMap<>(); | ||||
|         tenantIds.parallelStream().forEach(tenantId -> { | ||||
|             // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况 | ||||
|             TenantUtils.execute(tenantId, () -> { | ||||
|                 try { | ||||
|                     joinPoint.proceed(); | ||||
|                 } catch (Throwable e) { | ||||
|                     results.put(tenantId, ExceptionUtil.getRootCauseMessage(e)); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
|         return JsonUtils.toJsonString(results); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| package cn.iocoder.yudao.framework.tenant.core.job; | ||||
|  | ||||
| import cn.hutool.core.collection.CollUtil; | ||||
| import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||||
| import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | ||||
| import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||||
| import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | ||||
| import lombok.AllArgsConstructor; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
|  | ||||
| /** | ||||
|  * 多租户 JobHandler 装饰器 | ||||
|  * 任务执行时,会按照租户逐个执行 Job 的逻辑 | ||||
|  * | ||||
|  * 注意,需要保证 JobHandler 的幂等性。因为 Job 因为某个租户执行失败重试时,之前执行成功的租户也会再次执行。 | ||||
|  * | ||||
|  * @author 芋道源码 | ||||
|  */ | ||||
| @AllArgsConstructor | ||||
| public class TenantJobHandlerDecorator implements JobHandler { | ||||
|  | ||||
|     private final TenantFrameworkService tenantFrameworkService; | ||||
|     /** | ||||
|      * 被装饰的 Job | ||||
|      */ | ||||
|     private final JobHandler jobHandler; | ||||
|  | ||||
|     @Override | ||||
|     public final String execute(String param) throws Exception { | ||||
|         // 获得租户列表 | ||||
|         List<Long> tenantIds = tenantFrameworkService.getTenantIds(); | ||||
|         if (CollUtil.isEmpty(tenantIds)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // 逐个租户,执行 Job | ||||
|         Map<Long, String> results = new ConcurrentHashMap<>(); | ||||
|         tenantIds.parallelStream().forEach(tenantId -> { // TODO 芋艿:先通过 parallel 实现并行;1)多个租户,是一条执行日志;2)异常的情况 | ||||
|             try { | ||||
|                 // 设置租户 | ||||
|                 TenantContextHolder.setTenantId(tenantId); | ||||
|                 // 执行 Job | ||||
|                 String result = jobHandler.execute(param); | ||||
|                 // 添加结果 | ||||
|                 results.put(tenantId, result); | ||||
|             } catch (Exception e) { | ||||
|                 throw new RuntimeException(e); | ||||
|             } finally { | ||||
|                 TenantContextHolder.clear(); | ||||
|             } | ||||
|         }); | ||||
|         return JsonUtils.toJsonString(results); | ||||
|     } | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV