你开发一个服务,调用它,它做一些事情并返回一个结果。那么,需要多长时间?为什么有时花费的时间比用户预期的要长?在本文中,我将从基础知识入手,逐步了解一些标准术语,同时强调一些需要了解的关键点。首先,我们需要一种衡量持续时间的方法,我们需要理解两个完全不同的衡量角度。从调用服务的外部用户的角度来看,我们需要测量响应时间。从服务处理请求的角度来看,我们需要测量服务时间。这就引出了第一个关键点,人们常常对一些指标感到困惑。对于用户来说,就是响应时间(ResponseTime),对于服务来说,就是服务时间(ServiceTime)。在现实世界中,每个过程都包含许多步骤,每个步骤都需要一些时间。一个步骤所花费的时间称为停留时间,它由等待时间和服务时间组成。以用户登录应用为例。当用户登录到移动应用程序时,应用程序将调用网络服务进行用户身份验证。为什么有时会很慢?按理说,每次在手机上生成请求、将请求传输到web服务、查询用户、返回结果和显示下一屏幕的时间应该是相同的。使响应时间发生变化的是排队时间,即等待正在处理其他请求的资源的时间。手机到认证服务器的网络传输需要经过很多跳,每一跳之前都有数据包等待发送。如果队列为空或队列短,则响应快,如果队列长,则响应慢。当请求到达服务器时,也需要排队等待CPU处理。如果需要查询数据库,则需要在另一个队列中排队。排队等候是响应时间增加的主要原因。监控工具会提供一个叫做吞吐量(Throughput)的指标来衡量处理频率。在某些情况下,我们还会得到一个称为到达率的指标,它衡量请求到达服务器的速率。理想情况下,比如工作负载状态稳定的Web服务,一个请求对应一个响应,那么吞吐量和到达率是一样的。然而,重试和错误会导致到达率增加,但不会增加吞吐量。对于快速变化的工作负载或需要很长时间处理的请求(例如批处理作业),到达率和吞吐量之间会出现不平衡,请求模式会更加复杂。吞吐量是指成功处理的请求数,不同于到达率。我们可以通过一些跟踪系统(例如Zipkin或AWSX-Ray)来跟踪单个请求流,但这里我们讨论的是大量请求以及它们之间的交互。我们测量固定时间间隔内的平均值,可以是秒、分钟、小时或天。计算均值需要足够的数据,一般每个均值至少需要20个数据点。如果请求不频繁,请选择至少20次请求的间隔,以便很可能获得有用的信息。如果选择的时间间隔太大,工作负载的变化将被隐藏。例如,对于视频会议系统,大多数会议在一小时的第一分钟左右开始,很容易在这些时间出现峰值,使系统过载,如果时间间隔是每小时一次,信息就会丢失。因此,对于这种情况,将时间间隔设置为秒比较合适。对于快速变化的工作负载,可以使用二级平均。有各种各样的监控工具,但一般很少直接告诉我们等待队列有多长,或者有多少并发量可以处理队列。大多数网络一次只传输一个数据包,但CPU的每个核心或vCPU可以并行处理排队的任务。数据库通常有一个固定的最大连接数来限制并发。对于处理请求的每个步骤,可以记录或估计用于处理请求的并发度。如果系统稳定,具有稳定的平均吞吐量和响应时间,那么很容易估计等待队列的长度,只需将吞吐量乘以停留时间即可。这就是所谓的利特尔定律。该定律非常简单,以至于监控工具经常使用它来估计队列长度,但它仅对具有稳定手段的系统有效。根据Little定律,平均队列长度=平均吞吐量*平均停留时间。为了更好地理解这个定律是如何工作的,我们需要知道请求是如何到达服务器的以及请求之间的间隔是多少。如果我们在请求之间有固定间隔的循环中进行简单的性能测试,那么Little定律将不起作用,因为生成的队列很短并且测试不切实际。我们通常做这种测试,认为它很完美,但是将服务部署到生产环境后,我们看到它运行越来越慢,吞吐量越来越低。这种固定速率的循环测试没有队列,它们只是模拟传送带。在现实的网络世界中,用户是独立的,他们发送自己的请求,不同用户发送请求的间隔是随机的。因此,在测试时,我们需要使用一个生成器来生成具有随机等待时间的请求。大多数系统会使用随机分布,这比模拟传送带要好,但也是错误的。为了模拟真实的网络流量并使Little定律起作用,我们需要使用负指数分布(NegativeExponentialDistribution)。在这篇文章中,NeilCunther博士解释了什么是负指数分布。http://perfdynamics.blogspot.com/2012/05/load-testing-with-uniform-vs.html要生成更真实的队列,您需要使用适当的随机时间算法。但问题是真实的网络流量不是随机分布的,而是突发的。想象一下,当用户打开一个移动应用程序时,它不仅会发送一个请求,还会发送多个请求。在网购抢购活动中,很多用户会同时打开App,会导致流量爆发。这种分布形状称为帕累托或双曲线。此外,当网络重新配置时,流量会延迟,出现排队现象,会给下游系统带来闪电冲击。JimBrady和NeilGunther编写了一些脚本来展示如何配置测试工具以获得更真实的流量。JimBrady还写了一篇关于如何知道好的负载测试是坏的论文。https://github.com/DrQz/web-generator-toolkithttps://arxiv.org/abs/1809.10663与普通测试工具默认生成的流量负载相比,真实世界的流量负载更具爆发性,这将导致更长的等待队列和响应时间。等待队列和响应时间应该是可变的,即使使用率很低,也会有一些缓慢的请求处理。那么当其中一个处理步骤开始变慢时会发生什么?当使用量增加并且某些处理步骤没有足够的可用资源(例如网络传输)时,请求会争夺资源并增加停留时间。一般来说,当使用率达到50%到70%时,网络会逐渐变慢。将网络使用率保持在50%以下以获得更好的延迟。对于并行度高的CPU,在高使用率的情况下,速度会变慢,影响也会变大,这会让你大吃一惊。如果您将最后可用的CPU视为争用点,这在直觉上是有意义的。例如,如果有16个vCPU,最后一个可用的CPU具有6.25%的处理能力,则利用率为93.75%。对于具有100个vCPU的系统,其利用率约为99%。在稳定状态下,公式R=S/(1-U^N)可用于近似随机到达服务器的请求的行为。在多处理器系统上,平均驻留时间膨胀会随着使用量的增加而减少,但强度会增加。利用率使用比率而不是百分比,并将其作为处理器内核数量的幂。平均停留时间可以通过从1中减去使用的N次方并将结果除以平均服务时间来估算。如果使用率低,平均停留时间将非常接近平均服务时间。如果网络的N=1且利用率为70%,则将平均服务时间除以0.3得出的平均停留时间是低利用率时的三倍。通常,我们需要将平均停留时间保持在2到3倍以下,以获得更好的用户响应时间。对于具有16个vCPU和95%利用率的系统,0.95^16=0.44,将平均服务时间除以0.56得到两倍的平均停留时间。如果利用率是98%,那么0.98^16=0.72,再用平均服务时间除以0.28,平均停留时间就变得慢得不能接受,而利用率只增加了3%。多处理器系统的一个问题是,当多处理器系统的利用率很高时,一个小的负载变化可能会产生很大的影响。Unix/Linux系统有一个指标叫做LoadAverage,这个指标通常不太好理解并且存在一些问题。Unix系统(包括Solaris、AIX、HPUX)会记录运行和等待CPU的线程数,Linux也会记录等待IO阻塞的线程数,然后使用三个时间衰减值,分别是1分钟和5分钟和15分钟。我们需要知道的第一件事是,该指标可以追溯到60年代的单核CPU时代,因此我通常将平均负载除以vCPU的数量以获得可比较的值。其次,该指标与其他指标不同,没有使用固定的时间间隔,因此它们不属于同一种平均线。第三,这个指标在Linux上的实现已经成为一个错误,被制度化为一个系统特性,其结果被夸大了。LoadAverage这个指标不测量负载,也不是平均值,所以最好忽略它。如果系统过载,请求到达的速度快于处理速度,利用率达到100%,则上述公式的除数为0,这将导致无限的停留时间。在实践中,这种情况会更糟,因为当系统变慢时,上游用户会发送重试请求,这会增加系统的负载,造成“重试风暴”。这时候系统会排长队,无法响应。当系统使用率达到100%时,会出现很长的等待队列,无法响应请求。我发现系统的重试次数通常配置的很大,超时时间配置的很长,会增加工作量,更容易出现重试风暴。这个问题之前有深入讨论过,后来又写了一篇新文章。更好的方法是使用较短的超时和单次重试,如果其他实例可用,则将请求发送给它们。https://www.slideshare.net/adriancockcroft/evolution-of-microservices-craft-conference/29https://dev.to/aws/if-at-first-you-don-t-get-an-answer-3e85系统的重试时间不能都设置一样,前端部分要设置的长一些,后端部分要设置的短一些。通常,人们通过重新启动队列来清除超载队列,但设计良好的系统会限制队列长度并通过丢弃请求或以“快速失败”响应来减少流量。这由具有固定连接限制的数据库和其他服务使用。当您无法获得请求的可用容量时,您会得到快速失败响应。如果连接限制设置得太低,数据库将拒绝它应该能够处理的请求,如果设置得太高,数据库将在拒绝更多请求之前变慢。多想想当系统使用率达到100%时怎么办,以及如何设置一个合适的连接数限制。在极端情况下保持良好响应速度的最佳方法是使用快速故障响应和流量减少。即使在正常情况下,现实世界中的大多数系统也会经历很多缓慢的响应。但是,我们可以通过适当的指标监控、仔细的系统设计和测试来解决这些问题,以便构建具有最大响应能力的系统。
