主頁 > 後端開發 > springboot升級程序中踩坑定位分析記錄 | 京東云技術團隊

springboot升級程序中踩坑定位分析記錄 | 京東云技術團隊

2023-04-28 15:00:46 後端開發

作者:京東零售 李文龍

1.背景

俗話說:為了修復一個小bug而引入了一個更大bug

因所負責的系統使用的spring框架版本5.1.5.RELEASE在線上出過一個偶發的小事故,最后定位為spring-context中的一個bug導致的,

為了修復此bug進行了spring版本的升級,最終定的版本為收銀臺團隊使用的版本5.2.12.RELEASE,對應的springboot版本為2.2.12.RELEASE,

選擇這個版本的原因是:

1.有團隊經過了長時間的線上驗證

2.修復了5.1.5.RELEASE對應的bug

2.升級上線

升級相關版本后在預發環境進行了驗證,暫未遇到關于框架的問題,本以為安全升級完成,在上執行緒序中發現在APP中無法訪問,此時還未掛載流量,

日志中分析是某些引數未決議到,后在nginx日志中查到相關請求,使用postman模擬請求可以正常使用

3.分析驗證定位原因

1.臨時修復

在代碼一致的情況下,唯一的可能就只能是線上與預發配置不同,經對比分析得出是某個過濾器的順序在線上未配置,按照預發的配置后可正常使用,我們暫且稱修改的這兩個過濾器為MA,

其中默認情況下執行順序為M->A,順序修改為A->M后正常,其兩者作用大致為:

M : 通用過濾器,決議url中的引數至parameterMap中,并初始化讀取了body中的inputstream進行了byte陣列的快取,用于解決重復讀取流問題 A: 特定處理器,先是查詢parameter中的引數,然后邏輯處理后再設定一些特殊引數,

2.為何需要改過濾器順序

經查未升級前過濾器的順序與升級后過濾器順序一致,為何升級spring框架后需要修改配置,此時猜測可能是spring在升級程序中修改了一部分代碼,

但未有頭緒,只能先調轉方向分析為什么postman和瀏覽器中的swagger可以正常使用

3.分析nginx日志

前端請求與postman請求的nginx日志進行了分析得出了原因,對比日志如下:

postman POST /shop/bpaas/floor?client&clientVersion&ip=111.202.149.19&gfid=getShopMainFloor&body= 前端 : POST /shop/bpaas/floor HTTP/1.0" 200 634 "-" "api" "0.94" 0.008 0.007 client&clientVersion&ip=111.202.149.17&gfid=getShopMainFloor&body=

經過以上對比發現雖然postman使用了post請求,但資料還是放置在url中,在經過系統的一個內置過濾器M時將url中的引數決議到了parameterMap中,后續過濾器可以使用

request.getParameter獲取到,注意此方法是解決問題的關鍵,此時還未意識到,

4.升級前后框架是否有大的修改

因升級的版本是升級了一個小版本號,所以不好對比升級的buglist,只能慢慢進行分析,后在分析過濾器時發現升級spring后過濾器個數由11個減少到了10個,減少了那一個為:

org.springframework.web.filter.HiddenHttpMethodFilter

此過慮器的作用是在瀏覽器不支持PUT、DELETE、PATCH等method時,可以在form表單中使用隱藏的_method引數支持這幾種method,好像跟引數決議沒有任何關系,

繼續分析升級版本中 (由2.1.3.RELEASE->2.2.12.RELEASE)是否修改了此過濾器的一些內容,后在2.2.0.M5的release notes中發現HiddenHttpMethodFilter相關的:

Disable auto-configuration of HiddenHttpMethodFilter by default github上對應的版本release notes: https://github.com/spring-projects/spring-boot/releases/tag/v2.2.0.M5

也就是說升級后HiddenHttpMethodFilter默認配置由enable修改為了disable,如果再修改回去是不是可以修復引數決議的問題呢?

5.添加過濾器enable配置

因bug修復串列中有對應的issues,所以找到了此過濾器對應的配置:

-Dspring.mvc.hiddenmethod.filter.enabled=true

添加后可以正常使用,證明是此過濾器中在某種條件下不可缺少,

6.未升級spring版本時disable驗證

在確認未升級版本的spring支持此引數的情況下,添加了以上引數,將默認的啟動修改成了禁用,經驗證:在不代碼修改的情況下,無此過濾器時引數無法決議,證明了上步的猜測,

7.深入原始碼分析

此時需要分析HiddenHttpMethodFilter過濾器中是否有特殊操作,原始碼如下:

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		HttpServletRequest requestToUse = request;

		if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
			String paramValue = https://www.cnblogs.com/Jcloud/archive/2023/04/28/request.getParameter(this.methodParam);
			if (StringUtils.hasLength(paramValue)) {
				String method = paramValue.toUpperCase(Locale.ENGLISH);
				if (ALLOWED_METHODS.contains(method)) {
					requestToUse = new HttpMethodRequestWrapper(request, method);
				}
			}
		}

		filterChain.doFilter(requestToUse, response);
	}

分析以上原始碼可以發現,有且只有一種可能,就是request.getParameter可能是解決問題的是關鍵,

8.大膽猜測

分析后原始碼猜測,第一步中的修改順序有可能是A中有呼叫getParameter,所以順序調整為A->M后,相當于間接使用了HiddenHttpMethodFilter,

9.開始驗證

在不使用HiddenHttpMethodFilter的情況下,如果在過濾器原有順序不修改的情況下,只要在M執行前呼叫了request.getParameter,理論上可以正常為使用,所以在debug情況下

利用工具在M過濾器呼叫前先行執行request.getParameter,發現的確可以正常使用,

10.分析過濾器

先前簡述了M的功能,主要是包裝了request,后讀原始碼時發現,如果是post請求,讀取body體中的資料后并未決議body中的引數至parameterMap中,而代碼中的其它過濾器都是

通過request.getParameter獲取的資料,重寫后的代碼:

public String getParameter(String name) {
		if ( this.parameterMap.containsKey(name) )
			return this.parameterMap.get(name);
		else {
			return super.getParameter(name);
		}
	}

在經過request包裝后,先是從paremeterMap中獲取資料,此時map肯定是沒有資料,只能從父類獲取,而父類獲取時會決議parameter,決議時使用到了inputStream,但M過濾器

的在初始化時決議了輸入流,此時tomcat內部使用內部的request獲取stream時將獲取到空資料,即無法從parameter中獲取到body體中的資料,

而如果在呼叫M前呼叫了request.getParameter,tomcat內部將提前于M決議parameter,可以保證后續可獲取到相關引數,

4. 修復方案

既然得出了結論,那么升級spring版本后修復此bug可選擇的方案就比較多了,主要有:

  1. 啟用HiddenHttpMethodFilter,添加對應的引數,保證升級前后過濾器個數與順序一致

  2. 調整理過濾器A與M的順序,保證M在A之前執行即可,

  3. 修改過濾器M內部的邏輯,不在初始化的時候決議body,或是在決議body后將引數重新放置到parameterMap中,

此文是筆者按照分析流程進行簡單驗證,分析驗證程序中難免有遺漏之處,如有錯誤遺漏還煩請各位指出共同進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/551435.html

標籤:其他

上一篇:springboot專案出現”java: 錯誤: 無效的源發行版:17“問題解決方案

下一篇:返回列表

