mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-02-21 19:50:32 +08:00
优化多租户 Job 的实现,采用 AOP 替代 BeanPostProcessor,提升启动速度
This commit is contained in:
parent
6a371f06b0
commit
ebb3a04251
@ -1,14 +1,11 @@
|
|||||||
package cn.iocoder.yudao.framework.tenant.config;
|
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.common.enums.WebFilterOrderEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
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.redis.config.YudaoCacheProperties;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
|
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.db.TenantDatabaseInterceptor;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
|
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
|
|
||||||
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
|
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.redis.TenantRedisCacheManager;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
|
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 cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||||
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.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
@ -100,25 +95,8 @@ public class YudaoTenantAutoConfiguration {
|
|||||||
// ========== Job ==========
|
// ========== Job ==========
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
|
||||||
public BeanPostProcessor jobHandlerBeanPostProcessor(TenantFrameworkService tenantFrameworkService) {
|
return new TenantJobAspect(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== Redis ==========
|
// ========== Redis ==========
|
||||||
|
@ -8,7 +8,7 @@ import java.lang.annotation.Target;
|
|||||||
/**
|
/**
|
||||||
* 多租户 Job 注解
|
* 多租户 Job 注解
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.TYPE})
|
@Target({ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface TenantJob {
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -11,14 +11,14 @@ import javax.annotation.Resource;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@TenantJob // 标记多租户
|
|
||||||
public class DemoJob implements JobHandler {
|
public class DemoJob implements JobHandler {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AdminUserMapper adminUserMapper;
|
private AdminUserMapper adminUserMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String param) throws Exception {
|
@TenantJob // 标记多租户
|
||||||
|
public String execute(String param) {
|
||||||
System.out.println("当前租户:" + TenantContextHolder.getTenantId());
|
System.out.println("当前租户:" + TenantContextHolder.getTenantId());
|
||||||
List<AdminUserDO> users = adminUserMapper.selectList();
|
List<AdminUserDO> users = adminUserMapper.selectList();
|
||||||
return "用户数量:" + users.size();
|
return "用户数量:" + users.size();
|
||||||
|
Loading…
Reference in New Issue
Block a user