说到优化,其实机会不多,费力不讨好,但需求不少,而且往往是刚需。一般来说针对请求优化的思路都是“收敛”,但是在这篇文章中,我们剑走偏锋,反其道行之,利用域名发散的方式缓解并发请求的限制。
最近后端前辈的带领下做一件“大事儿”,将原有的基于页面的广告请求改为行业内比较先进的单广告位请求。随之带来了好多未知的坑。过去一个页面只发出一个异步请求,现在要发出1*广告位数的请求,这一个页面50-110的请求数无论对于后台能否承接高并发还是前端对大量异步回调的处理,都是挑战。但是单广告位有事只能投放的先提条件,身为广告人,怎能不迈出这一步呢。
这次我们就先看看其中一个坑。我的一个页面中有100 多个广告位,要在页面刚加载的时候,并发100个异步请求。首先服务端的压力就不小,前端同时并发这么多的请求也会有一系列的问题:
1、大量的异步请求所带来的大量的回调,注意这些回调并不是按请求的顺序返回的;
2、浏览器并发限制,浏览器针对同一个域名可并发的请求数量是有限的,平均下来是6个。很明显我这100多个广告位请求要分好多批出发啊?
为什么要这么多的请求?
一开始的时候我也在问这个问题,毕竟在做单广告位之前,我好不容易写了一个基于页面的广告展示代码(一个页面只有一个请求,包含这个页面所有需要展示的广告数据)。那时候风和日丽、岁月静好、大错不出、小错不少。后台仅仅提供有数据的广告位,我将他们按照业务逻辑展示。但是后端前辈的鸡汤太好喝加之确实这段时间一直有一些问题解决不了,就是我们的广告后台只是将广告定位到了页面,而没有定位到具体的广告位;广告后台需要针对具体的广告位进行智能投放;后台需要对页面上的所有广告数据进行一次打包,就不得不在广告后台加入页面的概念,这完全是为了应对现有的模式,理想中广告投放后台应该专注于用户画像和针对资源(广告位)的服务,而前端也不该直接对广告位进行处理。这样两端都去掉页面这一层才是更好的选择,同时这也是业内普遍公认的处理办法——“单广告位请求”(智能广告的基石)。
出问题了——异步回调,啥时候干活
我们的广告逻辑要求一部分广告马上显示,这部分还好,回调就渲染;另一部分要求分好几批次展示,这我就要等这部分“贵族广告”都回来了在启动渲染。但是单广告位请求就不得不面临一个问题——丢包。如果发100个,回来99个,那我的99个还不展示了?这问题不难,相信你心中肯定有答案了,不就是设定一个“截至时间”吗,但是和下面问题混在一起就要多考虑一点了。
又出问题了——并发请求
既然没有选择原地踏步,而是勇敢前行,就要踩坑。第一个问题就是太慢,100多个好几批要很长时间才回来,我后续的广告渲染要等多久?时间长了,能保证都回来,但用户体验太差;时间太短,体验好了,但是100多个请求来不及回来。看来这个“截止时间”不好定。
最终业务方选择了用户体验,毕竟广告本就是异步渲染,在等要等多久啊!这样我就只有一条路了——“提速”,优化请求的耗时,能在截至时间内那会所有的请求。那么我们来看一下耗时由那些部分组成:
单一请求耗时=发出请求耗时+网络延时+服务器计算响应时间+网络延时
从上面来,网络不是我们能控制的、服务端也已经加上了两级缓存,看好像没有前端能优化的部分哈,但是这里仅仅是一个请求的耗时,100个旧不是这样了。100个请求并不是一口气都发出去,而是被浏览器限制住了。浏览器为了方式同一个域名下的并发请求过多,从而做了安全限制,一般允许同一域名同时发出6个请求,如果每一个请求的耗时固定,并且网络带宽正常,总的耗时应该取决于最后一个请求发出的时间:
50个
100个
总耗时=最后一个请求的等待时间+单一请求耗时
通过demo我们可以看出50个请求总耗时391ms减去dom ready ,50个请求耗时 = 391 - 155 = 236ms;100个请求耗时 = 850 - 214 = 636ms
单一请求,我优化不了,但我可以减少最后一个请求的等待时间。那么等待时间是由什么决定的呢?等待时间是由同域并发请求限制造成的。虽然域名收敛时后续请求可以利用长链接来减少开销,感觉会“快一点”,但是浏览器限制了并发请求数。如果我们走另一条路(域名发散)呢?经过我的测试发散后单广告位的并发请求数又明显提高,50个广告位2个域名,总耗时=259 – 157 = 102ms 节约了134ms 减少56.7%。100个域名3个域名总耗时 = 428 - 256 = 172ms 节约了464ms 减少72.9%。当然这是理想环境下,优化率较高。观察上线效果,总耗时减少在30%-50%这个范围内。
50个
100个
怎么(利用域名发散)做的?
其实很简单,我们对同一个服务申请多个域名。换个名字,浏览器就分别限制了,两个域名不就并发12个了嘛!这样并发的多了,等待的就少了,自然最后一个请求的等待时间,就大幅减少。总耗时也就可控在用户体验上可接受的范围。
开发和测试的时候我们通过绑hosts的方式让服务器IP 有好几个“外号”,当然上线的时候还是要麻烦运维同事部署到公网环境。
是不是域名越发散(越多)越好?
当然不是的,我们刚才提到域名收敛有一个最大的好处就是:后续的请求可以利用第一次建立好的长链接来减小网络开销,从而“提速”。当我100个请求,10个域名,就要建立10次链接。开销也不小。我们在域名收敛和域名发散之间需要折中一下。当然我在实际开发中发现50个请求时4个域名就已经比3个的耗时长了,而且3个域名的总耗时不是很稳定。我最终决定根据广告位数量来判断使用几个域名。30个请求以下使用1个域名;70个请求以下使用2个域名;120以下使用3个域名。
总结:
实际工作中网站优化的机会不多,优化过静态请求的同学一定都知道合并静态资源。其背后正是“域名收敛”的思想。但是一般网站合并静态资源后,刨除内容图片资源懒加载的数量,往往js、css、精灵图的总量往往不会太多而且通过按需加载,往往不会产生如此大的并发请求。像单广告位请求这高并发的应用场景还是不多见,我们利用域名收敛、发散这样的一些基础理论却解决复杂棘手的问题。想来无论外界对前端价值褒贬不一,做前端开发还是挺有意思的。