RequestContextHolder多线程获取不到request对象

RequestContextHolder多线程获取不到request对象,调用feign接口时,在Feign中的RequestInterceptor也获取不到HttpServletRequest问题解决方案。

1.RequestContextHolder多线程获取不到request对象
异常信息,报错如下:

2024-07-09 22:06:32.320 [pool-5-thread-2] ERROR com.mergeplus.handler.ObjectHandler:227 - class: interface org.jeecg.common.system.api.client.SysFeignClient, methodName=mergeRegion, error: {}
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
 at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
 at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:313)
 at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:329)
 at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:324)
 at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:283)
 at jdk.proxy2/jdk.proxy2.$Proxy179.getHeaderNames(Unknown Source)
 at com.mergeplus.handler.ObjectHandler.lambda$doHandler$0(ObjectHandler.java:155)
 at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1768)
 at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java)
 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
 at java.base/java.lang.Thread.run(Thread.java:833)

异常错误截图:
在这里插入图片描述
2.解决方案

        // 设置子线程共享(重点)
        final RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        merges.stream().map(e -> CompletableFuture.supplyAsync(() -> {
            JSONObject returnMap = null;
            try {
                if (e.getClientBean() != null) {
                    // 设置当前线程的 RequestAttributes(重点)
                    RequestContextHolder.setRequestAttributes(attributes);

                    Object returnValue = e.getMethod().invoke(e.getClientBean(), jsonObject.get(e.getSourceKey()));
                    if (returnValue == null) {
                        return e;
                    }
                    returnMap = JSON.parseObject(JSON.toJSONString(returnValue));

                    if (returnMap == null || returnMap.isEmpty()) {
                        return e;
                    }

                    result.put(e.getTargetKey(), returnMap.get(jsonObject.get(e.getSourceKey())));
                } else {
                    if (e.getUrl() == null || e.getUrl().trim().length() == 0) {
                        return e;
                    }

                    //设置Http的Header
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);

                    //设置访问参数
                    HashMap<String, Object> params = new HashMap<>();
                    if (e.getKey() == null || e.getKey().trim().length() == 0) {
                        params.put(e.getSourceKey(), jsonObject.get(e.getSourceKey()));
                    }
                    //设置访问的Entity
                    HttpEntity entity = new HttpEntity<>(params, headers);

                    // todo
                    ResponseEntity<Object> exchange = restTemplate.exchange(e.getUrl(), HttpMethod.GET, entity, Object.class);
                    if (exchange == null) {
                        return e;
                    }

                    Object body = exchange.getBody();
                    if (body == null) {
                        return e;
                    }

                    returnMap = JSON.parseObject(JSON.toJSONString(body));
                    if (returnMap == null || returnMap.isEmpty()) {
                        return e;
                    }

                    result.put(e.getTargetKey(), returnMap.get(jsonObject.get(e.getSourceKey())));

//                    if (body instanceof Map) {
//                        returnMap = (Map) body;
//                        if (returnMap == null || returnMap.isEmpty()) {
//                            return e;
//                        }
//                        result.put(e.getTargetKey(), returnMap.get(jsonObject.get(e.getSourceKey())));
//                    }
                }
            } catch (Exception ex) {
                log.error("class: {}, methodName={}, error: {}", e.getClientBeanClazz(), e.getMethod().getName(), ex);
            } finally {
                RequestContextHolder.resetRequestAttributes(); // 记得在最后重置请求属性(重点)
            }
            return e;
        }, executor)).toList().stream().map(CompletableFuture::join).collect(Collectors.toList());

在这里插入图片描述
在这里插入图片描述
FeignInterceptorConfig代码

package org.jeecg.config;

import java.io.IOException;
import java.util.*;


import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.config.mqtoken.UserTokenContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PathMatcherUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;

/**
 * @Description: FeignConfig
 * @author: JeecgBoot
 */
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@Slf4j
@Configuration
public class FeignInterceptorConfig {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (requestAttributes == null) {
                return;
            }
            //1.获取请求对象
            HttpServletRequest request = requestAttributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();

