高并发应用中客户端等待、响应时间的推算,及RT/QPS概念辨析

高并发应用中客户端等待、响应时间的推算,及RT/QPS概念辨析

对于一个网站,已知服务端的服务线程数和处理单个请求所需的时间时,该如何算出高并发时用户从点击链接到收到响应的时间?注意这个时间并不等于服务端处理单个请求的时间,因为高并发时,很多用户请求需要排队等待,你要把这个额外的等待时间算进去。

这个问题很重要,因为它的结果直接影响你的网站的用户体验。这篇文章就是来帮你算这个时间的。你可以使用本文附带的程序来算,也可以通过本文提炼出的公式来算

另外还有一个问题:所谓RT(响应时间)和QPS,究竟要不要考虑用户请求的排队等待时间? 本文认为,对于RT我们应该区分出“服务器眼中的RT”和“客户端眼中的RT”,但对于QPS却不必区分

后文会辨析这些概念,同时还会给出不同场景下,RT和QPS随着并发数变化而变化的曲线图。你可以通过比较不同的曲线,获得一些收获。

下面先推演一个最简单的场景,通过这个场景,你可以了解到本文在计算时使用的基本思路。

最简单的场景:服务端线程数=1,客户端并发=2

如果服务端只有1个服务线程,每处理1个请求需要1个ms;  当客户端在同一瞬间提交两个请求时,各项性能指标是怎样的?

为了解这个题,可以想象一下每个请求被处理的过程:

请求1被立即处理;由于服务线程只有一个,请求2需要等待请求1完结后才能被处理。

据此算出各项性能指标:


性能指标


指标值


说明


服务端响应所有请求的时间


1ms + 1ms = 2ms


上图中紫色时间之和


服务端响应每个请求的平均时间


服务端处理所有请求的时间/2 = 1ms


客户端所有请求的等待、响应时间


1ms + (1ms + 1ms) = 3ms


第1个请求直接处理 = 1ms

第2个请求先等待后处理 = 1ms + 1ms


客户端每个请求的平均等待、响应时间


客户端所有请求的等待、响应时间/2 = 1.5 ms

看上面高亮的这些字,你应该已经了解了时间推算的基本思路了;后文其他场景中会使用相同的模式来推算。

再来观察一下客户端每个请求的平均等待、响应时间。它的值是1.5ms,大于服务端响应每个请求的平均时间1ms.

不同角度有不同的看法,

  1. 服务端站在自己的角度来看:并发为2时,处理每个请求消耗了自己1ms, RT = 1ms
  2. 客户端站在自己的角度来看:并发为2时,每个请求平均需要花1.5ms才能被完结,服务器的RT应用1.5ms ,比服务端宣称的1ms要高多了。

这两种说法都有道理。就像银行柜员一直在忙,没有偷懒,问心无愧;而排队的客户苦苦等待,牢骚漫天,其实也很无辜。

因此本文认为,RT应分为两种


性能指标


指标值


说明


服务端眼中的RT


1ms


不计入客户端的等待时间


客户端眼中的RT


1.5ms


计入客户端的等待时间

顺便再算一下此时的QPS. 事实上无论在服务端眼里还是客户端眼里,QPS都是:
 成功处理的请求数/(最后一个请求结束时间 – 第一个请求开始时间)= 2 / (2 – 0)ms = 1000

接下来再看看其它场景。

场景2:服务端线程数 > 1

上文说了服务端线程数=1的情况,如果服务端线程数超过1会怎么样? 比如,服务端线程数 = 3, 客户端并发 = 10:


性能指标


指标值


服务端响应所有请求的时间


1ms * 3 + 1ms * 3 + 1ms * 3 + 1ms = 10ms


服务端响应每个请求的平均时间


服务端处理所有请求的时间/10 = 1ms


客户端所有请求的等待、响应时间


1ms * 3 + 2ms * 3 + 3ms * 3 + 4ms = 22ms


客户端每个请求的平均等待、响应时间


客户端所有请求的等待、响应时间/10 = 2.2 ms

这个推算并不难,但如果总是需要这样手工推算就很烦了。因此,本文提供了一个程序来将推算步骤自动化。不过,在体验这个程序之前,我们先推演一下其他的场景。

