Fastjson 很快,但不適合我....

作者:nyingping
來源:juejin.cn/post/7215886869199863869

記者:大爺您有什麼特長呀?

FastJson:我很快。

記者:23423 乘以 4534 等於多少?

FastJson:等於 2343.

記者:??

FastJson:你就說快不快吧!

這個略顯馬麗蘇的標題,各位看官將就着看吧。主要是怕被噴。FastJson 真的很好,我用不用我喜不喜歡的,太不重要了,我只是覺得不適合我而已。

話說以前 Gson 用得好好的,同事極力推薦我使用 FastJson,說很快云云。儘管我們的系統根本感知不出來這點速度差異。

之前也聽說 FastJson 爆出來什麼重大漏洞,但對我們基本沒什麼影響,所以這一點倒是沒什麼偏見。

然後在一個新項目上,腦抽抽,把 Gson 換成了 FastJson,Spring Boot 默認支持的 Jackson 換成了 FastJson。

然後就開始遇到了一些問題。先聲明,這真不是尬黑,爲了文章效果,故意網上扒些黑料拼湊起來,本文所提到的問題,都來源於本人最近項目的真實經歷。

推薦一個開源免費的 Spring Boot 最全教程:

https://github.com/javastacks/spring-boot-best-practice

dateformat 優先級

本來是一個風和日麗的下午,一個非常簡單的改動需求。接口返回的時間只需要年月日日期類型不需要時分秒。因爲我配置全局時間格式化爲yyyy-MM-dd HH:mmss,於是我愉快的在 javabean 的屬性上加了個註解。

本地測試一下,沒問題,提交到測試環境,搞定,完美。

然後就接到產品的疑問,改動呢?

我登上去看了一下,唉,沒改到啊,日期還是帶了時分秒。我大意了啊,這麼小的改動,又是在測試環境,就沒加驗證。

那麼現在的直接問題是:FastJson 關於時間配置在局部的配置沒有生效,使用的還是全局配置。

現象是,開發環境 Windows 上沒有問題,測試環境 Linux 上出現了問題。兩者有什麼區別呢?系統問題?

既然懷疑是兩個系統導致的問題,那麼就在 idea 裏模擬一下 linux 系統。在 VM options 添加 -Dos.name=linux

這不能完全模擬 linux 系統,只針對通過System.getproperty("os.name")來判斷當前系統做某些操作的時候有用。

通過這種方式沒復現,我又想到了遠程調試。

一陣操作猛如虎,遠程調試倒是能進斷點,只是斷點進不了第三方 jar 包的源碼。等於白搞。

得,還是回到源碼吧。拉下源碼,斷點,觀察 JSONSerializer 類,主要是writeWithFormat方法。沒有發現問題。

因爲懷疑是系統導致的,在源碼中搜索'linux''unix'關鍵字,沒有發現。斷點整個流程重點觀察了一下這部份也沒有發現問題。

突然在 JSONSerializer.dateFormatPattern上發現了這段註釋。

這部份涉及到了調整 dateformat 的問題,重點在這個#1868,這通常是 github 的問題編號。

1.對於開源項目來說,解決了 BUG,通常會把問題編號放到註釋裏面去。前提是註釋有必要。通過問題編號可以看到問題的前因後果。

2.通常來說,對於 github 開源項目都有 issue 區,拿着這個到編號直接到 issue 一搜就能搜到。

3.但也有一些項級項目,如 spark,flink 是沒有 issue 區的,它們的類型問題發現描述追蹤都使用 jira 平臺。如:

https://issues.apache.org/jira/browse/SPARK-38349

在提交 PR 的時候標題也嚴格按照[jira 編號][spark 子模塊(如core/sql) title]的規則來。

所以拿着這個編號到issue區,不管有沒有issue區,也都可以直接到pullrequest區直接搜索,就算 PR 標題裏沒有問題編號,PR 描述肯定也是有的,只要是有嚴格 PR 流程的開源項目。

所以這個問題在這裏:

https://github.com/alibaba/fastjson/issues/1868

相應的 PR 在這裏:

https://github.com/alibaba/fastjson/pull/2706

通過 ISSUES 描述的已知信息,可以看出他遇到的問題跟我是一樣的,而這個問題早在 2018 年就提出了。但問題描述不太專業,沒有涉及到環境以及最重要的 FastJson 的版本問題。

而通過 PR 可知,這個問題最終在 2020 才解決,期間僅在 ISSUES 區提出的相同問題就有 #1868 #1968 #2029 #24524 個。

解決問題的版本爲:1.2.72.

這個信息很關鍵。我對照了我開發環境的版本,是高於 1.2.72 的,所以沒有出現測試環境的問題。

所以,柯南告訴我們,排除了所有可能性,剩下的哪怕再可笑,也是最終問題所在。

那就是,測試環境所用的 FastJson 版本是低於 1.2.72 的。

這種可能性是存在的,因爲我們用的是 maven 打代碼包,依賴包單獨存在。

我最終在測試環境的依賴包目錄下發現了兩個 Fastjson 包,果然不出所料,有一個 1.2.53 的低版本,它就是罪魁禍首。

所以,最終這個問題有相當大的程度是由於我們團隊自身問題引發的。但通過解決這個問題的過程也發現了一些有意思的情況。

首先,FastJson 在某一個版本爲什麼會引發這個問題。它肯定是某個 PR 改出問題的,rv,testcase 覆蓋沒有到位。

