首先设想我们在使用一个服务的时候,是不是低延迟的服务对我们更加有吸引力,你一直在看LOADING标志的时候是不是也觉得很烦躁。随着技术的发展,越来越多的服务需要具备更高的响应速度。比如google class的无缝交互,好比一个虚拟现实,要在一个桌子上放一个小火龙。你都换了个视野了,小火龙才出来,在一个奇怪的位置就会显得很蠢。
用户这边提出了需求,服务方就要解决需求。我们都知道网络是会抖动的,有可能可能会出现暂时性的高延迟。但是这个高延迟在所有请求里的某个重要环节是十分糟糕的。
我们在构建分布式系统的时候,我们非常注重的它的可靠性,因为他是构建在一群不可靠的组件上的。现在我们需要创建一个响应可预测的整体在很多响应不可预测的服务上。这个能力就称为"tail-tolerant"
为什么会存在抖动?
软件原因
- 单机上的CPU共享,处理器缓存的共享,内存和网络带宽的共享。造成会发生竞争而产生抖动
- 守护进程在调度起来时会增加延迟
- 全局性的资源,比如分布式文件系统的资源共享。
- 后台活动,比如BIGTABLE 后台的日志压缩程序,或者JAVA的GC。
- 中间件和网络交换机的多层队列会放大抖动
硬件原因
- CPU的高温降频技术
- 固态存储设备的垃圾回收
- 节电模式下的能源管理带来额外延迟
组件的抖动在规模增长下的放大
当用户打开亚马逊购物网站的一个商品信息页时,可能需要从底层数百个子服务来获取信息,而这些子服务还有它们依赖的更基础的服务,这意味着一个用户的请求可能需要数千台服务器协同工作才能完成。
在这样的集群规模下,即使有极少数机器请求响应时间较长,也有很大的概率导致用户请求的响应时间变长,如下图所示:
imageimage.png当用户请求需要100台服务器来协同完成时,假设只有 1%的调用延迟较大,那用户请求受影响导致响应时间较长的概率为0.63(1 - 0.99 ** 100)
当用户的请求需要2000台服务器来协同完成时,假设只有 0.01%的调用延迟较大,那用户请求受影响导致响应时间较长的概率为0.18(1 - 0.9999 ** 2000),相当于每5个用户就有一个体验不佳
降低组件的抖动
- 根据服务类别差异化和更高层次的队列机制: 比如自己维护一个队列允许服务器跳过那些更早到来的非延迟敏感的批处理操作,优先往下传那些高优先级的交互请求。
- 降低线头阻塞: 把长时间云清的请求切分成一系列小请求,来防止少数开销极高的查询请求影响到其他请求
- 后台操作在总体负载较低时触发。还可使多台机器同时做后台活动,这样就会影响到那些出现在这个短暂时间区间的请求。如果不同时做,总有机器在后台做,就会有长尾延迟。
和延迟抖动友好相处。
我们可以用技术来掩盖或绕过延迟问题,而不是试图完全消灭它。在技术上分为2大类:1.专注于请求内部的立即响应技术。 2. 跨请求的长时间适配
专注于请求内部的立即响应技术
很多WEB服务器都会复制多个副本来提供高吞吐和高可用。下面的技术展示了如何采用副本来减少单个请求的延迟抖动。
对冲请求:
客户端首先发送一个请求给服务端,并等待服务端返回的响应;
如果客户端在一定的时间内没有收到服务端的响应,则马上发送同样的请求到另一台(或多台)服务器;
客户端等待第一个响应到达之后,终止其他请求的处理;
上面“一定的时间”定义为:95%的请求的响应时间。
Google的测试数据表明,采用这种方法,可以仅用2%的额外请求,将系统99.9%的请求的响应时间从1800ms降低到74ms:
For example, in a Google benchmark that reads the values for 1,000 keys stored in a BigTable table distributed across 100 different servers, sending a hedging request after a 10ms delay reduces the 99.9th-percentile latency for retrieving all 1,000 values from 1,800ms to 74ms while sending just 2% more requests.
捆绑请求:
对冲请求技术的问题是他只能优化那些超过95%线的请求,这样收益限制在了一少部分请求上。如果我们可以对请求进行更快速的取消,可以有新的成果。
抖动一个常见的来源是请求被执行前在服务端的排队延迟。对于很多服务来说,一旦请求被调度起来开始执行,它的完成时间变化很小。所以我们可以不是选择某个服务器,而是将某个请求同时在多个服务器上入队。 当一个请求开始执行时,会向其他服务器发送取消消息。其他服务器发现它还在排队,就可以立即终止它或者把它的优先级设置最低。
此处有一个问题,就是怕所有服务器都同时开始了处理请求,这种情况的常见场景是两个服务器队列都为空。因此客户端在发送第一个请求和后一个请求之间要引入一个平均网络消息延迟大小。(现代数据中心通常这个值小于1MS)。
image.png
另一种改进:
还有一种思路是首先对远程队列进行探测,然后将请求提交到负载最轻的服务器上。这个有帮助,但是不如捆绑请求效果好。
因为
- 检测时和请求时的负载会改变;
- 待处理请求的用时通常难以预测;
- 如果客户端都选择低负载的服务器,那么这个服务器会进入高负载状态。
上面这些方法起作用的前提是:造成请求延迟波动的成因并不会影响多个请求副本。同时我们认为这种非关联病症在大规模系统中更普遍。
跨请求的长时间适配
这里讨论的主要是由粗粒度现象引发的延迟抖动。(比如服务器时间变动和负载不均衡)
尽管程序员在设计的时候都尽可能的把分区的开销设置尽可能均衡,但是由于配置是静态的,随着时间的推移。会发生改变。比如分区内的异常数据项会突然成为访问热点,那么它所在的分区负载会急剧上升。
-
微分区:让分区数据远远大于机器数量,然后通过动态分配分区到机器来实现负载均衡。
-
选择性副本:检测那些导致负载不均衡的项目,然后通过创建项目额外的副本来缓解负载不均衡问题。
-
延迟感应察看:调度服务器可以将一些特别慢的机器移除或者列入观察,等延迟恢复之后再重新启用。
大规模信息抽取系统
在大规模的信息抽取系统中,快速返回较好的结果比较慢地返回最好的结果要好。有两种技术解决这种系统中的延迟波动问题:
- 足够好:当从多个服务器请求查询结果时,可以适当放弃那些返回较慢的部分结果;(比如对网页搜索来说,如果广告系统和拼写校正系统无法及时响应,可以直接跳过他们)
- 金丝雀请求:在某些扇出很大的系统中,某些请求可能触及一些未测试的代码,会触发数千台机器同时产生极大的延迟。会耗尽服务器资源或者导致服务器崩溃。在要跑这种代码路径前,可以将这些请求先发送给一两个服务器,当请求成功后再发给全部服务器。
优化修改的延迟
上面提到的技术都是针对读取操作的,对于修改操作来说甚至更加容易解决:
- 对延迟敏感的修改通常非常少;
- 修改操作可以在返回用户结果之后再进行;
- 很多服务都可以容忍不一致的更新模型;
- 要求一致更新的模型通常基于多数算法(比如quorum-base, paxos),本来5个就要提交3个,本身就是可以处理延迟长尾的。
硬件发展趋势和影响
一些新兴的硬件趋势会增加延迟容忍技术的有效性。比如数据中心内部网络更高的等分带宽,以及用来降低但消息传输开始的优化技术如(RDMA),会降低捆绑请求的开销,使得取消消息可以更容易被及时接受到从而避免冗余工作。更低的单消息开销,将会允许产生更多细粒度请求,从而更好多路复用和避免线头阻塞带来的影响。
总结
虽然一些强大的尾部延迟容忍的技术需要占用额外资源。但是他们的开销同样可以控制在合理范围内。通常只利用现有为容错所预留的资源就可以带来延迟方面很大改进。其中很多技术都可以直接封装到基础库和系统中,同时大大简化应用层的设计也可以改进延迟问题。
网友评论