背景


  在互联时代的客户端用户体验,与网络资源内容刷新的及时性息息相关,于是离不开HTTP资源的下载。本文将介绍Q+在复杂的客户端前后台下载需求下,如何管理下载任务才是最高效的。


影响用户体验的因素


无管理的每任务自启线程


  在无管理的情况下,资源是恶性争抢的,直到资源被耗尽。对于HTTP下载,还没等到资源耗尽,每个任务自己就已经忍受不了长时间的等待而超时了。后面还有恶性循环的重试,每个任务的失败带了的不仅是对带宽的严重浪费,还有对其他任务效率影响。


  在Q+这种大项目中,模块的开发过程是相互独立的,程序员们也知道这个问题的严重,于是各自做着在自己模块范围内的管理。于是在全局来看,资源的恶性争抢还是在一些功能模块同时并发时存在。


  在任务数(实质是网络连接数)达到一定极限后,采取一定策略。保障所有任务有质量的进行。


与其他程序争抢要有竞争力


  一般来讲,与其他程序争抢资源是一个恶性竞争的模式,特别是一些P2P软件。最终导致用户在这台机器上再新开启的程序体验会非常差。


  如果我们最具竞争力的争抢到了大多数的资源,于是用户感觉只要用了我们的程序,别的程序就别开了。这也是我们不希望看到的,所以我们要在不影响整体的网络质量的前提下,具有更好的竞争力。


用户频繁操作后,感觉越来越慢


图1

  如图1所示,当用户迅速打开三个窗口,每个窗口有四张图片,这里有两种表现:


  表现A,过了老半天后,这12张图才一起展现。


  表现B,图片是逐一下载的,于是会按照如图所示的顺序展现。


  不如我们假设当前带宽100k每秒,每张图片1M。


  表现A,120秒后12张图才基本同时展现


  表现B,10秒后第一张图片展现,然后每个10秒展现一张,最后在第120秒展现最后一张。


  无论是那种表现,用户最关心的最后开的窗口的四张图片只能在一分半钟之后展现,这是多么可悲的体验。


图2


  我们希望的体验是,如图2所示,用户在先后打开窗口1和窗口2后,由于窗口1内图片1和图片2已经或正在下载,此时打开了窗口2,于是窗口2的四张图片由于更重要的而优先于窗口1剩下的两张图。


  与上面的例子相似的有这样的一种情况:


图3


  在浏览类似网页内容时,网页内容一般是超出屏幕高度的,需要向下滚屏的。如图3所示,用户浏览该网页,迅速滚屏到最下。于是图片的载入是顺序的,用户停留的网页区域图片最后才载入。


图4


  我们希望的体验是,如图4所示,用户在浏览网页,已有图片1和图片2已经或正在下载,然后迅速滚屏到最下的过程中,下载任务的优先级不断调整,最终如图所示数字,用户所看到的网页区域最高优先级下载。


  注:本文不讨论文件cache相关和图片文件流的递进式显示的知识,虽然这些在部分情况可以优化用户体验,但是批量有效的下载任务管理是本文研究重心。


高效的任务管理分析与方案


拥挤与排队的比较


  我们先实验一下,在限速环境下下载10个相同文件,下面是分别在拥挤与排队模式下的时间轴:


拥挤


排队




  可以看到,一拥而上的下载过程,每个任务的速度被平摊了,所有任务只能全部最后时段完成,而排队模式部分任务排队,部分先快速完成,排队任务随后也能快速完成。


争抢能力与网络连接数


  一般来讲网络连接数越多,整体争抢到的资源越多,但不是绝对的线性正比关系的,而且增多到一定程度后网络质量下降。这些取决于复杂的网络环境和本机其他程序的影响。


  所以,通过测试我们会通过在不同网络环境下测试,得到最大连接数的参考值。而且,我们无法回避的是,在有些情况下有可能出现,由于最大连接数的限制,而性能没有不限制的好。这里如何动态调整最大连接数,通过技术手段探知网络状况,从而智能调整,应该是一个很好的研究方向。


前后台任务


  前台任务,用户操作而等待内容。这是最高优先级的,而且随着用户新操作的出现,新任务赋予更高优先级。可以理解为插到队首。


  后台任务,不紧急的或预备给用户使用的。


  由于上面的分析,得到通用的方案是:多个优先级的队列。对优先级编号,每个优先级的队列都可以通过插队首或加队尾的方式添加任务。另外还可以提供调整优先级,移动任务的功能。


图5

超时机制


  在HTTP传输中,初级的程序员会使用总超时机制。这是一种很粗鲁的方式。如果这个值设的比较小,而数据量大,在差的网络情况下是有可能呢一直失败的。而如果这个值设的比较大,对于少量数据,一直不超时,用户体验不好。


  对于网络连接一直不断,但数据一点没有的情况,对于大家都是很头疼的事。一般会有更为有效的方式,那就是低速超时:如果在一定时间内,数据量太少或没有数据,继而判断超时。


  我们选择总时间超时机制和低速超时机制结合的方式,让我们能更好的控制。


网络连接资源的重用


  在每次HTTP传输过程中,都会使用网络连接,而建立网络连接是比较耗时的。我们可以在完成一次HTTP传输之后,暂时不释放网络资源,过一小段时间后再释放。如果在这段时间内有新任务可以重用这些网络资源。一般除浏览器客户端外,很多客户端会经常访问相同的域名下的资源,那么这些网络连接是可以重用的。如此,就省去了建立网络连接的时间。


复杂的方案如何提供简单的接口


  如果方案太复杂,而使用这个模块的程序员还有仔细研究才能使用,而且存在错误使用的风险。这是我们不愿意看到的。在除了应有的HTTP请求参数外,在任务管理上优先级队列编号还不能完全完成前面说的插队前和加对后的需求。而为了让使用者不去关心这个逻辑下面有了一种对优先级队列的优化:


图6

  所以这里为排队管理,新增唯一的参数:优先级编号


  一般优先级编号只允许正数,使用此编号表示对应优先级队列,在该队列队尾添加任务。此参数默认值为1。


  特别为最高优先级任务提供永远插在最前面的模式,设置优先级编号为0。如此一个参数搞定,对于无特殊需求的直接使用正数选择优先级。


图7

  注意:在批量插入最前面的操作时,如果希望插入后顺序依旧,由于枚举最后一个会插到最前面,所以枚举原序列时请倒序枚举,避免图7中的情形。


展望


没有免费的午餐


  争抢能力与网络连接数相关,但是连接数越多,排队的效果就越小。所以说没有免费的午餐,对于这种多目标问题,只能交给决策者自己,选择适合自己的平衡的优化了。


客户端优化的方向


  在客户端与互联网越来越密切的今天,由于客户端的用户优先的特殊性,对于下载策略的控制有很多可以优化的地方。比如,对于下载需求的预判,资源使用习惯的缓存。都是值得我们进一步思索和探讨的。