超極速優化:網絡開發中的請求合并!
今天,xjjdog來分享網絡開發中的一個超級技巧。它可以把兩個請求合并為一個請求,使得服務在弱網環境中性能得到極大的改善。
說開了很容易,但卻很難想到。
需求
如果我有大量的物聯網設備,比如說100萬臺。如果這些設備平均每10秒產生一個請求,那么QPS就是10W,這對于任何公司來說都是一個不小的規模了。
涉及到交易等有變更的需求,為了實現冪等操作,通常會提前申請一個交易號(或者說token),來進行唯一的交易請求。
這樣,完成一個交易,需要至少發起兩個請求。一個是申請token,一個是拿著token做交易。
雖然說生成token很快,但它是從網絡上傳輸的。且不說現在都是異步模型,就拿網絡延遲來說,就是一個大的問題。它可能硬生生的把服務質量給降了下去,增加了不確定性,也增加了編碼的復雜性。
有什么辦法來加快這個過程嗎?
從HTTP中學習經驗
大多數人都知道,TCP有三次握手和四次揮手的機制。這種冗長的對話雖然保證了連接的可靠性,但卻損失了不少性能。HTTP從一到三各個版本,都是在盡量減少HTTP連接的個數,也在減少交互的次數。
在比較早的HTTP1.0實現中,如果需要從服務端獲取大量資源,會開啟N條TCP短鏈接,并行的獲取信息。但由于TCP的三次握手和四次揮手機制,在連接數量增加的時候,整體的代價就變得比較大
在HTTP/1.1中,通過復用長連接,來改善這個情況,但問題是,由于TCP的消息確認機制和順序機制以及流量控制策略的原因,資源獲取必須要排隊使用。一個請求,需要等待另外一個請求傳輸完畢,才能開始
HTTP/2采用多路復用,多個資源可以共用一個連接。但它解決的只是應用層的復用,在TCP的傳輸上依然是阻塞的,后面的資源需要等待前面的傳輸完畢才能繼續。這就是隊頭阻塞現象(Head-of-line blocking)
QUIC,也就是HTTP3,抽象出了一個stream(流)的概念,多個流,可以復用一條連接,那么滑動窗口這些概念就不用作用在連接上了,而是作用在stream上。由于UDP只管發送不管成功與否的特性,這些數據包的傳輸就能夠并發執行。協議的server端,會解析并緩存這些數據包,進行組裝和整理等。由于抽象出了stream的概念,就使得某個數據包傳輸失敗,只會影響單個stream的準確性,而不是整個連接的準確性。
請求黏貼
其實,我們參考TCP的三次握手就可以了。TCP的握手和揮手流程都差不多,但為什么握手是三次,但揮手是四次呢?
原因就是TCP把SYN和ACK兩個報文,合并成一個返回了。

我們可以把token看作是序列號,然后把它黏貼在正常的請求里返回就可以了。
比如,原來的請求是。
一、獲取token
request: /getToken
response:
{
"token": "12345"
}
二、提交請求
request: /postOrder
{
"token": "12345",
"other": {}
}
response:
{
"status": 200
}
合并后的請求是。
request: /postOrder
{
"token": "12345",
"other": {}
}
response:
{
"status": 200,
"token": "12346"
}
只需要在每次請求返回的時候,不論成功還是失敗,都附加一個新的token到客戶端。客戶端緩存這個token,然后發起下個請求。
通過這個方法,就可以把兩個請求合并為1個請求,完成我們的優化目標。
End
在網絡編程中,減少網絡交互是一個非常重要的優化,尤其是在弱網環境中。雖然這個技巧很簡單,但它很難被想到。優化效果也是巨大的,畢竟減少了一次網絡交互。
它有一個響亮的名字,那就是三連環。意味著前后請求的銜接,永不斷環。
作者簡介:小姐姐味道 (xjjdog),一個不允許程序員走彎路的公眾號。聚焦基礎架構和Linux。十年架構,日百億流量,與你探討高并發世界,給你不一樣的味道。




























