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

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

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

一、問題現(xiàn)象

最近偶遇一詭異棘手問題:一個用于獲取 tokenGET 接口,在生產環(huán)境不定期偶發(fā)出現(xiàn) 參數(shù)不存在 的問題。一度懷疑是前端的鍋,雖然前端同學再三以人格擔保!經(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 微服務框架,當請求從瀏覽器發(fā)送過來后,經(jīng)過了以下步驟:

在這里插入圖片描述

順著這個思路逐層排查:

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

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

2.2 發(fā)生位置

我們進一步分析,在過濾器增加請求參數(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);
    }
}

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

在這里插入圖片描述

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

眾所周知,SpringBoot 默認內置 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() 方法 在請求處理過程中僅在第一次調用 時通過解析 queryString 獲取 parameters 參數(shù)值,并設置 didQueryParameter=true 標識已解析處理。
  5. Http 請求處理完成,processor 通過 release 方法釋放連接重置參數(shù)屬性,request.recycle 方法重置 request 參數(shù)屬性(注意:這里 連接器及 request 對象并不會銷毀,connector 再次受理新的請求時,將復用連接器、processor 及 request 對象而非創(chuàng)建)。

在這里插入圖片描述

2.3 源碼解析

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

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

RequestParamMethodArgumentResolve 類的 resovleName() 方法,可以看到這里調用了 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ù)在解析處理后會設置 didQueryParameters 參數(shù)為 true。

在這里插入圖片描述

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

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

在這里插入圖片描述

在這里插入圖片描述

2.4 Tomcat機制

Tomcat 機制如下:

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

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

在這里插入圖片描述

2.5 原因總結

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

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

在這里插入圖片描述

三、問題復現(xiàn)

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

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

復現(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 壓測工具,設置 200 線程并發(fā)請求:

在這里插入圖片描述

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

在這里插入圖片描述

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

在這里插入圖片描述

四、問題修復

修復這個問題的話有兩種方式:

方式一: 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ā)性丟失問題分析及修復的詳細內容,更多關于Spring GET請求參數(shù)丟失的資料請關注腳本之家其它相關文章!

相關文章

最新評論