场景3:无过载保护,服务器响应能力受并发数影响

以上的场景都假设系统有严格的“过载保护”:只提供有限的服务线程数,由于系统压力不会过载,所以每个线程都总是能以最高的速度来处理请求。

现实中,很多系统并没有这种做,相反:

  1. 没有过载保护,来一个请求启一个线程,直到系统崩溃
  2. 当客户端并发数超过一定阈值后,服务端处理单个请求的时间开始上升(原因很多,比如CPU、内存资源消耗过大,线程上下文切换过多等)

举个例子,一个应用在并发<=3时,处理单个请求只需1ms;当并发超过3时,处理单个请求的能力开始恶化,

3个并发 => 处理单个请求需1ms

4个并发 => 处理单个请求需要4ms

5个并发 => 处理单个请求需要5ms

10个并发 => 处理单个请求需要10ms

如果客户端并发=10,性能会是什么状况?


性能指标


指标值


服务端响应所有请求的时间


10 * 10ms = 100ms


服务端响应每个请求的平均时间


服务端处理所有请求的时间/10 = 10ms


客户端所有请求的等待、响应时间


10 * 10ms = 100ms


客户端每个请求的平均等待、响应时间


客户端所有请求的等待、响应时间/10 = 10 ms

没有过载保护时,所有请求都不用排队;这时推算性能的算法会比较简单。接下来再看最后一种场景。

场景4:弱过载保护

有很多应用,在过载保护方面是上面两种机制的折衷:

  1. 在客户端并发数低于某阈值时,单个请求的处理速度可以保持全速
  2. 当客户端并发数高于上述阈值但还不太高时,单个请求的处理速度有所下降,但仍可以接受,系统这时愿意容忍这种程度的并发,以换取更高的吞吐量(QPS)
  3. 当客户端并发数过大、高于另一个阈值时,单个请求的处理速度会迅速恶化,应该在这个点进行过载保护

本文把这种策略称作“弱过载保护”,场景2称作“强过载保护”,场景3则是“无过载保护”

弱过载保护系统的性能该如何推算?

  1. 并发数低于第一个阈值时,所有请求不必等待。此时的计算方法跟“无过载保护”基本相同,只不过这时单个请求的处理时间是个常量。
  2. 并发数高于第一个阈值但低于第二个阈值时,所有请求仍不必等待,此时的计算方法跟“无过载保护”完全一致。
  3. 并发数高于第二个阈值时,有些请求开始等待,此时的计算方法跟“强过载保护”场景下的模型基本一致,只不过这时单个请求的处理时间 = 最高并发时的单个请求的处理时间。

另外,“强过载保护”和“无过载保护”可以视为“弱过载保护”的特殊情况。

  1. “强过载保护”也是一种“弱过载保护”,它的第二个阈值 = 第一个阈值
  2. “无过载保护”也是一种“弱过载保护”,它的二级阈值 = ∞

既然所有的场景都可以统一为同一种机制,我们可以搞出一个“统一的性能 - 并发数计算模型”。

统一的性能 - 并发数计算模型

输入:

  


符号


公式中的符号


服务器端处理能力


第1个线程数阈值


serverThreshold1

 

第1个线程数阈值前的单个请求处理时间


serverResponse1

 

第2个线程数阈值


serverThreshold2

 

两个阈值之间的单个请求处理时间函数


serverResponse2Func (clientConcurrency)


s


客户端压力


客户端并发数


clientConcurrency

 

输出:


符号


公式中的符号


备注


服务器眼中的性能


服务器响应所有请求的时间


serverResponseSum

 

服务器响应每个请求的平均时间


serverResponseAverage

 
serverResponseSum/clientConsurrency


客户端眼中的性能


客户端所有请求的等待、响应时间


clientWaitResponseSum

 

客户端每个请求的平均等待、响应时间


clientWaitResponseAverage

 
clientWaitResponseSum/clientConsurrency

算法:

参照下图,推算出每个请求的处理时间和响应时间,然后分别求和、求平均.

通过程序自动化计算

按照上图的模式来手工推演性能会很烦的。本文提供了一个开源程序,以将推算过程自动化。