            if (headerNames == null || !headerNames.hasMoreElements()) {
                return;
            }
            //2.获取请求对象中的所有的头信息(网关传递过来的)
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();//头的名称
                // 跳过content-length,不然可能会报too many bites written问题
                if ("content-length".equalsIgnoreCase(name)) {
                    continue;
                }
                String value = request.getHeader(name);//头名称对应的值
                // System.out.println("name:" + name + "::::::::value:" + value);
                //3.将头信息传递给feign (restTemplate)
                requestTemplate.header(name,value);

//                if (CommonConstant.X_ACCESS_TOKEN.equals(name) && StringUtils.isBlank(value)) {
//                    String token = UserTokenContext.getToken();
//                    requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
//                }

            }

            //================================================================================================================
            //针对特殊接口,进行加签验证 ——根据URL地址过滤请求 【字典表参数签名验证】
            // todo
            if (PathMatcherUtil.matches(Arrays.asList(PathMatcherUtil.SIGN_URL_LIST),requestTemplate.path())) {
//            if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.SIGN_URL_LIST),requestTemplate.path())) {
                try {
                    log.info("============================ [begin] fegin api url ============================");
                    log.info(requestTemplate.path());
                    log.info(requestTemplate.method());
                    String queryLine = requestTemplate.queryLine();
                    String questionMark="?";
                    if(queryLine!=null && queryLine.startsWith(questionMark)){
                        queryLine = queryLine.substring(1);
                    }
                    log.info(queryLine);
                    if(requestTemplate.body()!=null){
                        log.info(new String(requestTemplate.body()));
                    }
                    SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
                    String sign = SignUtil.getParamsSign(allParams);
                    log.info(" Feign request params sign: {}",sign);
                    log.info("============================ [end] fegin api url ============================");
                    requestTemplate.header(CommonConstant.X_SIGN, sign);
                    requestTemplate.header(CommonConstant.X_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //================================================================================================================

        };
    }

}

3.相关大数据学习demo地址:
https://github.com/carteryh/big-data

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/788788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

共生与变革:AI在开发者世界的角色深度剖析

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已不再是遥不可及的概念&#xff0c;而是逐步渗透到我们工作与生活的每一个角落。对于开发者这一群体而言&#xff0c;AI的崛起既带来了前所未有的机遇&#xff0c;也引发了关于其角色定位的深刻讨论——AI…

Vue使用Echarts(入门级)

最终效果&#xff1a; npm install echarts --save // 先安装echarts<template><!-- 创建一个dom区域用于挂载echarts图表 --><div id"chart" style"width: 600px;height:500px;"/> </template> <script> import * as ech…

基于Unity3D的Rokid AR Glass项目开发环境搭建

初识Rokid AR 一、SDK简介二、准备工作1.软件环境2.硬件环境 三、快速接入SDK1.配置Package Manager2.安装UXR2.0 SDK 四、导入官方Demo进行模拟器测试五、Rokid AR系列教程 一、SDK简介 UXR2.0 SDK是Rokid为Unity开发者提供的AR开发工具包&#xff0c;提供空间定位跟踪、双目…

理解局域网技术:从基础到进阶

局域网&#xff08;LAN&#xff09;是在20世纪70年代末发展起来的&#xff0c;起初主要用于连接单位内部的计算机&#xff0c;使它们能够方便地共享各种硬件、软件和数据资源。局域网的主要特点是网络为一个单位所拥有&#xff0c;地理范围和站点数目均有限。 局域网技术在计算…

PowerShell install 一键部署mysql 9.0.0

mysql 前言 MySQL 是一个基于 SQL(Structured Query Language)的数据库系统,SQL 是一种用于访问和管理数据库的标准语言。MySQL 以其高性能、稳定性和易用性而闻名,它被广泛应用于各种场景,包括: Web 应用程序:许多动态网站和内容管理系统(如 WordPress)使用 MySQL 存…

RIP实验

目录 一、实验拓扑 二、实验需求 三、实验思路 四、实验步骤 1、配置IP地址&#xff08;截各设备配置图片&#xff09; 全图 R1 R2 R3 2、配置RIP协议&#xff08;截各设备配置图片&#xff09; R1 R2 R3 测试&#xff08;截连通测试截图、各设备RIP路由表学习截图…

04 效用评测层 显性指标+标准方法 对推荐效果定量评测

你好&#xff0c;我是大师兄。前面三节课我们介绍了推荐系统的数据采集层和数据加工层的相关内容&#xff0c;本节课我们重点介绍下推荐系统效用评价层的评测方法和评测指标。 这里的“效用评测”&#xff0c;通俗点说是指推荐系统输出数据的好坏是否符合用户和商业的预期&…

浏览器开发者视角及CSS表达式选择元素

