常听到有人说异步计算比同步计算性能要好,把前后台系统的交互方式做成异步,可以减少阻塞,从而缩短系统整体的响应时间。
听起来很有道理,但这个说法有点跳跃,让人不免疑惑。比如说,谁的阻塞减少了?虽然少了阻塞时间,但服务器执行一个请求所需的时间还是要那么多,响应时间怎么被缩短了?
我在网上搜了搜,没有找到答案,只好自己来解答。答题时我画了些图来推演上述理论,并最终说服了自己;下面就来分享这些图,但愿你看完后,能有所收获。
如果你只想知道结论,请直接跳到第2章。
1. 图示异步计算与响应时间的关系
1.1. 虚拟场景及概念定义
假设系统的应用由前端和后端两部分组成,前端模块接收用户请求,转发给后端模块;后端模块处理请求,把结果返回给前端;前端再把结果转发给用户。
不太严谨地说,如果前后端操作在同一个线程里进行,则称同步计算;如果前后端操作分别在两个线程里或两个进程里进行,两者通过消息队列或其它方法通信,则称异步计算。
解题的思路是:
1. 同步计算时,求出一个用户请求所需的响应时间
2. 异步计算时,求出一个用户请求所需的响应时间
3. 然后进行比较
各项量值的假设:
1. 为了简化问题,我们假设前后端操作的最大并发数都是1。
2. 对于一个请求,前端转发请求需100ms, 后端处理需200ms, 前端转发结果需100ms.
1.2. 并发数=1时的平均响应时间
1.2.1. 同步计算
以时间轴为基础,看看唯一的用户请求是如何被一步一步处理的:
响应时间 = 400ms
1.2.2. 异步计算
响应时间 = 400ms
1.2.3. 比较
和你猜的一样,两种计算模式下的响应时间相同,都是400ms;。原因很简单,当只有一个请求时,这个请求可以立即占用它想要的资源;不管是同步还是异步,都不存在阻塞等待的问题;所以同步异步的性能表现是相同的。
1.3. 并发数=2时的平均响应时间
并发数 = 2,即同时有两个请求。我们分别用“请求1”和“请求2”来指代它们,并假设“请求1”先到达系统。
1.3.1. 同步计算
同步计算时,请求1在执行结束前不会释放任何资源;请求2必须等请求1结束才能开始执行。
1.3.2. 异步计算
同步计算时,请求1进入后端处理后可以释放前端资源,这时请求2 可以占用它。
1.3.3. 比较
请求1的响应时间(ms) |
请求2的响应时间(ms) |
平均响应时间(ms) |
|
同步计算 |
400 |
800 |
600 |
异步计算 |
400 |
600 |
500 |
可以看出,请求1的响应时间在同异步时相同; 请求2在异步时则比同步时少用了200ms,然后将平均响应时间拉低了100ms. 那请求2缩短的200ms是哪里来的呢? 看下图,它来自等待时间的减少:
分析:
1. 同步模式下,请求2须等待请求1全部执行完毕才能开始执行,因为请求1在全部执行完毕前不会释放前端资源
2. 而在异步模式下,请求2可在请求1完成前端转发请求后就立即使用前端,并在请求1完成后端处理后立即使用后端;跟同步模式相步,少等了一些时间。基本上,这就是异步计算性能好的奥秘!
1.4. 补充说明:最大并发数>1
如果系统的最大并发数>1,响应时间会是个什么样子?
采用上述分析方法,可以得到相同的比较结果。限于篇幅,这里就不给出具体的图表和数据了。
OK, 到这里可以给出总体结论了。
2. 总体结论
1. 如果实际并发请求数 <= 系统最大并发数,则异步计算时的平均响应时间与同步计算时的平均响应时间相同;异步计算并不能缩短服务器处理请求的时间,它只能减少部分请求的等待时间,从而减少平均响应时间(见下一条)
2. 若并发请求数 > 系统最大并发数,则异步计算的平均响应时间会少于同步计算时的平均响应时间,因为异步计算时,有些请求的等待时间比同步计算时的等待时间时要短。
a) 同步计算时,一个请求总是要等到全部完成后才一次性释放所有资源,导致下一个请求的长久等待
b) 异步计算时,一个请求可以在完成每个步骤后就立即释放这个步骤里所占用的资源,下一个请求可以见缝插针,接盘资源;因此等待时间要短一些
3. 本文开头提到的“阻塞减少”意思是有些请求不必等那么久就可以执行; 而且“减少响应时间”确切来说,是指多个请求的响应时间的“平均值”减少
4. 综上可推论得知,若系统的并发数较低,异步计算对性能没什么作用
5. 另外可以看出,在同步计算时各请求的串行度较高,异步计算时各请求的并行度较高;
3. 进阶研究
1. 如果我没弄错的话,这种问题应该属于“排队论”的研究领域;你可以找本“排队论”的书看看,应该能够找到严格的数学论证过程和具体的吞吐量公式。
2. 以上的推导过程中使用了一个假设:消息队列的并发数总是超过用户并发数,且消息队列本身的响应时间可以忽略不计。实践中这个假设可能并不成立,性能规划时须小心对待。