如果急于看效果,可以

  1. 下载http://how-long-to-wait.googlecode.com/files/how-long-to-wait-1.0.0-SNAPSHOT-jar-with-dependencies.jar
  2. 执行 java -jar how-long-to-wait-1.0.0-SNAPSHOT-jar-with-dependencies.jar  这个DEMO算出了serverThreshold =3, 并发=10的各项性能指标

想自定义参数,则可以:

  1. 下载http://how-long-to-wait.googlecode.com/files/how-long-to-wait-1.0.0-SNAPSHOT-project.zip
  2. 解压,作为Maven工程放到你的IDE里
  3. 模仿org.googlecode.hltwsample.single.ShowCase写你自己的代码
  4. 执行程序,并查看控制台输出

这个程序还会记录每个请求被执行的起止时间,你可以利用它来验算

默认情况下执行时间会被直接打印到控制台,但你也可以扩展实现自己的记录展现工具。具体方法是,

  1. 实现SingleAppSimulateEventListener接口
  2. 再把它注入到singleAppVisitSimulator.setEventListener()

通过公式计算

你也可以直接通过公式来计算性能。

服务端眼中的RT

客户端眼中的RT

若 ,

若 ,

则令, (整除),  且(取模)

性能-并发数曲线图

前面一直在做定量分析,现在可以搞一下定性分析了:从数据中找到可以辅助决策的结论。

我们可以让并发数持续升高,同时观察服务器/客户端眼中性能的差异,以及同一性能指标在不同过载保护机制下的差异。

服务端处理能力:

 


第1个线程数阈值


10


第1个线程数阈值前的单个请求处理时间


10ms


第2个线程数阈值


20


单个请求处理时间函数

 

无过载保护时的性能趋势图

  1. 并发数超过一个极限之前,RT基本不变,但QPS会持续上升,表明系统的并发服务能力被利用得越来越充分
  2. 无过载保护时,当并发数超过一个极限后,系统性能会急剧下降
  3. 无过载保护时,所有请求会被服务端立即处理,没有等待时间,因此客户端和服务器的RT曲线完全吻合。

强过载保护时的性能趋势图

  1. 强过载保护时,服务器跟中的永远是条水平线,因为服务器一直处于最佳状态
  2. 强过载保护时,客户端眼中的系统性能在并发数经过一个阈值后逐渐恶化,实际上这只是排队时间在增多而已。
  3. 强过载保护时,QPS的变化比较平稳,因为服务器一直处于健康状态

弱过载保护时的性能趋势图

  1. 弱过载保护时,RT会先后经历两次较为平缓的性能下降。第一次是由于服务器本身性能下降,第二次则是因为客户端的排队时间增多。
  2. 弱过载保护时,QPS的变化仍算比较平稳

不同过载保护机制下的性能比较

放大下面两条曲线:

在服务器眼里,保护越强,则性能越好

放大下面两条曲线:

  1. 在客户端眼里,有过载保护比没有好
  2. 在上图中,弱过载保护时的性能比强过载保护时的RT要平一些,但这是因为本图里的函数本身比较平坦,使得系统能够在较高并发时仍以较好的速度处理请求,而且总体性能更好。 如果函数很陡的话,情况就会是另外一种样子了。所以,强过载保护好还是弱过载保护好,要具体案例具体分析。
  1. 无过载保护时,QPS超过一个极限后降到极低的水平
  2. 上图中,弱过载保护的QPS比强过载保护时要好一些,但这也跟函数本身的平坦度有关。

结语

本文给出的程序和公式可以帮助你在已知服务端性能的条件下,计算出客户端的平均等待、相应时间。

本文区别了服务器眼中的RT和客户端眼中的RT,并且通过曲线图介绍了不同过载保护机制下性能如何随并发数变化而变化。

不过,本文的推算剧本中,客户端只会发一次请求:若并发为10,则一次性提交10个请求。而现实中的客户端应该会持续不断的提交请求,这时候它们的平均等待、响应时间又是多少呢?  这个问题要结合客户端本身发送请求的速率来计算,我们以后再深入地看看。

时间: 2024-12-04 17:30:28

高并发应用中客户端等待、响应时间的推算,及RT/QPS概念辨析的相关文章

高并发分布式系统中生成全局唯一Id汇总

