欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring中GET請求參數(shù)偶發(fā)性丟失問題分析及修復(fù)

 更新時間:2025年04月23日 11:02:41   作者:不愿放下技術(shù)的小趙  
本文描述了一種在SpringCloud微服務(wù)架構(gòu)下GET接口偶爾出現(xiàn)參數(shù)丟失的問題,通過源碼分析和復(fù)現(xiàn),發(fā)現(xiàn)問題是由于線程中請求對象的生命周期管理導(dǎo)致的,解決方法包括改用POST請求或更換Tomcat為Undertow中間件,需要的朋友可以參考下

一、問題現(xiàn)象

最近偶遇一詭異棘手問題:一個用于獲取 tokenGET 接口,在生產(chǎn)環(huán)境不定期偶發(fā)出現(xiàn) 參數(shù)不存在 的問題。一度懷疑是前端的鍋,雖然前端同學(xué)再三以人格擔(dān)保!經(jīng)過長時間觀察,發(fā)現(xiàn)每每出現(xiàn)問題時,“再點一下就好了”!錯誤信息簡單明確,是大家熟知的參數(shù)缺失異常:

Required request parameter ‘phone’ for method parameter type String is not present

在這里插入圖片描述

這是怎么回事呢?這只是再普通不過的一個 GET 接口!

在這里插入圖片描述

二、問題分析

2.1 發(fā)生時間

由于項目使用的是 Spring Cloud 微服務(wù)框架,當(dāng)請求從瀏覽器發(fā)送過來后,經(jīng)過了以下步驟:

在這里插入圖片描述

順著這個思路逐層排查:

  • HTTP請求: F12查看參數(shù)正常,排除。
  • Nginx: 日志打印參數(shù)正常,排除。
  • Gateway: 日志打印參數(shù)正常,排除。
  • Controller: 參數(shù)丟失。。。

所以可以得出結(jié)論:參數(shù)丟失問題發(fā)生在 Spring Cloud 微服務(wù)內(nèi)部

2.2 發(fā)生位置

我們進(jìn)一步分析,在過濾器增加請求參數(shù)的打?。?/p>

LogFilter.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
public class LogFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        log.info(">>>>>>>>>>【INFO】request.getQueryString(): {}", httpServletRequest.getQueryString());
        log.info(">>>>>>>>>>【INFO】request.getParameter(): {}", httpServletRequest.getParameter("phone"));

        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }
}

再次復(fù)現(xiàn)問題后,在同一個 traceId 對應(yīng)的日志中,打印結(jié)果如下:

在這里插入圖片描述

可以發(fā)現(xiàn)在問題請求中 request.queryString() 正常,而 request.getParameter() 值卻沒有獲取到!

眾所周知,SpringBoot 默認(rèn)內(nèi)置 tomcat 容器,SpringMVC 則通過 request.getParameter() 方法獲取并綁定 Controller 接口參數(shù)的。因此,初步判斷:在 tomcat 獲取 parameter 參數(shù)的時候出現(xiàn)了問題。

那么,parameter 參數(shù)的獲取過程是怎樣的?

  1. SpringMVC 框架通過 DispatcherServlet 實現(xiàn)。
  2. Tomcat 接收到外部請求,將由 connector 通過 Processor 受理 http 請求。
  3. SpringMVC 通過 request.getParameter() 獲取并綁定 Controller 接口參數(shù)。
  4. request.getParameter() 方法 在請求處理過程中僅在第一次調(diào)用 時通過解析 queryString 獲取 parameters 參數(shù)值,并設(shè)置 didQueryParameter=true 標(biāo)識已解析處理。
  5. Http 請求處理完成,processor 通過 release 方法釋放連接重置參數(shù)屬性,request.recycle 方法重置 request 參數(shù)屬性(注意:這里 連接器及 request 對象并不會銷毀,connector 再次受理新的請求時,將復(fù)用連接器、processor 及 request 對象而非創(chuàng)建)。

在這里插入圖片描述

