关于Feign远程调用传递租户Id 及 jdbc分库的方法

Blade 未结 2 228
tongyi
tongyi 剑侠 2025-07-26 12:06

1. 创建一个配置文件

@Configuration
public class TenantFeignConfig {
    @Bean
    public RequestInterceptor tenantIdInterceptor() {
        return template -> {
            // 从当前线程上下文获取租户ID(需自行实现租户上下文管理)
            String tenantId = AuthUtil.getTenantId();
            if (Func.isNotBlank(tenantId)) {
                template.header("Blade-TenantId", tenantId);
            }
        };
    }
}

2. Feign Client 接口引入配置文件

@FeignClient(
       value = BaseAppConstant.APPLICATION_BASE_NAME,
       fallback = IBaseChannelClientFallback.class,
       configuration = TenantFeignConfig.class
)
public interface IBaseChannelClient {

3. Client 实现类

@RestController
@RequiredArgsConstructor
public class BaseChannelClient implements IBaseChannelClient {

    private final IBaseChannelService baseChannelService;
    private final BladeContext bladeContext;

    @Override
    public R<BaseChannel> getChannel(Long id) {
        String tenantId = bladeContext.getTenantId(); // 获取租户Id
        try {
            DynamicDataSourceContextHolder.push(tenantId); // 切换数据源(如不需要可以去掉 try{...}finally{...} )
            return R.data(
                    TenantUtil.use(tenantId, () -> baseChannelService.getById(id)) // 线程注入租户Id
            );
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }
}

希望官方可以封装上述功能到tool中

2条回答
  • 2025-07-28 14:21

    这个逻辑无法写到tool里全局生效,需要手动处理

    0 讨论(0)
  • 2025-07-28 15:42

    我后来优化了一下,代码更新到自己公司代码库了。可以参考一下,实现租户远程调用确实有很大的便利。

    1. @TenantFeign

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TenantFeign {
        /**
         * 是否开启租户动态数据源
         *
         * @return
         */
        boolean tenantDS() default false;
    
    }

    2. FeignTenantAspect

    @Slf4j
    @Aspect
    @Component
    @RequiredArgsConstructor
    public class FeignTenantAspect {
    
        private final BladeContext bladeContext;
    
        @Pointcut("@within(org.springblade.core.tenant.annotation.TenantFeign) || " +
                "@annotation(org.springblade.core.tenant.annotation.TenantFeign)")
        public void feignTenantPointcut() {
        }
    
        @Around("feignTenantPointcut()")
        public Object handleFeignTenant(ProceedingJoinPoint joinPoint) throws Throwable {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            String tenantId = bladeContext.getTenantId();
    
            TenantFeign tenantFeign = AnnotationUtils.findAnnotation(method, TenantFeign.class);
            if (tenantFeign == null) {
                tenantFeign = AnnotationUtils.findAnnotation(joinPoint.getTarget().getClass(), TenantFeign.class);
            }
            if (tenantFeign == null || Func.isBlank(tenantId)) {
                return joinPoint.proceed();
            }
    
            boolean tenantDS = tenantFeign.tenantDS();
    
            try {
                if (tenantDS) {// 切换租户数据源
                    DynamicDataSourceContextHolder.push(tenantId);
                }
    
                return TenantUtil.use(tenantId, () -> {
                    try {
                        return joinPoint.proceed();
                    } catch (Throwable e) {
                        throw new RuntimeException(e);
                    }
                });
            } finally {
                if (tenantDS) {
                    DynamicDataSourceContextHolder.poll();
                }
            }
        }
    
    }

    3. 实现方式

    @TenantFeign(tenantDS = true)  // tenantDS=true 开启租户多数据源,默认false, 只传递租户Id
    @Hidden
    @RestController
    @RequiredArgsConstructor
    public class BaseBrandClient implements IBaseBrandClient {

       private final IBaseBrandService baseBrandService;

       @Override
       public R<BaseBrand> getBaseBrand(Long id) {
           // 实现方发不需要改变
           return R.data(this.baseBrandService.getById(id));
       }

       @Override
       public R<Collection<BaseBrand>> listBaseBrand() {
           return R.data(this.baseBrandService.list(Wrappers.<BaseBrand>query().lambda()));
       }
    }

     image.png

    作者追问:2025-07-28 20:12

    后续我们评估优化下这个功能

    0 讨论(0)
提交回复