记者:大爷您有什么特长呀?
fastjson:我很快。
记者:23423乘以4534等于多少?
fastjson:等于2343.
记者:??
fastjson:你就说快不快吧!
这个略显马丽苏的标题,各位看官将就着看吧。主要是怕被喷。fastjson真的很好,我用不用我喜不喜欢的,太不重要了,我仅仅觉得不适合我罢了。
话说曾经GSON用得好好的,同事极力推荐我运用Fastjson,说很快如此。虽然咱们的体系根本感知不出来这点速度差异。
之前也听说Fastjson爆出来什么严峻缝隙,但对咱们根本没什么影响,所以这一点却是没什么偏见。
然后在一个新项目上,脑抽抽,把gson换成了fastjson,还把spring boot默认支持的jackson换成了fastjson。
然后就开始遇到了一些问题。先声明,这真不是尬黑,为了文章效果,成心网上扒些黑料拼凑起来,本文所提到的问题,都来源于自己最近项目的实在阅历。
dateformat优先级
本来是一个风和日丽的下午,一个十分简单的改动需求。接口回来的时刻只需要年月日日期类型不需要时分秒。
因为我装备大局时刻格局化为yyyy-MM-dd HH:mmss
,所以我愉快的在javabean的属性上加了个注解。
@JSONField(format="yyyy-MM-dd")
本地测验一下,没问题,提交到测验环境,搞定,完美。
然后就接到产品的疑问,改动呢?
我登上去看了一下,唉,没改到啊,日期仍是带了时分秒。
我粗心了啊,这么小的改动,又是在测验环境,就没加验证。
那么现在的直接问题是: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平台。
如:issues.apache.org/jira/browse… 在提交PR的时分标题也严格按照[jira 编号][spark 子模块(如core/sql) title]的规矩来。
所以拿着这个编号到issue
区,不论有没有issue
区,也都能够直接到pullrequest
区直接查找,就算PR标题里没有问题编号,PR描绘肯定也是有的,只要是有严格PR流程的开源项目。
所以这个问题在这儿
github.com/alibaba/fas…
相应的PR在这儿
github.com/alibaba/fas…
经过ISSUES描绘的已知信息,能够看出他遇到的问题跟我是相同的,而这个问题早在2018年就提出了。但问题描绘不太专业,没有涉及到环境以及最重要的fastjson的版别问题。
而经过PR可知,这个问题终究在2020才处理,期间仅在ISSUES区提出的相同问题就有 #1868 #1968 #2029 #2452
4个。
处理问题的版别为: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循环引证问题
public ResultBody test () {
List<Person> 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<Person> young = list.stream().filter(e -> e.getAge() <= 45).collect(Collectors.toList());
List<Person> 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]"}
]
}
}
我现在并不知道什么循环引证检测,这时分它是我的常识盲区。
此刻,我调查到的现象是,young
和children
两个list目标中均引证指向了王麻子
这个目标。然后,在第2次children
引证的时分它在序列化的时分直接指向了第1个young
里相应目标引证。
当然遇到这个问题的时分,我在仔细调查排除了非fastjson的问题今后,这次我学聪明了,我直接来到了github的issues区,查找$ref
。
果然有许多同道中人,近150个问题,从时刻上来看还挺新鲜。我点击了closed,已然封闭了,那肯定处理了吧。
我点进了closed区第一个问题,然后作者让晋级到fastjson2。 ???
假如我没有理解错,fastjson和fastjson2可不是两个版别的差异,是两个项目也!据说API也有兼容性问题。直接这样晋级过去,谈何容易!
我觉得这也是个槽点,Fastjson如同并没有一个安稳保护的版别,遇到问题总是在晋级,晋级的进程中也没做好质量操控,又引入了新的问题。
仍是在当时项目寻求处理方法吧,哪怕升版别也好啊。
总算在另一个问题下面找到了问题所在以及处理方案。
github.com/alibaba/fas…
我现在知道这是因为循环引证检测引起的。经过设置SerializerFeature.DisableCircularReferenceDetect
能够防止这个问题。
可是,我的代码其实并没有循环引证啊,仅仅两个子目标引证了同一个目标罢了。这算什么?误伤吗?
更重要的,一些操控权应该在运用者手里?
比如,当时这个循环引证在序列化会出的问题,应该是用户手动去敞开,而不是默认给用户敞开。
在优先级上,大局应该封闭,在有循环引证的当地,让用户挑选局部敞开。
现在我的前端并没有运用Fastjson,面对"$ref":"$.result.young[1]"
这种文本,它能解析吗?它不能呀。
我测验了一下,如同运用fastjson也并不能解析回来:
注:经提示,这儿应运用完好报文解析,经测验,的确能够。感谢提示!
更可怕的问题是,刚好在测验环节有两个子目标引证了同一个目标,被我提前发现了。假如测验环境没有这样的情况,在出产环境刚好遇到了呢?那便是出产事故了呀。
本来是一个挺好的规划点,能起到锦上添花的效果,但它却或许暴雷,这是好心办坏事。
相同的,还有SerializerFeature.WriteMapNullValue
。
假如一个字段值为null
,fastjson默认就不回来该字段了。本来前后端约定好,假如为null
就怎样处理的逻辑,或许在出产环境中忽然暴雷啊。
就像WriteNullListAsEmpty
就很好,不错的规划点,假如回来的list为null
的时分,用户能够挑选让它序列化为[]
,但它也不是默认敞开的呀,给了用户额外的挑选权,对吧。
总结
写到这儿的时分,我是诚心觉得fastjson有比竞品有些特色的当地。这真不是为了所谓的客观公平,非要负面写多点,再搞点正面的。
为了写文章,那肯定要去实验,得把竞品也拿出来测验一下,一测验发现并不是fastjson独有的,尴尬!
但我仍是那句话,不论你信不信,关于开源项目,特别是这样一个广泛运用的开源项目,肯定有十分值得学习的当地。一个开源项目,假如整天拿着显微镜去调查,那肯定能找出不少缺点。
这儿略微总结一下本文的信息点。 并不一定是某个详细BUG,而是经过这个BUG,处理这个BUG背后所展现出来的fastjson的信息或趋势。
1.review,testcase掩盖不是很到位
2.contributor看起来许多,但严峻依靠主力人员。而主力精力有限,某些优先级不那么高的BUG只能听任。
3.这个项目的荣誉感并没有那么高,或许叫并没有那么招引高手)。
4.有些功用点应该把操控主动权交给用户,如DisableCircularReferenceDetect,WriteMapNullValue等。默认敞开十分容易导致线上暴雷。
5.作者现已全面转向fastjosn2,而且哪怕在这之前,关于fastjson没有一个安稳保护的版别,不断晋级,不断引入新问题。
祝福fastjson2越来越好,不要步struts2的后尘。
注: 在研究fastjson依靠注入序列的问题是,同时也发现了相同情况下jackson的一点小问题,在这儿来自jackson的灵魂一击:@ControllerAdvice就能确保万无一失吗? – () 有兴趣的朋友能够看一看。