【問題】【SpringBoot】記一次springboot框架下用jackson解析RequestBody失敗的問題

wyinhua發表於2020-09-07

最近專案中遇到了一個問題,費好大勁才發現問題所在,並且修復了問題,下面分享一下這個問題的定位和修復的過程,
行文有些凌亂,因為中途嘗試過N多方法,勿怪。

先說下背景:該專案用的是SpringBoot框架,主要功能為對外提供一些Restful API,使用的Servlet容器是預設的Tomcat,Json解析工具是預設的Jackson,通過@RequestBody註解來解析body。
再說下問題:使用者通過https方法呼叫POST方法的介面,會偶現Jackson解析Json體失敗的問題。這個問題比較離奇,同樣的資料,同樣的介面,有時候會發生解析失敗。大概成功10+次會失敗一次的頻率。報錯資訊如下:

JSON parse error: Unexpected end-of-input: expected close marker for Object (start marker at [Source: (PushbackInputStream); line: 1, column: 1]); 
nested exception is com.fasterxml.jackson.core.io.JsonEOFException: Unexpected end-of-input: expected close marker for Object (start marker at 
[Source: (PushbackInputStream); line: 1, column: 1])\n at [Source: (PushbackInputStream); line: 1, column: 3]。

值得一提的是,line和column的值每次都會不一樣。

由於服務部署在公有云上,依賴的周邊服務有不少,所以要先進行問題的定界。

  1. 不通過公有云註冊的API,而是直接通過公網IP:PORT的形式呼叫,問題復現。排除APIG服務問題。
  2. 進入容器節點,直接呼叫容器內的pod ip,問題復現。排除k8s或者CCE服務的問題。
  3. 至此,已經能排除公有云服務的問題,可以專注定位是否是框架或者實現有問題。

接下來觀察一下報錯資訊,顯然是解析body體出了問題,看起來像是body體不完整導致的,而且每次提示的line和column值都不一樣,感覺是body體在哪裡被截斷了。那很自然的會做以下猜測:

  1. body體本身不合法。不再贅述,推薦一個線上平臺 bejson
  2. https請求對body內容做了攔截。檢視請求頭中是否包含Content-Length,如果包含的話,可能就是這個值有問題,把這個Content-Length從請求頭中刪除掉
  3. body體被spring框架攔截。
  4. body體被spring內的tomcat攔截。
--server.max-http-header-size=8192000
--server.tomcat.max-http-post-size=-1
--spring.server.tomcat.max-http-header-size=52428800
--spring.server.tomcat.max-http-post-size=-1
--spring.http.multipart.max-file-size=1000m
--spring.http.multipart.max-request-size=1000m

spring和tomcat的配置有很多,找了很多引數,都沒有解決問題,行吧,只能打斷點看了。
經過測試本地無法復現,那就只能對遠端伺服器上的deployment進行遠端debug。基於IDEA進行springboot工程的遠端除錯方法見我之前的blog:基於IDEA對springboot做遠端除錯

根據日誌打出來的棧資訊,不難找到是在AbstractJackson2HttpMessageConverter. readJavaType方法報的錯,這個Converter方法其實就是spring-web的http訊息轉換器,
readJavaType方法定義:
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException
報錯資訊顯示在這一行:
return this.objectMapper.readValue(inputMessage.getBody(), javaType)
這裡呼叫了Jackson的readValue方法對message這個流進行解析,那我們從這裡開始單步除錯,下一步應該進到jackson的readValue方法了,此時通過ctrl+左鍵進入這個方法,
發現jackson-core的版本為2.11.2,而報錯的棧資訊中jackson-core版本為2.10.2,兩邊的版本不一致?

由於spring-web版本是spring starter中指定的,帶著疑問,我嘗試著把starter的版本升級到最新的2.3.0.RELEASE,對環境上的應用進行了升級,
問題不再復現…好吧,終於解決。之前用的starter是2.2.5.RELEASE,有可能是倉庫中的該版本包有問題,亦或是這個版本下spring-web的版本就是有BUG,這個不得而知了…

相關文章