高并发分布式系统中生成全局唯一Id汇总 (转自:http://www.cnblogs.com/baiwa/p/5318432.html) 数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求:   1 不能有单点故障.   2 以时间为序,或者ID里包含时间.这样一是可以少一个索引,二是冷热数据容易分离.   3 可以控制ShardingId.比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易.   

ql Server 高频,高并发访问中的键查找死锁解析

死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库设计的潜在问题,一些不易捕捉的死锁可能出现从而影响业务.这里为大家介绍由于设计问题引起的键查找死锁及相关的解决办法. 这里我们在测试的同时开启trace profiler跟踪死锁视图(locks:deadlock graph).(当然也可以开启跟踪标记,或者应用扩展事件(xevents)等捕捉死锁)

Sql Server 高频,高并发访问中的键查找死锁解析

死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库设计的潜在问题,一些不易捕捉的死锁可能出现从而影响业务.这里为大家介绍由于设计问题引起的键查找死锁及相关的解决办法. 这里我们在测试的同时开启trace profiler跟踪死锁视图(locks:deadlock graph).(当然也可以开启跟踪标记,或者应用扩展事件(xevents)等捕捉死锁)

如何在高并发分布式系统中生成全局唯一Id(转)

http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了个MSSQL参数化语法生成器,会在9月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也

高并发系统中的常见问题

本文一共分析了三个案例,分别介绍并发系统中的共享资源并发访问.计算型密集型任务缓存访问 .单一热点资源峰值流量问题和解决方案. Q1:订票系统,某车次只有一张火车票,假定有1w个人同时打开12306网站来订票,如何解决并发问题? A1: 首先介绍数据库层面的并发访问,解决的办法主要是乐观锁和悲观锁. 乐观锁 假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性. 乐观锁使用一个自增的字段表示数据的版本号(或者timestamp),更新的时候检查版本号是否一致,比如数据库中版本号为4,更新时

[转载] 高并发系统中的常见问题

原文: http://weibo.com/p/1001603862417250608209#_loginLayer_1436489552567 本文一共分析了三个案例,分别介绍并发系统中的共享资源并发访问.计算型密集型任务缓存访问 .单一热点资源峰值流量问题和解决方案. Q1:订票系统,某车次只有一张火车票,假定有1w个人同时打开12306网站来订票,如何解决并发问题? A1: 首先介绍数据库层面的并发访问,解决的办法主要是乐观锁和悲观锁. 乐观锁 假设不会发生并发冲突,只在提交操作时检查是否违

高并发请求中的读写锁

在数据库中使用读写锁 数据库中使用读写锁,这样能更好地读取某一类统计数据,但一般读取不应该加锁,但修改操作却要慎重 事务的特性 1. 原子性(atomic),事务必须是原子工作单元:对于其数据修改,要么全都执行,要么全都不执行 2. 一致性(consistent),事务在完成时,必须使所有的数据都保持一致状态. 3. 隔离性(insulation),由并发事务所作的修改必须与任何其它并发事务所作的修改隔离. 4. 持久性(Duration),事务完成之后,它对于系统的影响是永久性的. 在j2ee

如何在高并发分布式系统中生成全局唯一Id

我了解的方案如下-------------------------- 1.  使用数据库自增Id 优势:编码简单,无需考虑记录唯一标识的问题. 缺陷: 1)         在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询.删除时就会有异常. 2)         在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题. 3)         在业务上操作父.子表(即关联表)插入时,需要

在一个高并发系统中 如果突然出现一个应用或者说一个服务突然变得很慢,应该怎么排查?

声明:该总结为网友朋友总结,本人是归纳成文,方便各网友学习交流. 这个是考线上排查问题能力,没有标准答案,作为开发,假设这种情景出现你怎么诊断问题? 首先:想知道,在实际情况下,怎么知道[一个应用或者说一个服务突然变得很慢]?调用访问的时候会发现的,对于业务流程比较熟悉很重要,先能够初步圈出,可能出现问题的地方,服务监控是必须的,做业务必须要知道自己服务的状态. 1.首先就是想看日志,后来想想看日志确实不太可行,并发量太大的情况下,查日志会很慢,(看日志,pstack strace gdb).