常聽到有人說異步計算比同步計算性能要好,把前后台系統的交互方式做成異步,可以減少阻塞,從而縮短系統整體的響應時間。
聽起來很有道理,但這個說法有點跳躍,讓人不免疑惑。比如說,誰的阻塞減少了?雖然少了阻塞時間,但服務器執行一個請求所需的時間還是要那么多,響應時間怎么被縮短了?
我在網上搜了搜,沒有找到答案,只好自己來解答。答題時我畫了些圖來推演上述理論,並最終說服了自己;下面就來分享這些圖,但願你看完后,能有所收獲。
如果你只想知道結論,請直接跳到第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. 以上的推導過程中使用了一個假設:消息隊列的並發數總是超過用戶並發數,且消息隊列本身的響應時間可以忽略不計。實踐中這個假設可能並不成立,性能規划時須小心對待。