標籤雲
其他(158267) Python(38108) JavaScript(25396) Java(18011) C(15221) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7152) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5870) 数组(5741) R(5409) Linux(5334) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4565) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2432) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1964) Web開發(1951) HtmlCss(1928) python-3.x(1918) 弹簧靴(1913) C++(1912) xml(1889) PostgreSQL(1874) .NETCore(1857) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • springboot升級程序中踩坑定位分析記錄 | 京東云技術團隊

    因所負責的系統使用的spring框架版本5.1.5.RELEASE在線上出過一個偶發的小事故,最后定位為spring-context中的一個bug導致的。 ......

    uj5u.com 2023-04-28 15:00:46 more
  • springboot專案出現”java: 錯誤: 無效的源發行版:17“問題解決方

    下面是報錯頁面 問題決議 在我個人遇到此問題的情況下,出現此錯誤的原因是springboot的版本與java版本不一致 在spring3更新后,idea在創建springboot專案時會默認選擇spring3,哪怕你選擇的是java8的版本 idea默認選擇spring3 在你以java8創建spr ......

    uj5u.com 2023-04-28 15:00:17 more
  • JasperReports教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 JasperReports入門教程 - 使用包含從環境設定,報告設計,編譯報告設計,填充報告,查看和列印報告,匯出,引數,資料源開始的基礎知識到高級知識的初學者教程,簡單易學地設計和創建JasperReports ,欄位,運算式,變數,部分,組,樣式,Scriplets,子報告,圖表,Co ......

    uj5u.com 2023-04-28 15:00:07 more
  • 如何判斷redis慢了

    來自 https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247532967&idx=1&sn=19790c981aa33502aa1e3a8abe9cd064&chksm=e92a7ca8de5df5befc6cc534cbabdb847e ......

    uj5u.com 2023-04-28 14:59:45 more
  • SpringBoot SpringSecurity 介紹(基于記憶體的驗證)

    SpringBoot 集成 SpringSecurity + MySQL + JWT 附原始碼,廢話不多直接盤 SpringBoot已經為用戶采用默認配置,只需要引入pom依賴就能快速啟動Spring Security。 目的:驗證請求用戶的身份,提供安全訪問 優勢:基于Spring,配置方便,減少大 ......

    uj5u.com 2023-04-28 14:45:43 more
  • 當 SQL Server(mssql-jdbc) 遇上 BigDecimal → 精度丟失,真坑!

    開心一刻 中午和哥們一起喝茶 哥們說道:晚上喝酒去啊 我:不去,我女朋友過生日 哥們瞪大眼睛看著我:你有病吧,充氣的過什么生日 我生氣到:有特么生產日期的好吧 需求背景 系統對接了外部系統,呼叫外部系統的介面需要付費,一個介面一次呼叫付費 0.03 元 同一個月內,同一個介面最高付費 25 元 統計 ......

    uj5u.com 2023-04-28 14:39:43 more
  • 訂單30分鐘未支付自動取消怎么實作?

    目錄 了解需求 方案 1:資料庫輪詢 方案 2:JDK 的延遲佇列 方案 3:時間輪演算法 方案 4:redis 快取 方案 5:使用訊息佇列 了解需求 在開發中,往往會遇到一些關于延時任務的需求。最全面的Java面試網站 例如 生成訂單 30 分鐘未支付,則自動取消 生成訂單 60 秒后,給用戶發短 ......

    uj5u.com 2023-04-28 14:24:04 more
  • Python資料型別

    Python語言中有6個標準資料型別。 不可變資料(3 個):Number(數字)、String(字串)、Tuple(元組); 可變資料(3 個):List(串列)、Dictionary(字典)、Set(集合)。 有序資料:元組,串列 無序資料:集合,字典 數字number 整型int 正或負整數 ......

    uj5u.com 2023-04-28 14:18:46 more
  • springboot專案出現”java: 錯誤: 無效的源發行版:17“問題解決方

    下面是報錯頁面 問題決議 在我個人遇到此問題的情況下,出現此錯誤的原因是springboot的版本與java版本不一致 在spring3更新后,idea在創建springboot專案時會默認選擇spring3,哪怕你選擇的是java8的版本 idea默認選擇spring3 在你以java8創建spr ......

    uj5u.com 2023-04-28 14:17:15 more
  • 如何判斷redis慢了

    來自 https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247532967&idx=1&sn=19790c981aa33502aa1e3a8abe9cd064&chksm=e92a7ca8de5df5befc6cc534cbabdb847e ......

    uj5u.com 2023-04-28 14:16:24 more