由OpenFeign异步调用时401报错(上下文丢失)引发的思考

文章讨论了在SpringMVC中使用异步处理时,如何处理OpenFeign请求的Token获取以及RequestContextHolder在多线程环境下的问题。作者提到,因为线程切换导致RequestContextHolder中的ThreadLocal失效,常规方法如传递ServletRequestAttributes可能并不适用,且request对象内部可能在主线程结束时被销毁的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步调用该代码时:

    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
        try {
            //...获取导入的文件list
            asyncTask.saveImport(tblYyCalculateService,list);
            log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
            return Result.ok("文件导入成功!数据行数:" + list.size());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Result.error(e.getMessage());
        }
    }
    @Async
    public void saveImport(IService service, List<TblYyCalculate> list) {
            service.saveBatch(list);

            MessageDTO messageDTO = new MessageDTO();
            messageDTO.setFromUser("admin");

            //openfeign调用执行成功通知
            sysBaseAPI.sendSysAnnouncement(messageDTO);
    }

sysBaseAPI会通过OpenFeign请求其他模块的接口,这时会通过FeignConfig中的拦截器获取Header中的Token:

  return (requestTemplate) -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
            String token;
            String queryLine;
            String signUrls;
            if (null != attributes) {
                HttpServletRequest request = attributes.getRequest();
                log.debug("Feign request: {}", request.getRequestURI());
                token = request.getHeader("X-Access-Token");
                if (token == null || "".equals(token)) {
                    token = request.getParameter("token");
                    if (StringUtils.isEmpty(token)) {
                        token = UserTokenContext.getToken();
                    }
                }

                log.info("Feign Login Request token: {}", token);
                requestTemplate.header("X-Access-Token", new String[]{token});
                queryLine = request.getHeader("X-Tenant-Id");
                if (queryLine == null || "".equals(queryLine)) {
                    queryLine = request.getParameter("X-Tenant-Id");
                }

                log.info("Feign Login Request tenantId: {}", queryLine);
                requestTemplate.header("X-Tenant-Id", new String[]{queryLine});
            } else {
                signUrls = UserTokenContext.getToken();
                log.info("Feign no Login token: {}", signUrls);
                requestTemplate.header("X-Access-Token", new String[]{signUrls});
                token = TenantContext.getTenant();
                log.info("Feign no Login tenantId: {}", token);
                requestTemplate.header("X-Tenant-Id", new String[]{token});
            }

此时由于多线程上下文切换导致RequestContextHolder中的ThreadLocal无法获取原先线程中的request,必然会导致token无效,请求失败。

那么,按照网上大多数的方法,直接将ServletRequestAttributes 通过参数传递给异步线程,再手动set到RequestContextHolder中,问题就可以解决了吗?

测试发现,request在主线程结束时会由于某些逻辑导致销毁,此时异步线程如果后执行,那么request的引用虽然还在,但内部的所有对象都为null。当然如果让主线程睡眠几秒,那么还可以让异步线程赶在主线程的request销毁前成功获取内部的对象。

为什么会导致request内部的对象被销毁,但request却还在,目前尚不清楚,肯请评论区大佬解惑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值