点击想要查看的接口&#xff0c;然后点击检查&#xff0c;便可以切换到该接口对应的html代码 如果F12不起作用的话&#xff0c;点击更多工具&#xff0c;然后选择开发者工具即可 ctrlF可以去查阅相关的CSS表达式选择元素 如果没有加#t1&#xff0c;那么表示的是选择所有的p 使用…

QListWidget、QTreeWidget、QTableWidget的拖放

QListWidget、QTreeWidget、QTableWidget的拖放实验 QAbstractItemView::DragDropMode 的枚举值 QAbstractItemView::NoDragDrop0组件不支持拖放操作QAbstractItemView::DragOnly1组件只支持拖动操作QAbstractItemView::DropOnly 2组件只支持放置操作QAbstractItemView::DragDr…

解释 C 语言中的递归函数

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&#xff0c;看过的人都说好。 文章目…

如何在 C 语言中进行选择排序?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&#xff0c;看过的人都说好。 文章目…

潜在空间可视化(Latent space visualization)

在“深度学习”系列中&#xff0c;我们不会看到如何使用深度学习来解决端到端的复杂问题&#xff0c;就像我们在《A.I. Odyssey》中所做的那样。我们更愿意看看不同的技术&#xff0c;以及一些示例和应用程序。 1、引言 上次&#xff08;Autoencoders - Deep Learning bits #…

Linux——多线程(五)

1.线程池 1.1初期框架 thread.hpp #include<iostream> #include <string> #include <unistd.h> #include <functional> #include <pthread.h>namespace ThreadModule {using func_t std::function<void()>;class Thread{public:void E…

Python29 Tensorflow的基本知识和使用

1. TensorFlow TensorFlow 是一个开源的机器学习框架&#xff0c;由 Google Brain 团队开发。它用于数据流图的计算&#xff0c;尤其擅长深度学习任务。在 TensorFlow 中&#xff0c;数据流图&#xff08;Data Flow Graph&#xff09;是其核心概念之一&#xff0c;它定义了计算…

Excel第28享:如何新建一个Excel表格

一、背景需求 小姑电话说&#xff1a;要新建一个表格&#xff0c;并实现将几个单元格进行合并的需求。 二、解决方案 1、在电脑桌面上空白地方&#xff0c;点击鼠标右键&#xff0c;在下拉的功能框中选择“XLS工作表”或“XLSX工作表”都可以&#xff0c;如下图所示。 之后&…

徒手绘制 Android 通用进度条

拖动条&#xff08;FlexSeekBar&#xff09;&#xff0c;在Android的各个地方都非常常用&#xff0c;本文旨在自研一套通用的进度条&#xff0c;非常适合车载App使用 样式如下&#xff1a; 使用示例 <!--默认用法--> <com.max.android.ui.seekbar.FlexSeekBarandroi…

Retrofit框架源码深度剖析【Android热门框架分析第二弹】

Android热门框架解析&#xff0c;你确定不来看看吗&#xff1f; OkHttp框架源码深度剖析【Android热门框架分析第一弹】 Retrofit框架源码深度剖析【Android热门框架分析第二弹】 什么是Retrofit&#xff1f; 准确来说&#xff0c;Retrofit 是一个 RESTful 的 HTTP 网络请求…

雷达视频采集卡 HPx-410

产品简介 雷达视频采集卡 HPx-410&#xff0c;应用于接入导航雷达数据&#xff0c;导航雷达视频&#xff0c;适用于JRC雷达、古野furuon雷达、Sperry雷达等多种型号的雷达。 HPx-410 可以接入导航雷达数据&#xff0c;引入导航雷达原始回波&#xff0c;然后将雷达视频采集到计…

centos部署jar包

第一步&#xff1a; 将IDEA中的项目打包为jar,将这个jar文件放到centos服务器上的目录里&#xff0c;我在opt新建api目录&#xff0c;将jar文件放入&#xff0c;如下图&#xff1a; 第二步&#xff1a; 将需要读取的配置文件也放入此目录(其他目录也可以&#xff0c;和脚本中…

Python面试宝典第9题:买卖股票

题目 给定一个整型数组&#xff0c;它的第i个元素是一支给定股票第i天的价格。如果最多只允许完成一笔交易&#xff08;即买入和卖出一支股票一次&#xff09;&#xff0c;设计一个算法来计算你所能获取的最大利润。注意&#xff1a;你不能在买入股票前卖出股票。 示例 1&#…