博客 / 詳情

返回

SseEmitter返回data被雙引號包裹的問題排查

一、背景

最近做接口的性能改造,大概背景如下:
舊:
1.前端每秒輪詢後端接口,接口返回數據狀態,前端用狀態做判斷,變更頁面交互。
2.前端固定調用後端接口,接口阻塞100秒,等待後端隨時返回結果,100秒到達後無結果,直接失敗。

新:
改為ServerSentEventtext-event-stream固定時間窗口由後端返回處理進度。

二、簡單對比

1.後端服務壓力大。尤其在用户量大,併發量大,又是系統核心業務時,不好把控。
2.阻塞Servlet線程, 當前業務同時訪問量大時,影響其他業務請求;最大等待時間不好把控,且受HTTP響應超時時間影響。
3.自由簡單,且實時性比較強,只要不是高併發入口請求業務就能使用。不佔用Servlet線程。

三、問題現象

1.主代碼展示

以下代碼均為精簡後的最小問題復現demo
  • SSE主接口
    PixPin_2025-11-16_11-57-06.png

2.問題表現

通過curl-N參數調用後,發現返回的data都被雙引號包裹,並且前端的EventSource.onmessage無法回調到(總是觸發onerror回調)
PixPin_2025-11-16_11-59-00.png

四.問題思考

參考並閲讀以下文檔
前端MDN ServerSentEvent文檔
Spring ServerSentEvent文檔

在前端側瀏覽器看到開發者工具內EventStream標籤頁顯示空白, 意識到確實是後端自身問題,觀察數據結構後發現,像是返回了JSON結構的String字符串,只有JSON結構下String兩側才會有引號,接着立刻翻源碼驗證。

從源碼org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler.HttpMessageConvertingHandler#sendInternal中找到線索,發現Spring在處理text-event-stream響應時,仍然是從Servlet Web的全局HttpMessageConverter查找,但這裏的mediaType值到底是哪個值呢,存在以下三種情況

  • Controller方法整體的MediaType,這個值固定是text/event-stream
  • SseEmitter#send(java.lang.Object, org.springframework.http.MediaType)方法整體的MediaType,這個值固定是text/plain
  • SseEmitter#send(java.lang.Object, org.springframework.http.MediaType)方法第二個參數傳入的MediaType,這個值動態的,我這裏是application/json;charset=UTF-8

這裏大概率是第二種導致的。
PixPin_2025-11-16_12-09-33.png

五、得到結論

由於在K8s內網部署,本機無法啓動,關聯的中間件和附加Bean太多。到這裏為止,其實問題清晰了,結合對Spring源碼的理解,項目中的全局HttpMessageConverter配置肯定有問題,我們公司前身有很多認為自己很厲害的人,都是從阿里轉過來的,酷愛fastjson,項目基建時,肯定幹掉了所有的HttpMessageConverter,統一換成了fastjson,接着找這個代碼

PixPin_2025-11-16_12-25-22.png

這是2021年,前人留下來的寶藏,只能説這個人複製的時候可能沒過腦

  • 紅色框出來的就是根因
  • 綠色框出來的就是完全無腦,二進制文檔,圖片,也要Json嗎?

1.看看上游Spring的處理

隨即問題來了,Spring默認會有一大堆的HttpMessageConverter自動配置,當前這個是addList<HttpMessageConverter<?>> converters的第一位嗎?默認的還在不在?

源碼如下: org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getMessageConverters
PixPin_2025-11-16_12-29-52.png

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addDefaultHttpMessageConverters如下
如圖就是Spring的解法
PixPin_2025-11-16_12-31-54.png

六、圈定影響

那麼新的問題來了,如果這個項目中有其他直接返回String的接口,豈不是意味着全部被包了一層雙引號?

恰好想起之前有其他人不聽勸,不使用Spring Actuator的健康檢查,非要手搓,返回的恰好是String,線上驗證一下
PixPin_2025-11-16_12-37-48.png

那就是影響到了所有直接返回String的接口。

七、問題復現

本地不能啓動,最小demo,debug一下

debug啓動後發現確實是這裏影響的,這裏會走兩次
一次前面的text/plain
PixPin_2025-11-16_12-17-41.png
實際的第二個參數的MediaType,值application/json;charset=UTF-8
PixPin_2025-11-16_12-18-09.png

看看HttpMessageConverter是不是錯選了
PixPin_2025-11-16_12-43-27.png

根因確定: text/plain錯誤配置了使用FastJsonHttpMessageConverter,導致返回的不是字符串,而是json字符串

八、問題修復

修復就非常簡單了,刪除不合理的MediaType配置,最前面追加StringHttpMessageConverter即可
PixPin_2025-11-16_12-50-20.png

當然為了縮小影響範圍,只刪除MediaType.TEXT_PLAIN配置即可

至此,問題修復。
PixPin_2025-11-16_12-52-46.png

user avatar codingdgsun 頭像 deltaf 頭像 buxiyan 頭像 91cyz 頭像 saoming_zhang 頭像 qiehxb8 頭像 shadowck 頭像 jacheut 頭像 thinkfault 頭像 zoux 頭像 xiaoxiaofeng_java 頭像 smart_doc 頭像
16 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.