2.3 源碼解析

下面,我們可以看一些源碼的片段來驗證一下:

源碼1:SpringBoot 從 request 獲取 parameter 參數(shù)。

RequestParamMethodArgumentResolve 類的 resovleName() 方法,可以看到這里調(diào)用了 request.getParameterValue() 方法。

在這里插入圖片描述

源碼2:tomcat 封裝了解析參數(shù)。

org.apache.catalina.connector.Request 類的 getParameterValues() 方法,request 通過 Parameters 獲取 parameter 參數(shù)。

在這里插入圖片描述

在這里插入圖片描述

源碼3:Parameters 從 queryString 解析封裝 parameter 參數(shù)。

org.apache.tomcat.util.http.Parameters 類的 handleQueryParameters() 方法,可以發(fā)現(xiàn),參數(shù)在解析處理后會設(shè)置 didQueryParameters 參數(shù)為 true。

在這里插入圖片描述

源碼4:請求處理結(jié)束,重置參數(shù)屬性,并不銷毀對象。

org.apache.tomcat.util.http.Parameters 類的 recycle() 方法。

在這里插入圖片描述

在這里插入圖片描述

2.4 Tomcat機制

Tomcat 機制如下:

  • tomcat 可支持多個 service 示例;
  • 每個 service 實例維護了一個包含多個 connector 的連接池;
  • 當(dāng) service 接收到了一個 http 請求時,則從 connector 池中獲取一個 connector 連接器進(jìn)行響應(yīng)處理。
  • connector 連接器是通過 Processor 對應(yīng) HTTP 請求進(jìn)行響應(yīng)處理。

