- dateformat優(yōu)先級
- $ref循環(huán)引用問題
- 總結(jié)
?
記者:大爺您有什么特長呀?fastjson:我很快。記者:25乘以23等于多少?fastjson:等于88。記者:??fastjson:你就說快不快吧!
?
這個略顯馬麗蘇的標(biāo)題,各位看官將就著看吧。主要是怕被噴。fastjson真的很好,我用不用我喜不喜歡的,太不重要了,我只是覺得不適合我而已。
話說以前GSON用得好好的,同事極力推薦我使用Fastjson,說很快云云。盡管我們的系統(tǒng)根本感知不出來這點速度差異。
之前也聽說Fastjson爆出來什么重大漏洞,但對我們基本沒什么影響,所以這一點倒是沒什么偏見。
然后在一個新項目上,腦抽抽,把gson換成了fastjson,還把spring boot默認(rèn)支持的jackson換成了fastjson。
然后就開始遇到了一些問題。先聲明,這真不是尬黑,為了文章效果,故意網(wǎng)上扒些黑料拼湊起來,本文所提到的問題,都來源于本人最近項目的真實經(jīng)歷。
dateformat優(yōu)先級
本來是一個風(fēng)和日麗的下午,一個非常簡單的改動需求。接口返回的時間只需要年月日日期類型不需要時分秒。
因為我配置全局時間格式化為yyyy-MM-dd HH:mmss
,于是我愉快的在javabean的屬性上加了個注解。
@JSONField(format="yyyy-MM-dd")
本地測試一下,沒問題,提交到測試環(huán)境,搞定,完美。
然后就接到產(chǎn)品的疑問,改動呢?
我登上去看了一下,唉,沒改到啊,日期還是帶了時分秒。我大意了啊,這么小的改動,又是在測試環(huán)境,就沒加驗證。
大意了那么現(xiàn)在的直接問題是:fastjson關(guān)于時間配置在局部的配置沒有生效,使用的還是全局配置。
現(xiàn)象是,開發(fā)環(huán)境windows上沒有問題,測試環(huán)境linux上出現(xiàn)了問題。兩者有什么區(qū)別呢?系統(tǒng)問題?
既然懷疑是兩個系統(tǒng)導(dǎo)致的問題,那么就在idea里模擬一下linux系統(tǒng)。在 VM options 添加 -Dos.name=linux
?
這不能完全模擬linux系統(tǒng),只針對通過
System.getproperty("os.name")
來判斷當(dāng)前系統(tǒng)做某些操作的時候有用。?
通過這種方式?jīng)]復(fù)現(xiàn)。
我又想到了遠(yuǎn)程調(diào)試。一陣操作猛如虎,遠(yuǎn)程調(diào)試倒是能進(jìn)斷點,只是斷點進(jìn)不了第三方j(luò)ar包的源碼。等于白搞。
得,還是回到源碼吧。拉下源碼,斷點,觀察JSONSerializer類,主要是writeWithFormat
方法。沒有發(fā)現(xiàn)問題。因為懷疑是系統(tǒng)導(dǎo)致的,在源碼中搜索'linux''unix'關(guān)鍵字,沒有發(fā)現(xiàn)。斷點整個流程重點觀察了一下這部份也沒有發(fā)現(xiàn)問題。
突然在 JSONSerializer.dateFormatPattern 上發(fā)現(xiàn)了這段注釋。
JSONSerializer.dateFormatPattern
這部份涉及到了調(diào)整dateformat的問題,重點在這個#1868
,這通常是github的問題編號。
?
1.對于開源項目來說,解決了BUG,通常會把問題編號放到注釋里面去。前提是注釋有必要。通過問題編號可以看到問題的前因后果。
2.通常來說,對于github開源項目都有issue區(qū),拿著這個到編號直接到issue一搜就能搜到。
3.但也有一些項級項目,如spark,flink是沒有issue區(qū)的,它們的類型問題發(fā)現(xiàn)描述追蹤都使用jira平臺。如:
https://issues.apache.org/jira/browse/SPARK-38349
在提交PR的時候標(biāo)題也嚴(yán)格按照[jira 編號][spark 子模塊(如core/sql) title]的規(guī)則來。?
所以拿著這個編號到issue
區(qū),不管有沒有issue
區(qū),也都可以直接到pullrequest
區(qū)直接搜索,就算PR標(biāo)題里沒有問題編號,PR描述肯定也是有的,只要是有嚴(yán)格PR流程的開源項目。
所以這個問題在這里
https://github.com/alibaba/fastjson/issues/1868
相應(yīng)的PR在這里
https://github.com/alibaba/fastjson/pull/2706
通過ISSUES描述的已知信息,可以看出他遇到的問題跟我是一樣的,而這個問題早在2018年就提出了。但問題描述不太專業(yè),沒有涉及到環(huán)境以及最重要的fastjson的版本問題。
而通過PR可知,這個問題最終在2020才解決,期間僅在ISSUES區(qū)提出的相同問題就有 #1868 #1968 #2029 #2452
4個。
解決問題的版本為:1.2.72.
這個信息很關(guān)鍵。我對照了我開發(fā)環(huán)境的版本,是高于1.2.72的,所以沒有出現(xiàn)測試環(huán)境的問題。所以,柯南告訴我們,排除了所有可能性,剩下的哪怕再可笑,也是最終問題所在。
那就是,測試環(huán)境所用的fastjson版本是低于1.2.72的。
這種可能性是存在的,因為我們用的是maven打代碼包,依賴包單獨存在。我最終在測試環(huán)境的依賴包目錄下發(fā)現(xiàn)了兩個Fastjson包,果然不出所料,有一個1.2.53的低版本,它就是罪魁禍?zhǔn)住?/p>
所以,最終這個問題有相當(dāng)大的程度是由于我們團(tuán)隊自身問題引發(fā)的。但通過解決這個問題的過程也發(fā)現(xiàn)了一些有意思的情況。
首先,F(xiàn)astjson在某一個版本為什么會引發(fā)這個問題。它肯定是某個PR改出問題的,rv,testcase覆蓋沒有到位。
其次,從試圖解決這個問題的3個PR的時間線,分別在2018年,2019年,2020年。說明,fastjson這個項目的contributor看起來有百來人,但其中過于依賴其中某1個或者某些主力人員。精力有限,某些優(yōu)先級不那么高的BUG只能放任。
fastjson同時這個項目的榮譽感并沒有那么高(或者叫并沒有那么吸引高手),它并不是apache頂級項目,要是其它諸如spark/flink,spring,哪怕是dubbo呢,很想象這些項目會有一個并不算復(fù)雜的BUG懸而未決長達(dá)3年時間。在這些頂級開源項目,大家都是拼了老命的想找些BUG來提交PR。
當(dāng)然,以上只是我個人的一點猜測。
?
復(fù)盤,遇到Fastjson的問題,一開始就應(yīng)該奔著github的issues區(qū),它大概率已經(jīng)被前人踩坑了。
?
基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
$ref循環(huán)引用問題
public?ResultBody?test?()?{
????????List?list?=?new?ArrayList<>();
????????Person?obj1?=?new?Person("張三",?48);
????????list.add(obj1);
????????Person?obj2?=?new?Person("李四",?23);
????????list.add(obj2);
????????Person?obj3?=?new?Person("王麻子",?17);
????????list.add(obj3);
????????List?young?=?list.stream().filter(e?->?e.getAge()?<=?45).collect(Collectors.toList());
????????List?children?=??list.stream().filter(e?->?e.getAge()18).collect(Collectors.toList());
????????HashMap?map?=?new?HashMap();
????????map.put("young",?young);
????????map.put("children",?children);
????????return?ResultBody.success(map);
????????}
以上測試接口返回前端什么?
{
????????"code":"200",
????????"message":"成功!",
????????"result":{
????????"young":[
????????{
????????"age":23,
????????"name":"李四"
????????},
????????{
????????"age":17,
????????"name":"王麻子"
????????}
????????],
????????"children":[
??????????{"$ref":"$.result.young[1]"}
????????]
????????}
????????}
我現(xiàn)在并不知道什么循環(huán)引用檢測,這時候它是我的知識盲區(qū)。此時,我觀察到的現(xiàn)象是,young
和children
兩個list對象中均引用指向了王麻子
這個對象。然后,在第2次children
引用的時候它在序列化的時候直接指向了第1個young
里相應(yīng)對象引用。
當(dāng)然遇到這個問題的時候,我在仔細(xì)觀察排除了非fastjson的問題以后,這次我學(xué)聰明了,我直接來到了github的issues區(qū),搜索$ref
。
果然有很多同道中人,近150個問題,從時間上來看還挺新鮮。我點擊了closed,既然關(guān)閉了,那肯定解決了吧。
我點進(jìn)了closed區(qū)第一個問題,然后作者讓升級到fastjson2。???
fastjson2連 fastjson 原作者都拋棄了 fastjson 了,哎,開源真難!
如果我沒有理解錯,fastjson和fastjson2可不是兩個版本的區(qū)別,是兩個項目也!據(jù)說API也有兼容性問題。直接這樣升級過去,談何容易!
我覺得這也是個槽點,F(xiàn)astjson好像并沒有一個穩(wěn)定維護(hù)的版本,遇到問題總是在升級,升級的過程中也沒做好質(zhì)量控制,又引入了新的問題。
還是在當(dāng)前項目尋求解決方法吧,哪怕升版本也好啊。終于在另一個問題下面找到了問題所在以及解決方案。
?
https://github.com/alibaba/fastjson/issues/3643
?
我現(xiàn)在知道這是由于循環(huán)引用檢測引起的。通過設(shè)置SerializerFeature.DisableCircularReferenceDetect
可以避免這個問題。
但是,我的代碼其實并沒有循環(huán)引用啊,只是兩個子對象引用了同一個對象而已。這算什么?誤傷嗎?更重要的,一些控制權(quán)應(yīng)該在使用者手里?
比如,當(dāng)前這個循環(huán)引用在序列化會出的問題,應(yīng)該是用戶手動去開啟,而不是默認(rèn)給用戶開啟。在優(yōu)先級上,全局應(yīng)該關(guān)閉,在有循環(huán)引用的地方,讓用戶選擇局部開啟。
現(xiàn)在我的前端并沒有使用Fastjson,面對"$ref":"$.result.young[1]"
這種文本,它能解析嗎?它不能呀。我測試了一下,好像使用fastjson也并不能解析回來:
注:經(jīng)提醒,這里應(yīng)使用完整報文解析,經(jīng)測試,確實可以。感謝提醒!
更可怕的問題是,剛好在測試環(huán)節(jié)有兩個子對象引用了同一個對象,被我提前發(fā)現(xiàn)了。如果測試環(huán)境沒有這樣的情況,在生產(chǎn)環(huán)境剛好遇到了呢?那就是生產(chǎn)事故了呀。
本來是一個挺好的設(shè)計點,能起到錦上添花的作用,但它卻可能暴雷,這是好心辦壞事。
同樣的,還有SerializerFeature.WriteMapNullValue
。如果一個字段值為null
,fastjson默認(rèn)就不返回該字段了。本來前后端約定好,如果為null
就怎樣處理的邏輯,可能在生產(chǎn)環(huán)境中突然暴雷啊。
就像WriteNullListAsEmpty
就很好,不錯的設(shè)計點,如果返回的list為null
的時候,用戶可以選擇讓它序列化為[]
,但它也不是默認(rèn)開啟的呀,給了用戶額外的選擇權(quán),對吧。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
總結(jié)
寫到這里的時候,我是真心覺得fastjson有比競品有些特色的地方。這真不是為了所謂的客觀公正,非要負(fù)面寫多點,再搞點正面的。
為了寫文章,那肯定要去試驗,得把競品也拿出來測試一下,一測試發(fā)現(xiàn)并不是fastjson獨有的,尷尬!
但我還是那句話,不管你信不信,對于開源項目,特別是這樣一個廣泛使用的開源項目,肯定有非常值得學(xué)習(xí)的地方。一個開源項目,如果整天拿著顯微鏡去觀察,那肯定能找出不少毛病。
這里稍微總結(jié)一下本文的信息點。并不一定是某個具體BUG,而是通過這個BUG,解決這個BUG背后所展現(xiàn)出來的fastjson的信息或趨勢。
?
1.review,testcase覆蓋不是很到位2.contributor看起來很多,但嚴(yán)重依賴主力人員。而主力精力有限,某些優(yōu)先級不那么高的BUG只能放任。3.這個項目的榮譽感并沒有那么高,或者叫并沒有那么吸引高手)。4.有些功能點應(yīng)該把控制主動權(quán)交給用戶,如DisableCircularReferenceDetect,WriteMapNullValue等。默認(rèn)開啟非常容易導(dǎo)致線上暴雷。5.作者已經(jīng)全面轉(zhuǎn)向fastjosn2,而且哪怕在這之前,對于fastjson沒有一個穩(wěn)定維護(hù)的版本,不斷升級,不斷引入新問題。
?
祝愿fastjson2越來越好,不要步struts2的后塵。
評論
查看更多