继承RequestInterceptor,实现feign调用前参数配置,为何会被所有项目下请求调用?
本篇会分以下几个模块进行梳理,如有需要可以直接进行跳转
- Feign调用原理
- 重要组件的主要作用
- Feign的简单使用
- Feign调用前的参数设置
(1)header存值大小限制
(2)多线程情况下的上下文丢失 - 配置为何会被所有项目下请求调用
一、Feign调用原理
SpringCloud 中 Feign 是目前跨服务调用应用中使用最多的技术。
在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果。
执行图例:
二、重要组件的主要作用
组件名称 | 作用领域 |
---|---|
JDK Proxy | 不属于Feign组件 内置代理 |
FeignInvocationHandler | 实现了InvocationHandler,Feign专属处理器,反射用来寻找MethodHandler |
SynchronousMethodHandler | 实现了MethodHandler,进行请求重封装,并接收返回值 |
1、创建requestTemplate 2、client.excecute()正式调用 3、response接收 | |
Client | 最终进行请求封装的组件 |
LoadBalancerFeignClient负载均衡客户端选择调用Default进行请求 |
执行顺序图示:
Cilent的四种实现方式,注意上表中Client调用方式
名称 | 来源 |
---|---|
Default | feign.Client |
ApacheHttpClient | Apache httpclient |
OkHttpClient | OkHttp3 |
LoadBalancerFeignClient | Ribben |
三、Feign的简单使用
一、创建一个接口服务,用来专属调用统一url的另一个服务
二、创建方法,设置调用参数与url
三、补充请求方式、请求参数、请求地址
@FeignClient(name = "feignTest", url = "${otherService.url}", configuration = FeignConfiguration.class)
public interface FeignTestService {
@PostMapping("/test/add")
ResultVO<JSONObject> save(@RequestBody String addVo);
}
四、Feign调用前的参数设置
@FeignClient中有一个参数是configuration,可以支持开发者对请求的自定义。
feign在发送请求之前都会调用RequestInterceptor接口的apply方法,通过实现RequestInterceptor接口的apply方法达到要求
代码示例:
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = null;
if ("username".equalsIgnoreCase(name)) {
values = request.getHeader(name);
}
if (StringUtils.isNotBlank(values)) {
requestTemplate.header(name, values);
}
}
}
}
}
}
RequestInterceptor 有几个实现类,可以直接继承使用,作用分别如下:
(1)header存值大小限制
如果发现存在:Request header is too large的错误出现,很有可能是header中存放的数据过长导致的
默认情况下,tomcat(8.0版本)允许的http请求header的最大值是8024个字节(8KB)
这种情况下网上也有很多中解决方案推荐,
比如说:设置请求头大小
或者:使用MultiValueMap
处于安全考虑,这里还是建议将header传送的参数修改为paramer形式传递。
(2)多线程情况下的上下文丢失
如果请求被放置在异步中,那么会丢失请求上下文,包括最外层请求中的header
这个问题暂时我还没有更好的解决方案落地
不过网上有几个推荐,有空可以试试:
使用 HystrixRequestVariableDefault,通过该类的注释可以知道,这个是Hystrix 为自身执行的线程提供的一个类似于ThreadLocal 的类,但是它与ThreadLocal 的不同之处在于,该Locals 的作用范围提升到了这个User Request Scope 级别,通过其注解可知,它是通过父类线程与子类线程共享的方式来共用该Locals 中的信息;所以,达到了User Request 线程与Hystrix 线程共用 attributes 的目的;由此,可以猜测,Hystrix 的线程是由当前Request 线程所创建的子线程;不过,使用的过程中,需要注意的是,HystrixRequestVariable 必须在每一个请求开始的时候进行初始化;也就是说,我们可以将 Request Context 中的有用信息存储到HystrixRequestVariableDefault中达到与Hystrix Context 共享信息;也就实现了Request Context 中的属性与Hystrix Context 之间共享的目的。
五、配置为何会被所有项目下请求调用
笔者在这里踩过一个坑,其实也是因为这个坑,才准备写一篇这样子的博客记录整个过程。
事情是这样子的。
需求:新需求里面需要携带用户信息去中台校验权限,每个接口的交互都需要
考虑:如果给每一个接口都放置参数,出问题不好定位,同时需要开发的代码量很大,可以编写配置类,统一处理
做法:
1、@FeignClient中进行configuration配置
2、配置文件使用@Configuration标记
3、继承RequestInterceptor,重写apply放置
简单看起来没有啥问题,可以测试之后发现只要使用到了feign的调用,不管是不是此类中的请求,都会走配置。
原因就是
配置文件加@Configuration会将配置注入到Spring中,导致所有FeignClient都生效
此处涉及到局部注解与全部注解,区别就在于@Configuration
全局配置:
@Configuration
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
}
}
局部配置:
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
}
}
THE END
GOOD LUNCK
感谢前辈笔记
参考博客:
https://www.cnblogs.com/crazymakercircle/p/11965726.html
https://blog.csdn.net/wudiyong22/article/details/103801874