基于时间缓存优化浏览器轮询阻塞问题
# 问题
笔者前段时间要开发一个实时监控的功能,由前端轮询间隔1s发起一次调用,后端接口输出当前这1s时间的监控信息。 理想情况下,请求和响应的结果会像下面一样有序执行。

实际上,随着轮询请求的增加后,响应结果变成了这样,1:00时的数据得到1:01的数据,而1:01的数据得到的也是1:01数据,请求和响应会出现偶发的延迟。

# 排查过程
笔者最先开始排查时以为是后端服务的性能瓶颈,排查是否是当前功能非常耗时有关,查看日志发现接口的耗时基本是毫秒级,不存在性能瓶颈。于是查看是不是后端服务有什么功能开销太大霸占CPU,经排查发现一个线程CPU利用率很高,就是和上文关联的查询,于是对该功能进行一次优化。再次跑任务时发现问题还是存在。 再次推断问题是否和网络延迟有关,查看当前带宽以及项目都是在内网运行的,所以也不存在网络延迟,自此我们排除了服务端的问题。
于是我们把排查方向对准了客户端,推断是否是接口请求后端有延迟,于是通过Google调试模式中的network关于timing一探究竟。
我们以这里以百度页面为例打开调试模式,在network中看到的timing,我们点开查看会发现如下参数:

这些参数的含义分别是:
1. Queued at :表示该请求加入到请求队列中的时刻,请求队列在打开F12后第一次发送请求的时候创建,直到关闭控制台的时候销毁。
2. Started at :表示请求开始处理的时刻。
3. Queueing:表示请求从加入到请求队列中到请求开始处理经过的时间。
4. Stalled:请求在可以被发送出去之前的等待时间(阻塞时间),一般是等待可复用的TCP连接释放的时间。浏览器对于单个域名只能同时建立4~6个TCP连接(不同浏览器实现有差异)。
5. Proxy Negotiation:浏览器和代理服务器连接的协商时间。
6. DNS Lookup:域名解析花费的时间。
7. Initial Connection:建立TCP连接花费的的时间,包括TCP握手/重试和协商SSL。
8. Request sent:发送请求花费的时间。
9. Waiting (TTFB):从发出请求到接收到响应第一个字节经过的时间,包括网络延迟时间。
10. Content Download:接收响应花费的时间。
2
3
4
5
6
7
8
9
10
11
排查时,笔者发现那些有延迟的接口Queueing非常耗时,基本都是1-2s,大概率推测是因为频繁的轮询请求导致需多请求完成后未能及时释放TCP(单个域名只能同时建立4~6个TCP连接),连接导致当前请求在队列中未能及时处理导致数据延迟。

# 解决方案
得出前端轮询不可靠以及websocket方案被驳回之后,笔者采用时间缓存的方案,即将第一次请求的时间缓存下来,后续前端发起的请求都会基于缓存的数据加1s,确保时间不会错误,尽管前端可能会出现延迟,但是页面渲染的时间是不会错的。

# 参考文献
从理解谷歌浏览器timing到优化页面请求阻塞的问题实战:https://www.jianshu.com/p/24a126a2329f (opens new window)