Processor 封裝了 requestresponse 對象,在請求處理開始時進(jìn)行初始化封裝(進(jìn)封裝參數(shù)屬性,并不創(chuàng)建對象),請求處理完成后,則進(jìn)行釋放重置。(注意:這里的釋放僅指重置參數(shù)屬性,并不銷毀對象!

在這里插入圖片描述

2.5 原因總結(jié)

本次問題的根本原因在于 線程中引用了 request 對象,并在線程中調(diào)用了 request.getParameter() 方法使參數(shù)屬性 didQueryParameter 錯誤而導(dǎo)致 http 請求無法正確獲取參數(shù)值。

  • 假設(shè)第一次受理 http 請求的連接器為 connector1;
  • 請求 request 在子線程 thread1 中被引用;
  • connector1 完成 http 請求并執(zhí)行 release 釋放連接,這時 request.didQueryParameters 值為 false;
  • 如果子線程 thread1 處理任務(wù)的時間較長,調(diào)用了 getParameter() 方法,這時 request.didQueryParameters 值將再次被更新為 true;
  • 當(dāng) tomcat 再次通過 connector1 受理新的 http 請求時,由于 request.didQueryParameters=true,這時新請求調(diào)用 getParameter() 方法將不會再解析 queryString,因而無法正確獲取 parameter 參數(shù)值。

在這里插入圖片描述

三、問題復(fù)現(xiàn)

這里為了方便,我們使用 Hutool 的線程池工具。依賴如下:

<!-- Hutool -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.23</version>
</dependency>

復(fù)現(xiàn)代碼如下:

DemoController.java

import cn.hutool.core.thread.ThreadUtil;
import com.demo.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Slf4j
@RestController
@RequestMapping("/demo")
public class DemoController {

    /**
     * 根據(jù)手機號獲取token
     */
    @GetMapping("/getToken")
    public Result<Object> getToken(@RequestParam String phone) {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        ThreadUtil.execute(() -> {
            RequestContextHolder.setRequestAttributes(attributes);
            ThreadUtil.safeSleep(1000);
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            System.out.println("********** " + request.getParameter(phone));
        });
        return Result.succeed();
    }

}

使用 Jmeter 壓測工具,設(shè)置 200 線程并發(fā)請求:

在這里插入圖片描述

壓測 http://localhost:8080/demo/test?phone=111111 接口,配置請求信息如下:

在這里插入圖片描述

成功復(fù)現(xiàn),結(jié)果如下所示:

在這里插入圖片描述

四、問題修復(fù)

修復(fù)這個問題的話有兩種方式:

方式一: GET 請求改為 POST請求,使用 JSON 格式傳輸數(shù)據(jù)。

(經(jīng)過嘗試,即使使用 POST 請求,不使用 JSON 格式傳輸數(shù)據(jù)的話,還是會丟失參數(shù)。)

方式二: 將 tomcat 中間件替換為 undertow 中間件。修改后如下所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

將 tomcat 替換為 undertow 之后,發(fā)現(xiàn)不再出現(xiàn)參數(shù)丟失的情況。

在這里插入圖片描述

以上就是Spring中GET請求參數(shù)偶發(fā)性丟失問題分析及修復(fù)的詳細(xì)內(nèi)容,更多關(guān)于Spring GET請求參數(shù)丟失的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringCloud使用AOP統(tǒng)一處理Web請求日志實現(xiàn)步驟

    SpringCloud使用AOP統(tǒng)一處理Web請求日志實現(xiàn)步驟

    這篇文章主要為大家介紹了SpringCloud使用AOP統(tǒng)一處理Web請求日志實現(xiàn)步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • swagger文檔增強工具knife4j使用圖文詳解

    swagger文檔增強工具knife4j使用圖文詳解

    這篇文章主要介紹了swagger文檔增強工具knife4j使用詳解,想要使用knife4j非常簡單,只要在Springboot項目中引入knife4j的依賴即可,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解

    Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解

    這篇文章主要為大家介紹了Spring?Data?JPA?注解Entity關(guān)聯(lián)關(guān)系使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • springboot根據(jù)實體類生成表的實現(xiàn)方法

    springboot根據(jù)實體類生成表的實現(xiàn)方法

    本文介紹了如何通過SpringBoot工程引入SpringDataJPA,并通過實體類自動生成數(shù)據(jù)庫表的過程,包括常見問題解決方法,感興趣的可以了解一下
    2024-09-09
  • spring基于通用Dao的多數(shù)據(jù)源配置詳解

    spring基于通用Dao的多數(shù)據(jù)源配置詳解

    這篇文章主要為大家詳細(xì)介紹了spring基于通用Dao的多數(shù)據(jù)源配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下解
    2018-03-03
  • Springboot常用注解及配置文件加載順序詳解

    Springboot常用注解及配置文件加載順序詳解

    這篇文章主要介紹了Springboot常用注解及配置文件加載順序,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • 深入理解SpringBoot事務(wù)傳播機制

    深入理解SpringBoot事務(wù)傳播機制

    本文介紹了SpringBoot中事務(wù)傳播機制的原理及其常用配置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • 一不小心就讓Java開發(fā)踩坑的fail-fast是個什么鬼?(推薦)

    一不小心就讓Java開發(fā)踩坑的fail-fast是個什么鬼?(推薦)

    這篇文章主要介紹了Java fail-fast,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • SpringBoot項目中JDK動態(tài)代理和CGLIB動態(tài)代理的使用詳解

    SpringBoot項目中JDK動態(tài)代理和CGLIB動態(tài)代理的使用詳解

    JDK動態(tài)代理和CGLIB動態(tài)代理都是SpringBoot中實現(xiàn)AOP的重要技術(shù),JDK動態(tài)代理通過反射生成代理類,適用于目標(biāo)類實現(xiàn)了接口的場景,性能較好,易用性高,但必須實現(xiàn)接口且不能代理final方法,CGLIB動態(tài)代理通過生成子類實現(xiàn)代理
    2025-03-03
  • ArrayList的自動擴充機制實例解析

    ArrayList的自動擴充機制實例解析

    本文主要介紹了ArrayList的自動擴充機制,由一個題目切入主題,逐步向大家展示了ArrayList的相關(guān)內(nèi)容,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10

最新評論