最近在做一个浏览器画数据图的任务。
任务其实很简单,浏览器通过ajax请求拿到服务端已经生成好的json文件,然后在前端浏览器绘制相应的折线图,饼状图等。

起初我的做法
leader给我这个任务的时候,我想这不很简单吗。好吧俩天交差,先给我一个大概的json数据结构,我来造个假数据。
技术栈里面使用vuejs,因为要根据不同的ID 过滤数据重新绘图,想想虽然原生Javascript也能搞定,但毕竟是内部一个演示项目,就杀鸡用一次牛刀吧。
敲敲敲。。。 好了,代码完成。上dev环境
上dev环境之后
上线之后,运维人员说:老铁,你这个不work呀?
我嘀咕:不work,是不是你打开的方式不对?
我自己打开浏览器看了一下,确实很长时间页面都是空白的,然后运行2、3分钟之后页面崩溃。纳尼?老子的代码居然这么脆弱?
我打开Chrome dev tool看一下千里之堤究竟溃于哪里。原来真实的json文件尺寸大概是2.5GB,我了个乖乖。有木有搞错呀?你们这些运维人员让浏览器去解析一个2.5GB的json文件,怎么可能完成吗?JSON.parse表示臣妾做不到呀。
运维人员:我不管,过俩天大老板就要看数据图,你看着办吧。
我:。。。

真实JSON文件数据图如下

你们这是为难傲天呀!!
我想的第一个解决办法就是:服务端流式读取大文件=>解析=>计算绘图所需的数据=>结果数据丢给前端
但是有个问题,计算绘图所需的数据这一步我都在前端实现好了呀,难道要把这部分代码搬到server来吗?而且就算这样,也只是把2.5GB的数据变成100MB的结果数据,这个结果数据还是很大呀,浏览器能解析这么大的json文件吗?
不行应该用一种更优雅的方式解决!
首先我知道一点就是,这个任务现在的瓶颈就是:浏览器无法一次解析这么大的JSON文件。内存其实都不是问题,Chrome现在一个page上GB的内存使用都是可以的。
要不然在浏览器与文件服务端之间加一个中间组件,这个中间组件将JSON拆分成一条条小的JSON数据,浏览器将这些小的JSON数据依次解析然后放入内存中。
然后这个中间组件就用一个websocket服务来实现。

进一步优化
考虑到JSON文件中有的字段数据并用不到,这里我在websocket服务做了一层过滤将用不到的字段丢弃,又为前端节省了一大笔内存开销。
果然这个方案可以了!json数据终于可以传输到浏览器了,而且这个方案中浏览器每次解析一小段json文件,不存在JSON.parse内存溢出的问题了。
附一张websocket传递拆分JSON的图片:

然鹅新的问题又来了!
合并的JSON数据已经变成Javascript对象了,准备挂载到vuejs的data上,但是一挂载崩溃!
卧槽,原来是vuejs需要给data数据设置get set等方法,为了UI及时响应数据的变化。然鹅我们的数据有329万条

vuejs在为这么多数据设置get set的时候,崩溃了!

找到了root cause,傲天自然知道该怎么做了。我不把这个数据挂载到vuejs中不就可以了,我把这些数据挂载到window对象上,然后vuejs组件画图的时候从window对象里面取值,完美解决,bingo!
总结
其实主要就是加了一个websocket服务来中转一下巨大JSON文件,并且将JSON文件拆分成小的JSON片段。中间用到了一些开源的技术,顺便分享给大家。
还有我想这个websocket服务还可以做成一个共用的中间件,岂不是更棒。
用到的开源项目:
- ws 非常简单又贼他么快的websocket服务,为了nodejs而生
- JSONStream 流水式解析巨大json文件
关于霸都丶傲天
- Github : Github.com/AJLoveChina
- zhihu : zhihu.com/people/AJLoveChina
- 纪念日:爱情表白结婚纪念日网站
