需求背景:
在微服务横行天下的今天,Spring Cloud OpenFeign 早就成了我们服务间调度的主要远程调用手段。
在Feign进行远程调度的时候,难免会做一些心跳,权限的校验,来保证调度过程的相对安全。
但对于外部系统来说,有时候这些“额外信息”反而成了负担(笔者就遇到了调用外部系统时由于外部系统设置了对 http head 大小的设置,导致调用400错误)。
常见的对http head 大小限制如:
1.服务对请求头大小的限制
2.网关对请求头大小的限制
当遇到这种情况时,我们就需要对不同的Feign调用添加不同的RequestInterceptor;
参考Spring Cloud OpenFeign 的文档手册
可以根据不同的feignName去配置对应的拦截器;
但是项目系统有很多FeignClient调用时,就显得配置比较繁琐。
那我们怎么对项目系统内部的FeignClient进行统一拦截器拦截,外部调用做其他处理呢;
需要我们去分析一下FeignClient的工厂类FeignClientFactoryBean;
在用户没有其他自定义的配置文件时,都会调用根据contextId去获取配置文件;
那么我们可以通过利用这个特性,通过BeanFactoryPostProcessor去给
feignClientProperties添加对应contextId的过滤器;
@Slf4j
@Component
public class FeignConfigBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
//获取FeignClientProperties
FeignClientProperties feignClientProperties = configurableListableBeanFactory.getBean(FeignClientProperties.class);
//获取FeignClientBeanFacoty数组
String [] feignClientBeanFactoryArray = configurableListableBeanFactory.getBeanNamesForAnnotation(FeignClient.class);
//自定义FeignClientConfiguration
FeignClientProperties.FeignClientConfiguration feignClientConfiguration = new FeignClientProperties.FeignClientConfiguration();
//添加自定义CustomerFeignInterceptor
feignClientConfiguration.setRequestInterceptors(Arrays.asList(new Class[]{CustomerFeignInterceptor.class}));
for(String feignClientBeanFactory : feignClientBeanFactoryArray){
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(feignClientBeanFactory);
//公司内部系统的内部统一命名为"abc-***",如果不统一,也可以通过配置文件或者其他方式去记录需要统一处理的FeignClient;
if(Objects.nonNull(beanDefinition.getPropertyValues().get("name")) && beanDefinition.getPropertyValues().get("name").toString().startsWith("abc-")){
//根据contextId添加FeignClientConfiguration
feignClientProperties.getConfig().put(Objects.requireNonNull(beanDefinition.getPropertyValues().get("contextId")).toString(),feignClientConfiguration);
}
}
}
}
这样,就做到了给一些特定的FeignClient去添加统一的RequestInterceptor;
注:Feign.Builder在添加requestInterceptor的时候存储的是个List数组
@FeignClient(value = "abc-web",contextId = "abc-web",configuration = {CustomerFeignInterceptor.class})
当我们添加FeignConfigBeanFactoryPostProcessor 的同时在@FeignClient的configuration 写入同一个CustomerFeignInterceptor时,CustomerFeignInterceptor会调用多次