当前位置:网站首页>feign调用丢失请求头问题解决及原理分析
feign调用丢失请求头问题解决及原理分析
2022-07-27 04:45:00 【wen-pan】
以为feign调用请求头里添加cookie为例!!!!添加其他请求头数据过程一样!!!
一、问题抛出
- 我们有两个服务A、B,现在用户通过浏览器访问A服务的某个接口
- 在该接口中通过openFeign调用了B服务
- B服务中有个拦截器,对所有的请求都进行拦截,检查请求头中是否包含了cookie,如果有cookie这放行,如果没有cookie则进行拦截
问题:我们知道openFeign进行远程调用的时候他会新构建一个RequestTemplate进行请求,由于是新发起的请求,所以请求头里当然没有浏览器请求里带上的cookie数据了,如何让这个openFeign请求带上浏览器请求时的cookie呢?
二、问题分析
通过上面的介绍,我们知道openFeign进行远程调用的时候,由于是新起的一个请求调用,所以他丢失了原有请求(浏览器发起的)里的cookie信息以及请求头信息。
那么我们能不能将原有的请求里的cookie数据拿出来,然后在进行openFeign调用的时候在放入到新的请求中呢?当然可以,这就是我们下面介绍的feign拦截器原理。
三、问题解决
①、创建一个feign调用拦截器并注入容器
@Configuration
public class FeignConfig {
@Bean("requestInterceptor")
public static RequestInterceptor requestInterceptor() {
// 创建拦截器
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// 1、使用RequestContextHolder拿到原生请求的请求头信息(下文环境保持器)
// 从ThreadLocal中获取请求头(要保证feign调用与controller请求处在同一线程环境)
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
// 获取controller请求对象
HttpServletRequest request = requestAttributes.getRequest();
// 如果使用线程池进行远程调用,则request是空的(因为RequestContextHolder.getRequestAttributes()是从threadlocal里拿的值)
if (Objects.nonNull(request)) {
//2、获取老请求里的cookie信息
String cookie = request.getHeader("Cookie");
// 同步Cookie (将老请求里的cookie信息放入新请求里(RequestTemplate))
template.header("Cookie", cookie);
}
}
}
};
}
}
②、测试
再次启动远程调用,此时就会发现feign调用的请求头里有了cookie数据
四、源码分析
通过上面的添加一个
RequestInterceptor拦截器就解决了feign调用丢失请求头里的数据问题,那么他的做到的呢?原理是什么呢?其实去看一眼feign调用的源码就知道了,很简单首先要知道feign调用底层原理是基于动态代理!!!
1、FeignInvocationHandler.invoke()
通过debug我们可以看到,feign调用最终会执行FeignInvocationHandler.invoke()方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// 这里是具体执行逻辑,点进invoke
return dispatch.get(method).invoke(args);
}
2、SynchronousMethodHandler.invoke()
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 具体执行的入口,点进去
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
// 省略一部分代码。。。。这里其实就是做失败重试,和我们的主题没有关系
continue;
}
}
}
3、SynchronousMethodHandler.executeAndDecode()
- 可以看到他是先构建请求用的Request
- 然后再通过
client.execute(request, options);发起的真正调用
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 1、构建request,核心就在这里了,点进去
Request request = targetRequest(template);
Response response;
long start = System.nanoTime();
try {
// 2、真正发起调用的地方
response = client.execute(request, options);
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
throw errorExecuting(request, e);
}
// 省略部分无关代码
}
4、SynchronousMethodHandler.targetRequest()
Request targetRequest(RequestTemplate template) {
// 这里也就是我们自定义RequestInterceptor被调用的地方
// 可以看到,他拿到容器中所有实现了RequestInterceptor接口的bean,然后依次执行他们的apply方法
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(template);
}
5、总结
通过上面跟了一波源代码,我们知道了为openFeign增加请求头数据的工作原理,如下:
- 注入一个自定义的
RequestInterceptor接口的实现类到容器中 - openFeign发起调用前会先构建请求的Request,在构建request的时候就会拿到我们自定义的
RequestInterceptor并执行,在自定义的RequestInterceptor实现类中便会从原有请求里拿到请求头信息,然后放入feign调用的request里
注意如果是通过线程池或多线程进行发起feign调用,那么通过上述的添加RequestInterceptor实现类里通过RequestContextHolder.getRequestAttributes()获取原有请求里的请求头数据是获取不到的,这种情况怎么解决?其实也简单,最简单的方式就是在提交任务到线程池里的时候将请求头信息传递进去就可以了。也可以使用阿里的TransmittableThreadLocal.
边栏推荐
- Grid layout
- 【C语言】动态内存管理
- 【搜索】Flood Fill 和 最短路模型
- Review of various historical versions of Photoshop and system requirements
- Vscode opens a new chapter in the visualization of pull request update code branches
- 【报错】Cannot read property ‘parseComponent‘ of undefined
- MySQL下载安装 & 完美卸载
- ps太卡怎么办?几步帮您解决问题
- Two way republication experiment
- STL 上头系列——list 容器详解
猜你喜欢

How does PS import LUT presets? Photoshop import LUT color preset tutorial

Structural mode - facade mode

项目对接支付宝支付,内网穿透实现监听支付宝的支付成功异步回调通知

Dynamic routing configuration

Final Cut Pro中文教程 (2) 素材窗口的认识

"Photoshop2021 tutorial" align and distribute to make dot patterns

Final Cut Pro Chinese tutorial (2) understanding of material window

Safety fourth after class exercise

js小技巧

CDH cluster integration external Flink (improved version - keep pace with the times)
随机推荐
事件总结-常用总结
【无标题】按照一定的条件,对 i 进行循环累加。条件通常为循环数组的长度,当超过长度就停止循环。因为对象无法判断长度,所以通常搭配 Object.keys() 使用。\nforEach 一般认为是 普
背包问题dp
【搜索】—— 多源BFS + 最小步数模型
如何重置Photoshop首选项?ps重置首选项的方法
Summary of fire safety training materials
Structural mode - facade mode
数字中国建设峰会闭幕,现场海量图片一览!
C语言 通讯录管理系统(链表,手机号码分段存储,txt文件存取,完整源码)
深入 Qt5 信号槽新语法
Structural mode - decorator mode
Plato farm is expected to further expand its ecosystem through elephant swap
Hiding skills of Photoshop clipping tool
Text processing tool in shell, cut [option parameter] filename Description: the default separator is the built-in variable of tab, awk [option parameter] '/pattern1/{action1}filename and awk
TCP's three handshakes and four waves
【搜索】双向广搜 + A*
Comprehensive experiment of static routing
Idea 如何新建一个groovy的项目(图文详细解释)
There is no need to install CUDA and cudnn manually. You can install tensorflow GPU through a one-line program. Take tensorflow gpu2.0.0, cuda10.0, cudnn7.6.5 as examples
What if Photoshop prompts that the temporary storage disk is full? How to solve the problem that PS temporary storage disk is full?