地图数据缓存方案

2018-08-12  本文已影响53人  凌晨的夜

问题描述

http://www.mypxh.com/
雪花网站中的故事是通过地图检测中心点的变化,并按照指定范围的半径,发送数据请求得到的。在下列场景中会出现大量不必要的请求:

优化意义

解决方案

我们要做的就是标记地图上已搜索的点,发送请求前先检查,如果发现要搜索的点未被标记,则发送数据请求,收到返回数据后更新标记范围。

数学建模

首先,以地图中心点为中心,2r为边长的正方形表示的是一片连续的范围,想要标记这个范围,最简单的方法是采样法,化连续为离散。拿到的中心点是六位小数,根据实际情况, 采用两位小数就可以达到较好的体验。
确定了采样点,接下来就是处理数据了。数组下标只能是整数,所以我们给每个经纬度乘以100,化小数为整数。纬度的范围是[-90,90],经度的范围是[-180,180],要想标记地图上所有点,就需要一个mn([902100, 1802*100])的二维数组,这个数据量非常大。初始化这样大的数组需要耗费大量内存。

经纬度.png
位运算节省内存

手机上的内存非常宝贵,我们可以采取时间换空间的策略。

先化二维为一维,可以想象彩虹弹簧。 弹簧.jpeg

然后用位运算压缩数据大小。
js中Number类型的实质是一个64位的浮点数,11位表示指数,1位表示正负,我们取32位来表示经度纬度位数48的点阵列,可以把数据压缩成[902100/8, 1802*100/4]的大小。
那么我们怎么存取某一位数据呢?用掩码(MASK)数组,MASK[i] = Math.pow(2, i)。取数据的时候和掩码数组进行逻辑与运算,存数据的时候逻辑非运算。

掩码与运算.jpeg

初始化地图数组时,依旧比较大,但其实没有初始化过的点是null,与初始化的0一样是逻辑非,不影响取值,因此我们可以只初始化一个长度为902100/8,内容为空的数组。这样,搜索过的点为1,未搜索过的点为null。此时我们已经把初始化的任务缩小了1802100 *8 = 288000倍。

web worker 初始化数组

js是单线程的。初始化数组比较耗时,会阻塞其他任务。一开始初始化会阻塞页面渲染,造成londing时间过长;放在地图渲染后会影响用户的操作,给用户拖不动的感觉。此时我们采用webWorker方案,另起一个线程。


Web-Worker.png

Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面,很适合处理计算密集型的工具。worker通过new worker(worker.js) 来新建worker进程,通过postMessage和onMessage来与主进程通信。worker.js必须是外部文件,这个可以通过webpakc的CopyWebpackPlugin插件来指定外部文件路径,一般情况是在'../static'路径下。
主进程中,postMessage和onMessage 机制太过简单,不利于事件的管理。所以要进一步封装workerManager 类。事件的类型用action表示,检查是否已搜索和更新已搜索点,分别命名为check和fresh。在发起事件的时候(postMessage)给每个事件添加一个带有ID,action和payload的监听者(listener)用与处理回调(onMessage)。
worker收到事件,根据action选择相应的处理函数,并根据payload处理数据并返回结果给主进程。主进程收到结果,取消此id的listener,进行下一步处理。

成果

我们用三天的时间形成了一个比较完善的地图数据缓存方案。这种性能优化在平时的业务中不常见,是一次比较有意义的探索,不但积累了工程化解决具体问题的经验,也形成了可复用的work和位运算的代码。
其实,探索方案的过程还是比较曲折的,比如

上一篇下一篇

猜你喜欢

热点阅读