其次,從試圖解決這個問題的 3 個 PR 的時間線,分別在 2018 年,2019 年,2020 年。說明,FastJson 這個項目的 contributor 看起來有百來人,但其中過於依賴其中某 1 個或者某些主力人員。精力有限,某些優先級不那麼高的 BUG 只能放任。

同時這個項目的榮譽感並沒有那麼高(或者叫並沒有那麼吸引高手),它並不是 Apache 頂級項目,要是其它諸如 Spark、Flink、Spring,哪怕是 Dubbo 呢,很想象這些項目會有一個並不算複雜的 BUG 懸而未決長達 3 年時間。在這些頂級開源項目,大家都是拼了老命的想找些 BUG 來提交 PR。

當然,以上只是我個人的一點猜測。

覆盤,遇到 FastJson 的問題,一開始就應該奔着 github 的 issues 區,它大概率已經被前人踩坑了。

$ref 循環引用問題

以上測試接口返回前端什麼?

我現在並不知道什麼循環引用檢測,這時候它是我的知識盲區。此時,我觀察到的現象是,youngchildren兩個 list 對象中均引用指向了王麻子這個對象。然後,在第 2 次children引用的時候它在序列化的時候直接指向了第 1 個young裏相應對象引用。當然遇到這個問題的時候,我在仔細觀察排除了非 fastjson 的問題以後,這次我學聰明瞭,我直接來到了 Github 的 issues 區,搜索$ref

果然有很多同道中人,近 150 個問題,從時間上來看還挺新鮮。我點擊了 closed,既然關閉了,那肯定解決了吧。

我點進了 closed 區第一個問題,然後作者讓升級到 fastjson2。???

如果我沒有理解錯,FastJson 和 FastJson2 可不是兩個版本的區別,是兩個項目也!據說 API 也有兼容性問題。直接這樣升級過去,談何容易!

我覺得這也是個槽點,FastJson 好像並沒有一個穩定維護的版本,遇到問題總是在升級,升級的過程中也沒做好質量控制,又引入了新的問題。

還是在當前項目尋求解決方法吧,哪怕升版本也好啊。終於在另一個問題下面找到了問題所在以及解決方案。

https://github.com/alibaba/fastjson/issues/3643

我現在知道這是由於循環引用檢測引起的。通過設置SerializerFeature.DisableCircularReferenceDetect可以避免這個問題。

但是,我的代碼其實並沒有循環引用啊,只是兩個子對象引用了同一個對象而已。這算什麼?誤傷嗎?

更重要的,一些控制權應該在使用者手裏?

比如,當前這個循環引用在序列化會出的問題,應該是用戶手動去開啓,而不是默認給用戶開啓。在優先級上,全局應該關閉,在有循環引用的地方,讓用戶選擇局部開啓。

現在我的前端並沒有使用 FastJson,面對"$ref":"$.result.young[1]"這種文本,它能解析嗎?它不能呀。

我測試了一下,好像使用 FastJson 也並不能解析回來:

:經提醒,這裏應使用完整報文解析,經測試,確實可以。感謝提醒!

更可怕的問題是,剛好在測試環節有兩個子對象引用了同一個對象,被我提前發現了。如果測試環境沒有這樣的情況,在生產環境剛好遇到了呢?那就是生產事故了呀。

本來是一個挺好的設計點,能起到錦上添花的作用,但它卻可能暴雷,這是好心辦壞事。

同樣的,還有SerializerFeature.WriteMapNullValue。如果一個字段值爲null,fastjson 默認就不返回該字段了。本來前後端約定好,如果爲null就怎樣處理的邏輯,可能在生產環境中突然暴雷啊。

就像WriteNullListAsEmpty就很好,不錯的設計點,如果返回的 list 爲null的時候,用戶可以選擇讓它序列化爲[],但它也不是默認開啓的呀,給了用戶額外的選擇權,對吧。

總結

寫到這裏的時候,我是真心覺得 FastJson 有比競品有些特色的地方。這真不是爲了所謂的客觀公正,非要負面寫多點,再搞點正面的。

爲了寫文章,那肯定要去試驗,得把競品也拿出來測試一下,一測試發現並不是 FastJson 獨有的,尷尬!

但我還是那句話,不管你信不信,對於開源項目,特別是這樣一個廣泛使用的開源項目,肯定有非常值得學習的地方。一個開源項目,如果整天拿着顯微鏡去觀察,那肯定能找出不少毛病。

這裏稍微總結一下本文的信息點。並不一定是某個具體 BUG,而是通過這個 BUG,解決這個 BUG 背後所展現出來的 FastJson 的信息或趨勢。

  1. reviewtestcase 覆蓋不是很到位
  2. contributor 看起來很多,但嚴重依賴主力人員。而主力精力有限,某些優先級不那麼高的 BUG 只能放任。
  3. 這個項目的榮譽感並沒有那麼高,或者叫並沒有那麼吸引高手)。
  4. 有些功能點應該把控制主動權交給用戶,如 DisableCircularReferenceDetectWriteMapNullValue 等。默認開啓非常容易導致線上暴雷。
  5. 作者已經全面轉向 fastjosn2,而且哪怕在這之前,對於 fastjson 沒有一個穩定維護的版本,不斷升級,不斷引入新問題。

祝願 fastjson2 越來越好,不要步 struts2 的後塵。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這纔是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章