實(shí)戰(zhàn)?|?QUIC?協(xié)議在螞蟻集團(tuán)落地
自 2015 年以來(lái),QUIC 協(xié)議開始在 IETF 進(jìn)行標(biāo)準(zhǔn)化并被國(guó)內(nèi)外各大廠商相繼落地。鑒于 QUIC 具備“0RTT 建聯(lián)”、“支持連接遷移”等諸多優(yōu)勢(shì),并將成為下一代互聯(lián)網(wǎng)協(xié)議:HTTP3.0 的底層傳輸協(xié)議,螞蟻集團(tuán)支付寶客戶端團(tuán)隊(duì)與接入網(wǎng)關(guān)團(tuán)隊(duì)于 2018 年下半年開始在移動(dòng)支付、海外加速等場(chǎng)景落地 QUIC。
-
QUIC背景:簡(jiǎn)單全面的介紹下 QUIC 相關(guān)的背景知識(shí)
-
方案選型設(shè)計(jì):詳細(xì)介紹螞蟻的落地方案如何另辟蹊徑、優(yōu)雅的支撐 QUIC 的諸多特性,包括連接遷移等
-
落地場(chǎng)景:介紹 QUIC 在螞蟻的兩個(gè)落地場(chǎng)景,包括:支付寶客戶端鏈路以及海外加速鏈路
-
幾項(xiàng)關(guān)鍵技術(shù):介紹落地 QUIC 過(guò)程中核心需要解決的問(wèn)題,以及我們使用的方案,包括:“支持連接遷移”、“提升 0RTT 比例", "支持 UDP 無(wú)損升級(jí)”以及“客戶端智能選路” 等
-
幾項(xiàng)關(guān)鍵的技術(shù)專利
QUIC 背景介紹
https://datatracker.ietf.org/wg/quic/documents/
一、QUIC 是什么?
簡(jiǎn)單來(lái)說(shuō),QUIC (Quick UDP Internet Connections) 是一種基于 UDP 封裝的安全 可靠傳輸協(xié)議,他的目標(biāo)是取代 TCP 并自包含 TLS 成為標(biāo)準(zhǔn)的安全傳輸協(xié)議。下圖是 QUIC 在協(xié)議棧中的位置,基于 QUIC 承載的 HTTP 協(xié)議進(jìn)一步被標(biāo)準(zhǔn)化為 HTTP3.0。
二、為什么是 QUIC ?
在 QUIC 出現(xiàn)之前,TCP 承載了 90% 多的互聯(lián)網(wǎng)流量,似乎也沒(méi)什么問(wèn)題,那又為何會(huì)出現(xiàn)革命者 QUIC 呢?這主要是因?yàn)榘l(fā)展了幾十年的 TCP 面臨 “協(xié)議僵化問(wèn)題”,表現(xiàn)在幾方面:
- 網(wǎng)絡(luò)設(shè)備支持 TCP 時(shí)的僵化,表現(xiàn)在:對(duì)于一些防火墻或者 NAT 等設(shè)備,如果 TCP 引入了新的特性,比如增加了某些 TCP OPTION 等,可能會(huì)被認(rèn)為是攻擊而丟包,導(dǎo)致新特性在老的網(wǎng)絡(luò)設(shè)備上無(wú)法工作。
- 網(wǎng)絡(luò)操作系統(tǒng)升級(jí)困難導(dǎo)致的 TCP 僵化,一些 TCP 的特性無(wú)法快速的被演進(jìn)。
-
除此之外,當(dāng)應(yīng)用層協(xié)議優(yōu)化到 TLS1.3、 HTTP2.0 后, 傳輸層的優(yōu)化也提上了議程,QUIC 在 TCP 基礎(chǔ)上,取其精華去其糟粕具有如下的硬核優(yōu)勢(shì):
三、QUIC 生態(tài)圈發(fā)展簡(jiǎn)史
下圖是 QUIC 從創(chuàng)建到現(xiàn)在為止的一些比較重要的時(shí)間節(jié)點(diǎn),2021 年,QUIC V1 即將成為 RFC,結(jié)束百花齊放的態(tài)勢(shì)。
- QUIC LB 組件:基于 NGINX 4層 UDP Stream 模塊開發(fā),用來(lái)基于 QUIC DCID 中攜帶的服務(wù)端信息進(jìn)行路由,以支持連接遷移。
-
NGINX QUIC 服務(wù)器:開發(fā)了 NGINX_QUIC_MODULE,每個(gè) Worker 監(jiān)聽(tīng)兩種類型的端口:(1)BASE PORT ,每個(gè) Worker 使用的相同的端口號(hào),以 Reuseport 的形式監(jiān)聽(tīng),并暴露給 QUIC LB,用以接收客戶端過(guò)來(lái)的第一個(gè) RTT 中的數(shù)據(jù)包,這類包的特點(diǎn)是 DCID 由客戶端生成,沒(méi)有路由信息。(2)Working PORT,每個(gè) Worker 使用的不同的端口號(hào),是真正的工作端口,用以接收第一個(gè) RTT 之后的 QUIC 包,這類包的特定是 DCID 由服務(wù)端的進(jìn)程生成攜帶有服務(wù)端的信息。
- 在不用修改內(nèi)核的情況下,完全在用戶態(tài)支持 QUIC 的連接遷移,以及連接遷移時(shí) CID 的 Update
- 在不用修改內(nèi)核的情況下,完全在用戶態(tài)支持 QUIC 的無(wú)損升級(jí)以及其他運(yùn)維問(wèn)題
- 支持真正意義上的 0RTT ,并可提升 0RTT 的比例
兩個(gè)落地場(chǎng)景
- 支持的 QUIC 版本是 gQUIC Q46。
- NGINX QUIC MODULE 支持 QUIC 的接入和 PROXY 成 TCP 的能力。
- 支持包括移動(dòng)支付、基金、螞蟻森林在內(nèi)的所有的 RPC 請(qǐng)求。
- 當(dāng)前選擇 QUIC 鏈路的方式有兩種 :
- Backup 模式,即在 TCP 鏈路無(wú)法使用的情況下,降級(jí)到 QUIC 鏈路。
- Smart 模式,即 TCP和 QUIC 競(jìng)速,在 TCP 表現(xiàn)力弱于 QUIC 的情況下,下次請(qǐng)求主動(dòng)使用 QUIC 鏈路。
- 在客戶端連接發(fā)生遷移的時(shí)候,可以不斷鏈繼續(xù)服務(wù)
- 客戶端在首次發(fā)起連接時(shí),可以節(jié)省 TCP 三次握手的時(shí)間
-
對(duì)于弱網(wǎng)情況,QUIC 的傳輸控制可以帶來(lái)傳輸性能提升
- 通過(guò) QUIC 長(zhǎng)連接的上的 Stream 承載 TCP 請(qǐng)求,避免每次的跨海建聯(lián)。
- 對(duì)于跨海的網(wǎng)絡(luò),QUIC 的傳輸控制可以帶來(lái)傳輸性能提升。
專利一
我們將落地場(chǎng)景 2 中,通過(guò) QUIC Stream 進(jìn)行四層代理的手段來(lái)進(jìn)行海外回源的加速方法進(jìn)行專利保護(hù),提出:“一種基于 QUIC 協(xié)議代理的的鏈路加速方法”,目前此專利已經(jīng)獲得美國(guó)專利授權(quán),專利號(hào):CN110213241A。
專利二
將我們落地框架中的 QUIC LB 組件作為專利進(jìn)行保護(hù),提出:“一種無(wú)狀態(tài)、一致性、分布式的 QUIC 負(fù)載均衡設(shè)備”,目前此專利還在受理中。由于通過(guò) QUIC LB 可以很好的支持 QUIC 協(xié)議的連接遷移問(wèn)題,所以目前 IETF QUIC WG 上有關(guān)于 QUIC LB 相關(guān)的草案,我們目前已經(jīng)參與到 Draft 的討論和制定中,后序相關(guān)的方案也會(huì)繼續(xù)推廣到云上產(chǎn)品。
專利三
將我們解決的 UDP 的無(wú)損升級(jí)方法進(jìn)行專利保護(hù),提出 “一種 QUIC 服務(wù)器無(wú)損升級(jí)方案”,目前此專利還在受理中。由于 UDP 無(wú)損升級(jí)問(wèn)題是一個(gè)業(yè)界難題,當(dāng)前有些手段需要在用戶態(tài)進(jìn)行跳轉(zhuǎn),性能損失較大,我們的方案可以在我們的落地框架中解決當(dāng)前問(wèn)題,關(guān)于這個(gè)方案的細(xì)節(jié)我們?cè)俸竺娴年P(guān)鍵技術(shù)中進(jìn)行介紹。
四項(xiàng)關(guān)鍵技術(shù)
技術(shù)點(diǎn)1.優(yōu)雅的支持連接遷移能力
先說(shuō) 連接遷移面臨的問(wèn)題 ,上文有提到,QUIC 有一項(xiàng)比較重要的功能是支持連接遷移。這里的連接遷移是指:如果客戶端在長(zhǎng)連接保持的情況下切換網(wǎng)絡(luò),比如從 4G 切換到 Wifi , 或者因?yàn)?NAT Rebinding 導(dǎo)致五元組發(fā)生變化,QUIC 依然可以在新的五元組上繼續(xù)進(jìn)行連接狀態(tài)。
QUIC 之所以能支持連接遷移,一個(gè)原因是 QUIC 底層是基于無(wú)連接的 UDP,另一個(gè)重要原因是因?yàn)?QUIC 使用唯一的 CID 來(lái)標(biāo)識(shí)一個(gè)連接,而不是五元組。
如下圖所示,是 QUIC 支持連接的一個(gè)示意圖,當(dāng)客戶端出口地址從 A 切換成 B 的時(shí)候,因?yàn)?CID 保持不變,所以在 QUIC 服務(wù)器上,依然可以查詢到對(duì)應(yīng)的 Session 狀態(tài)。
再說(shuō) 我們的解決方法,為了解決此問(wèn)題,我們?cè)O(shè)計(jì)了開篇介紹的落地框架,這里我們將方案做一些簡(jiǎn)化和抽象,整體思路如下圖所示:
- 在四層負(fù)載均衡上,我們?cè)O(shè)計(jì)了 QUIC LoadBalancer 的機(jī)制:
- 我們?cè)?QUIC 的 CID 中擴(kuò)展了一些字段(ServerInfo)用來(lái)關(guān)聯(lián) QUIC Server 的 IP 和 Working Port 信息。
- 在發(fā)生連接遷移的時(shí)候,QUIC LoadBalancer 可以依賴 CID 中的 ServerInfo 進(jìn)行路由,避免依賴五元組關(guān)聯(lián) Session 導(dǎo)致的問(wèn)題。
-
在 CID 需要 Update 的時(shí)候,NewCID 中的 ServerInfo 保留不變,這樣就避免在 CID 發(fā)生 Update 時(shí),僅依賴 CID Hash 挑選后端導(dǎo)致的尋址不一致問(wèn)題。
- 在 QUIC 服務(wù)器多進(jìn)程工作模式上,我們突破了 NGINX 固有的多 Worker 監(jiān)聽(tīng)在相同端口上的桎梏,設(shè)計(jì)了多端口監(jiān)聽(tīng)的機(jī)制,每個(gè) Worker 在工作端口上進(jìn)行隔離,并將端口的信息攜帶在對(duì) First Initial Packet 的回包的 CID 中,這樣代理的好處是:
- 無(wú)論是否連接遷移,QUIC LB 都可以根據(jù) ServerInfo,將報(bào)文轉(zhuǎn)發(fā)到正確的進(jìn)程。
- 而業(yè)界普遍的方案是修改內(nèi)核,將 Reuse port 機(jī)制改為 Reuse CID 機(jī)制,即內(nèi)核根據(jù) CID 挑選進(jìn)程。即便后面可以通過(guò) ebpf 等手段支持,但我們認(rèn)為這種修改內(nèi)核的機(jī)制對(duì)底層過(guò)于依賴,不利于方案的大規(guī)模部署和運(yùn)維,尤其在公有云上。
-
使用獨(dú)立端口,也有利于多進(jìn)程模式下,UDP 無(wú)損升級(jí)問(wèn)題的解決,這個(gè)我們?cè)诩夹g(shù)點(diǎn) 3 中介紹。
這里先 介紹 QUIC 0RTT 原理。前文我們介紹過(guò), QUIC 支持傳輸層握手和安全加密層握手都在一個(gè) 0RTT 內(nèi)完成。TLS1.3 本身就支持加密層握手的 0RTT,所以不足為奇。而 QUIC 如何實(shí)現(xiàn)傳輸層握手支持 0RTT 呢?
我們先看下傳輸層握手的目的,即:服務(wù)端校驗(yàn)客戶端是真正想握手的客戶端,地址不存在欺騙,從而避免偽造源地址攻擊。在 TCP 中,服務(wù)端依賴三次握手的最后一個(gè) ACK 來(lái)校驗(yàn)客戶端是真正的客戶端,即只有真正的客戶端才會(huì)收到 Sever 的 syn_ack 并回復(fù)。
- 類似于 Session Ticket 原理,Server 會(huì)將客戶端的地址和當(dāng)前的 Timestamp 通過(guò)自己的 KEY 加密生成 STK。
- Client 下次握手的時(shí)候,將 STK 攜帶過(guò)來(lái),由于 STK 無(wú)法篡改,所以 Server 通過(guò)自己的 KEY 解密,如果解出來(lái)的地址和客戶端此次握手的地址一致,且時(shí)間在有效期內(nèi),則表示客戶端可信,便可以建立連接。
-
由于客戶端第一次握手的時(shí)候,沒(méi)有這個(gè) STK,所以服務(wù)度會(huì)回復(fù) REJ 這次握手的信息,并攜帶 STK。
-
因?yàn)?STK 是服務(wù)端加密的,所以如果下次這個(gè)客戶端路由到別的服務(wù)器上了,則這個(gè)服務(wù)器也需要可以識(shí)別出來(lái)。
- STK 中 encode 的是上一次客戶端的地址,如果下一次客戶端攜帶的地址發(fā)生了變化,則同樣會(huì)導(dǎo)致校驗(yàn)失敗。此現(xiàn)象在移動(dòng)端發(fā)生的概率非常大,尤其是 IPV6 場(chǎng)景下,客戶端的出口地址會(huì)經(jīng)常發(fā)生變化。
再介紹下我們的解決方法。第一個(gè)問(wèn)題比較好解,我們只要保證集群內(nèi)的機(jī)器生成 STK 的秘鑰一致即可。第二個(gè)問(wèn)題,我們的解題思路是:
- 我們?cè)?STK 中擴(kuò)展了一個(gè) Client ID, 這個(gè) Clinet ID 是客戶端通過(guò)無(wú)線保鏢黑盒生成并全局唯一不變的,類似于一個(gè)設(shè)備的 SIMID,客戶端通過(guò)加密的 Trasnport Parameter 傳遞給服務(wù)端,服務(wù)端在 STK 中包含這個(gè) ID。
-
如果因?yàn)?Client IP 發(fā)生變化導(dǎo)致校驗(yàn) STK 校驗(yàn)失敗,便會(huì)去校驗(yàn) Client ID,因?yàn)?ID 對(duì)于一個(gè) Client 是永遠(yuǎn)不變的,所以可以校驗(yàn)成功,當(dāng)然前提是,這個(gè)客戶端是真實(shí)的。為了防止 Client ID 的泄露等,我們會(huì)選擇性對(duì) Client ID 校驗(yàn)?zāi)芰ψ鱿蘖鞅Wo(hù)。
技術(shù)點(diǎn)3. 支持 QUIC 無(wú)損升級(jí)
我們知道 UDP 無(wú)損升級(jí)是業(yè)界難題。無(wú)損升級(jí)是指在 reload 或者更新二進(jìn)制時(shí),老的進(jìn)程可以處理完存量連接上的數(shù)據(jù)后優(yōu)雅退出。以 NGINX 為例,這里先介紹下 TCP 是如何處理無(wú)損升級(jí)的,主要是如下的兩個(gè)步驟:
- 老進(jìn)程先關(guān)閉 listening socket,待存量連接請(qǐng)求都結(jié)束后,再關(guān)閉連接套接字
-
新進(jìn)程從老進(jìn)程繼承 listening socket , 開始 accept 新的請(qǐng)求
- 在熱升級(jí)的時(shí)候,old process fork 出 new process 后,new process 會(huì)繼承 listening socket 并開始 recv msg。
- 而 old process 此時(shí)如果關(guān)閉 listenging socket, 則在途的數(shù)據(jù)包便無(wú)法接收,達(dá)不到優(yōu)雅退出的目的。
-
而如果繼續(xù)監(jiān)聽(tīng),則新老進(jìn)程都會(huì)同時(shí)收取新連接上的報(bào)文,導(dǎo)致老進(jìn)程無(wú)法退出。
這里介紹下相關(guān)的解決方法。針對(duì)此問(wèn)題,業(yè)界有一些方法,比如:在數(shù)據(jù)包中攜帶進(jìn)程號(hào),當(dāng)數(shù)據(jù)包收發(fā)錯(cuò)亂后,在新老進(jìn)程之間做一次轉(zhuǎn)發(fā)??紤]到接入層上的性能等原因,我們不希望數(shù)據(jù)再做一次跳轉(zhuǎn)。
結(jié)合我們的落地架構(gòu),我們?cè)O(shè)計(jì)了如下的 基于多端口輪轉(zhuǎn)的無(wú)損升級(jí)方案,簡(jiǎn)單來(lái)說(shuō),我們讓新老進(jìn)程監(jiān)聽(tīng)在不同的端口組并攜帶在 CID 中,這樣 QUIC LB 就可以根據(jù)端口轉(zhuǎn)發(fā)到新老進(jìn)程。為了便于運(yùn)維,我們采用端口輪轉(zhuǎn)的方式,新老進(jìn)程會(huì)在 reload N 次之后,重新開始之前選中的端口。如下圖所示:
- 無(wú)損升級(jí)期間,老進(jìn)程的 Baseport 端口關(guān)閉,這樣不會(huì)再接受 first intial packet, 類似于關(guān)閉了 tcp 的 listening socket。
- 老進(jìn)程的工作端口,繼續(xù)工作,用來(lái)接收當(dāng)前進(jìn)程上殘余的流量。
- 新進(jìn)程的 Baseport 開始工作,用來(lái)接收 first initial packet, 開啟新的連接,類似于開啟了 tcp 的 listening socket。
-
新進(jìn)程的 working port = (I 1) mod N, N 是指同時(shí)支持新老進(jìn)程的狀態(tài)的次數(shù),例如 N = 4, 表示可以同時(shí) reload 四次,四種 Old, New1, New2, New3 四種狀態(tài)同時(shí)并存,I 是上一個(gè)進(jìn)程工作的端口號(hào),這里 1 是因?yàn)橹挥幸粋€(gè) worker, 如果 worker 數(shù)有 M 個(gè),則加 M。
-
建好的連接便被 Load Balancer 轉(zhuǎn)移到新進(jìn)程的監(jiān)聽(tīng)端口的 Working Port 上。
- 在帶寬緊張的時(shí)候,UDP 會(huì)經(jīng)常被限流。
- 一些防火墻對(duì)于 UDP 包會(huì)直接 Drop。
- NAT 網(wǎng)關(guān)針對(duì) UDP 的 Session 存活時(shí)間也較短。
做個(gè)總結(jié)
未來(lái)規(guī)劃
- 我們將利用 QUIC 在應(yīng)用層實(shí)現(xiàn)的優(yōu)勢(shì),設(shè)計(jì)一套統(tǒng)一的具備自適應(yīng)業(yè)務(wù)類型和網(wǎng)絡(luò)類型的 QUIC 傳輸控制框架,對(duì)不同類型的業(yè)務(wù)和網(wǎng)絡(luò)類型,做傳輸上的調(diào)優(yōu),以優(yōu)化業(yè)務(wù)的網(wǎng)絡(luò)傳輸體驗(yàn)。
- 將 gQUIC 切換成 IETF QUIC,推進(jìn)標(biāo)準(zhǔn)的 HTTP3.0 在螞蟻的進(jìn)一步落地。
- 將螞蟻的 QUIC LB 技術(shù)點(diǎn)向 IETF QUIC LB 進(jìn)行推進(jìn),并最終演變?yōu)闃?biāo)準(zhǔn)的 QUIC LB。
- 探索并落地 MPQUIC(多路徑 QUIC) 技術(shù),最大化在移動(dòng)端的收益。
- 繼續(xù) QUIC 的性能優(yōu)化工作,使用 UDP GSO, eBPF,io_uring 等內(nèi)核技術(shù)。
- 探索 QUIC 在內(nèi)網(wǎng)承載東西向流量的機(jī)會(huì)。
作者:孔令濤- EOF -來(lái)源:金融級(jí)分布式架構(gòu)公眾號(hào)