openFeign 异步 调用丢失上下文怎么破?

简介: openFeign 异步 调用丢失上下文怎么破?

最近有读者问了这样一个问题:陈哥,openFeign异步调用总是失败触发Sentinel的降级,同步调用就没问题?

他还给我晒了一下代码,大致如下:

CompletableFuture<T> future1 = CompletableFuture.supplyAsync(() -> {
 //openfeign的调用
 return feign.remoteCall();
},executor);
CompletableFuture<T> future2 = CompletableFuture.supplyAsync(() -> {
 //openfeign的调用
 return feign.remoteCall();
},executor);
CompletableFuture.allOf(future1,future2).join();
.....

这种情况你遇到过吗?

如何解决?

这个算是常见问题了:feign的调用导致上下文丢失;前面有一篇文章说过这种问题:实战!openFeign如何实现全链路JWT令牌信息不丢失?

在集成OAuth2的时候如果做任何设置会丢失令牌信息,当时我们的解决方案是新建一个拦截器,如下:

@Component
@Slf4j
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        HttpServletRequest httpServletRequest = RequestContextUtils.getRequest();
        Map<String, String> headers = getHeaders(httpServletRequest);
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            template.header(entry.getKey(), entry.getValue());
        }
    }
    /**
     * 获取原请求头
     */
    private Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        if (enumeration != null) {
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                if (StrUtil.equals(OAuthConstant.TOKEN_NAME,key)){
                    map.put(key, value);
                    break;
                }
            }
        }
        return map;
    }
}

上述代码根本逻辑就是将请求头中Token信息放入RequestTemplate的头中。

注意:这里取出请求头中信息用的是RequestContextHolder

看到这里是不是明白了,RequestContextHolder中是将请求信息放入ThreadLocal中的,只能取到同一个线程的数据。

因此要解决异步调用的问题,只需要在发起远程调用之前给异步线程添加上主线程的上下文信息,此时最上方调用失效的代码变成如下:

//获取主线程的请求信息
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<T> future1 = CompletableFuture.supplyAsync(() -> {
   //将主线程的请求信息设置到异步线程中,否则会丢失请求上下文,导致调用失败
 RequestContextHolder.setRequestAttributes(attributes);
 //openfeign的调用
 return feign.remoteCall();
},executor);
CompletableFuture<T> future2 = CompletableFuture.supplyAsync(() -> {
   //将主线程的请求信息设置到异步线程中,否则会丢失请求上下文,导致调用失败
 RequestContextHolder.setRequestAttributes(attributes);
    //openfeign的调用
 return feign.remoteCall();
},executor);
CompletableFuture.allOf(future1,future2).join();
.....
相关文章
|
9月前
|
数据库 开发者
Seata调用问题之全局异常捕获没法回滚如何解决
Seata是一款开源的分布式事务解决方案,旨在提供高效且无缝的分布式事务服务;在集成和使用Seata过程中,开发者可能会遇到不同的异常问题,本合集针对Seata常见异常进行系统整理,为开发者提供详细的问题分析和解决方案,助力高效解决分布式事务中的难题。
648 13
|
4月前
|
自然语言处理 JavaScript Java
Spring 实现 3 种异步流式接口,干掉接口超时烦恼
本文介绍了处理耗时接口的几种异步流式技术,包括 `ResponseBodyEmitter`、`SseEmitter` 和 `StreamingResponseBody`。这些工具可在执行耗时操作时不断向客户端响应处理结果,提升用户体验和系统性能。`ResponseBodyEmitter` 适用于动态生成内容场景,如文件上传进度;`SseEmitter` 用于实时消息推送,如状态更新;`StreamingResponseBody` 则适合大数据量传输,避免内存溢出。文中提供了具体示例和 GitHub 地址,帮助读者更好地理解和应用这些技术。
678 0
|
并行计算 安全 Java
【JavaSE专栏80】多线程通信,多个线程之间如何实现信息传递和同步?
【JavaSE专栏80】多线程通信,多个线程之间如何实现信息传递和同步?
151 0
解决Feign远程调用参数里面内容丢失的问题
解决Feign远程调用参数里面内容丢失的问题
519 0
|
消息中间件 安全 数据库连接
.NET开发问题记录——TransactionScope出错:“与基础事务管理器的通信失败”的解决方法
.NET开发问题记录——TransactionScope出错:“与基础事务管理器的通信失败”的解决方法
326 0
|
NoSQL Java 数据库
java接口防重提交如何处理
举一个最简单的例子:日常开发中crud在业务系统中普遍存在,在服务端没有做任何处理,客户端没有做节流、防抖等限流操作时,同一秒一个用户点了两次新增按钮,导致数据库中存在同样两条数据,其结果可想而知,同理修改、删除同样的道理;查询本身具有幂等性,但是在同一秒钟同样的操作,查询多次和一次,有区别吗?区别大了去了,不谈用户体验如何,光是网络开销、流量占用、带给服务器的压力等等,生产中一点小的问题,如何不及时处理,可能会引发灾难性bug。
|
Java Spring
spring如何避免重复事件触发
spring如何避免重复事件触发
|
Java Spring
一不小心,你就掉进了Spring延迟初始化的坑!
由于 spring bean 的默认作用域是:singleton。所以在启动的时候 bean 会被初始化,如果被标记了@Lazy,会延迟初始化,但是如果被非懒加载的 Bean 注入了,@Lazy会失效。
|
设计模式 缓存 安全
【Hystrix技术指南】(6)请求合并机制原理分析
【Hystrix技术指南】(6)请求合并机制原理分析
223 0
【Hystrix技术指南】(6)请求合并机制原理分析