服務(wù)網(wǎng)格落地
2019 年雙十一是螞蟻集團(tuán)架構(gòu)云化的關(guān)鍵時(shí)間節(jié)點(diǎn),Service Mesh 是應(yīng)用云化非常重要的一環(huán)。業(yè)務(wù)與基礎(chǔ)設(shè)施層的解耦勢(shì)在必行,Mesh 化為這層解耦帶來了實(shí)際可落地的解決方案。本文主要介紹螞蟻集團(tuán) Service Mesh 落地實(shí)踐的核心部分。
本文主要內(nèi)容分為下述幾個(gè)方面:
基礎(chǔ)能力建設(shè)
SOFAMosn 能力大圖
SOFAMosn 主要包括了下述能力:
網(wǎng)絡(luò)代理具備的基礎(chǔ)能力。
XDS(Extended Discovery Service) 等云原生能力。
SOFAMosn 主要模塊圖
業(yè)務(wù)支持
SOFAMosn 作為底層的高性能安全網(wǎng)絡(luò)代理,支撐的業(yè)務(wù)場(chǎng)景包括:RPC、MSG、GATEWAY 等。
IO 模型
SOFAMosn 支持兩種 IO 模型:
Golang 經(jīng)典模型:在螞蟻集團(tuán)內(nèi)部的落地場(chǎng)景,連接數(shù)不是瓶頸,都在幾千或者上萬的量級(jí),螞蟻集團(tuán)選擇了 Golang 經(jīng)典模型 goroutine-per-connection。
模型缺陷:協(xié)程數(shù)量與連接數(shù)量成正比,大鏈接場(chǎng)景下,協(xié)程數(shù)量過多,存在以下開銷:
Stack 內(nèi)存開銷
Read buffer 開銷
Runtime 調(diào)度開銷
RawEpoll 模型:也就是 Reactor 模式,即 I/O 多路復(fù)用(I/O multiplexing) + 非阻塞 I/O (non-blocking I/O)模式。對(duì)于接入層和網(wǎng)關(guān)有大量長(zhǎng)連接的場(chǎng)景,更加適合于 RawEpoll 模型。
步驟說明:
建立連接:
向 Epoll 注冊(cè) oneshot 可讀事件監(jiān)聽。此時(shí)不允許有協(xié)程調(diào)用
conn.read
,以免與runtime Netpoll
沖突。可讀事件到達(dá),從 goroutine pool 挑選一個(gè)協(xié)程進(jìn)行讀事件處理。由于使用的是 oneshot 模式,該 fd 后續(xù)可讀事件不會(huì)再觸發(fā)。
請(qǐng)求處理過程中,協(xié)程調(diào)度與經(jīng)典 Netpoll 模式一致。
請(qǐng)求處理完成,將協(xié)程歸還給協(xié)程池,同時(shí)將 fd 重新添加到 RawEpoll 中。
協(xié)程模型
一個(gè) TCP 連接對(duì)應(yīng)一個(gè) Read 協(xié)程,執(zhí)行收包和協(xié)議解析。
一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè) Worker 協(xié)程,執(zhí)行業(yè)務(wù)處理、Proxy 和 Write 邏輯。
在常規(guī)模型中,一個(gè) TCP 連接有 Read/Write 兩個(gè)協(xié)程,螞蟻團(tuán)隊(duì)取消了單獨(dú)的 Write 協(xié)程,讓 workerpool 工作協(xié)程代替它,減少了調(diào)度延遲和內(nèi)存占用。
能力擴(kuò)展
能力擴(kuò)展主要包括下述幾個(gè)方面:
協(xié)議擴(kuò)展:SOFAMosn 通過使用統(tǒng)一的編、解碼引擎,以及編、解碼器核心接口,提供協(xié)議的 plugin 機(jī)制。支持下述協(xié)議:
SOFARPC
HTTP1.x/HTTP2.0
Dubbo
NetworkFilter 擴(kuò)展:SOFAMson 通過提供 Network Filter 注冊(cè)機(jī)制,以及統(tǒng)一的 packet read/write filter 接口,實(shí)現(xiàn)了 Network filter 擴(kuò)展機(jī)制,當(dāng)前支持下述功能。
TCP proxy
Fault injection
StreamFilter 擴(kuò)展:SOFAMosn 通過提供 stream filter 注冊(cè)機(jī)制,以及統(tǒng)一的 stream send/receive filter 接口,實(shí)現(xiàn)了 Stream filter 擴(kuò)展機(jī)制,支持下述功能。
流量鏡像
RBAC 鑒權(quán)
TLS 安全鏈路
作為金融科技公司,資金安全是最重要的一環(huán),鏈路加密又是其中最基礎(chǔ)的能力。在 TLS 安全鏈路上,螞蟻團(tuán)隊(duì)進(jìn)行了大量的調(diào)研測(cè)試。測(cè)試結(jié)果顯示:
原生 Go 的 TLS 經(jīng)過了大量的匯編優(yōu)化,在性能上是 Nginx(OpenSSL)的 80%。
Boring 版本的 Go,使用 CGO 調(diào)用 BoringSSL,因?yàn)?CGO 的性能問題, 該版本并不占優(yōu)勢(shì)。
所以,螞蟻團(tuán)隊(duì)最后選擇了原生 Go 的 TLS,相信 Go Runtime 團(tuán)隊(duì)后續(xù)會(huì)有更多的優(yōu)化,螞蟻團(tuán)隊(duì)也會(huì)有一些優(yōu)化計(jì)劃。
Go 在 RSA 上沒有太多優(yōu)化,Go-boring(CGO)的能力是 Go 的 1 倍。
p256 在 Go 上有匯編優(yōu)化,ECDSA 優(yōu)于 Go-boring。
在 AES-GCM 對(duì)稱加密上,Go 的能力是 Go-boring 的 20 倍。
在 SHA、MD 等 HASH 算法上,也有對(duì)應(yīng)的匯編優(yōu)化。
為了滿足金融場(chǎng)景的安全合規(guī),螞蟻團(tuán)隊(duì)同時(shí)也對(duì)國(guó)產(chǎn)密碼進(jìn)行了開發(fā)支持,這個(gè)是 Go Runtime 所沒有的。相比國(guó)際標(biāo)準(zhǔn) AES-GCM,目前的性能有大概 50% 的差距,螞蟻團(tuán)隊(duì)已經(jīng)有了后續(xù)的一些優(yōu)化計(jì)劃,敬請(qǐng)期待。
平滑升級(jí)能力
為了讓 SOFAMosn 的發(fā)布對(duì)應(yīng)用無感知,螞蟻團(tuán)隊(duì)開發(fā)了平滑升級(jí)方案,該方案類似 Nginx 的二進(jìn)制熱升級(jí)能力,最大的區(qū)別是 SOFAMosn 老進(jìn)程的連接不會(huì)斷,會(huì)遷移給新的進(jìn)程,包括底層的 socket FD 和上層的應(yīng)用數(shù)據(jù)。這樣可以保證整個(gè)二進(jìn)制發(fā)布過程中,業(yè)務(wù)不受損,對(duì)業(yè)務(wù)無感知。除了支持 SOFARPC、Dubbo、消息等協(xié)議,還支持 TLS 加密鏈路的遷移。
平滑升級(jí)能力主要包括下述幾個(gè)方面的內(nèi)容:
容器升級(jí):主要流程包括下述幾個(gè)方面。
先注入一個(gè)新的 SOFAMosn。
通過共享卷的 UnixSocket 去檢查是否存在老的 SOFAMosn。
如果存在老的 SOFAMosn,就和老的 SOFAMosn 進(jìn)行連接遷移,然后老的 SOFAMosn 退出。
SOFAMosn 的連接遷移:連接遷移的核心是內(nèi)核 Socket 的遷移和應(yīng)用數(shù)據(jù)的遷移。連接不斷,且對(duì)用戶無感知。
SOFAMosn 的 Metric 遷移:螞蟻團(tuán)隊(duì)使用了共享內(nèi)存來共享新老進(jìn)程的 Metric 數(shù)據(jù),保證在遷移的過程中 Metric 數(shù)據(jù)也是正確的。
內(nèi)存復(fù)用機(jī)制
內(nèi)存復(fù)用機(jī)制主要特征如下:
基于 sync.Pool。
Slice 復(fù)用使用 Slab 細(xì)粒度,提高復(fù)用率。
常用結(jié)構(gòu)體復(fù)用。
當(dāng)前現(xiàn)狀:
線上復(fù)用率可以達(dá)到 90% 以上。
sync.Pool 還存在一些問題,隨著 Runtime 對(duì) sync.Pool 的持續(xù)優(yōu)化,比如 Go 1.13 使用 lock-free 結(jié)構(gòu)減少鎖競(jìng)爭(zhēng)和增加了 victim cache 機(jī)制,它在未來會(huì)越來越完善。
XDS(UDPA)
支持云原生統(tǒng)一數(shù)據(jù)面 API,全動(dòng)態(tài)配置更新。其中,XDS 指 Extended Discovery Service。UDPA 指 Universal Data Plane API。
前期準(zhǔn)備
性能壓測(cè)和優(yōu)化
在上線前的準(zhǔn)備過程中,螞蟻團(tuán)隊(duì)在灰度環(huán)境中針對(duì)核心應(yīng)用 cashiercloudtb 進(jìn)行了大量的壓測(cè)和優(yōu)化,為后面的落地打下了堅(jiān)實(shí)的基礎(chǔ)。
從線下環(huán)境到灰度環(huán)境,螞蟻團(tuán)隊(duì)遇到了很多線下沒有的大規(guī)模場(chǎng)景,比如:
單實(shí)例數(shù)萬后端節(jié)點(diǎn),數(shù)千路由規(guī)則:不僅占用內(nèi)存,對(duì)路由匹配效率也有很大影響。
海量高頻的服務(wù)發(fā)布注冊(cè):對(duì)性能和穩(wěn)定性有很大挑戰(zhàn)。
整個(gè)壓測(cè)優(yōu)化過程歷時(shí)五個(gè)月,從最初的 CPU 整體增加 20%,RT 每跳增加 0.8 ms, 到最后 CPU 整體增加 6%,RT 每跳增加 0.25 ms,內(nèi)存占用峰值優(yōu)化為之前的 1/10。
整體增加CPU | 每跳RT | 內(nèi)存占用峰值 | |
優(yōu)化前 | 20% | 0.8 ms | 2365 M |
優(yōu)化后 | 6% | 0.25 ms | 253 M |
部分優(yōu)化措施:
在 6.18 大促時(shí),螞蟻團(tuán)隊(duì)上線了部分核心鏈路應(yīng)用,CPU 損耗最多增加 1.7%,有些應(yīng)用從 Java 遷移到 Go,CPU 損耗還降低了 8% 左右。延遲方面平均每跳增加 0.17 ms,兩個(gè)合并部署系統(tǒng)全鏈路增加 5~6 ms,有 7% 左右的損耗。
在單機(jī)房上線 SOFAMosn 時(shí),SOFAMosn 在全鏈路壓測(cè)下的整體性能表現(xiàn)更好。比如:交易付款時(shí),帶 SOFAMosn 比不帶 SOFAMosn 的響應(yīng)時(shí)間(RT)降低了 7.5%。
SOFAMosn 所做的大量核心優(yōu)化和下沉的 Route Cache 等業(yè)務(wù)邏輯優(yōu)化,更帶來了架構(gòu)的紅利。
Go 版本選擇
版本的升級(jí)都需要做一系列測(cè)試,新版本并不都最適合目標(biāo)場(chǎng)景。該項(xiàng)目最開始使用的版本為 Go 1.9.2,在經(jīng)過一年迭代之后,螞蟻團(tuán)隊(duì)開始調(diào)研當(dāng)時(shí)的最新版 Go 1.12.6,測(cè)試驗(yàn)證了新版很多好的優(yōu)化,也修改了內(nèi)存回收的默認(rèn)策略,以便更好地滿足項(xiàng)目需求。
GC 優(yōu)化,減少長(zhǎng)尾請(qǐng)求:新版的自我搶占(self-preempt)機(jī)制,將耗時(shí)較長(zhǎng)的 GC 標(biāo)記過程打散,來換取更為平滑的GC 表現(xiàn),減少對(duì)業(yè)務(wù)的延遲影響。
Go 1.9.2
Go 1.12.6
內(nèi)存回收策略:Go 1.12 修改了內(nèi)存回收策略,從默認(rèn)的 MADV_DONTNEED 修改為了 MADV_FREE。雖然這是一個(gè)性能優(yōu)化,但是在實(shí)際使用中,測(cè)試顯示性能并沒有大的提升,卻占用了更多的內(nèi)存,對(duì)監(jiān)控和問題判斷有很大的干擾。螞蟻團(tuán)隊(duì)通過 GODEBUG=madvdontneed=1 恢復(fù)為之前的策略。在 issue 里也有相關(guān)討論,后續(xù)版本可能也會(huì)改動(dòng)這個(gè)值。
使用 Go 1.12 默認(rèn)的 MADV_FREE 策略時(shí),HeapInuse = 43 M, 但是 HeapIdle = 600 M,一直不能釋放。
Go Runtime Bug 修復(fù)
在前期灰度驗(yàn)證時(shí),SOFAMosn 線上出現(xiàn)了較嚴(yán)重的內(nèi)存泄露,一天泄露了 1 G 內(nèi)存,最終排查顯示,是 Go Runtime 的 Writev 實(shí)現(xiàn)存在缺陷,導(dǎo)致 Slice 的內(nèi)存地址被底層引用,GC 不能釋放。
螞蟻團(tuán)隊(duì)給 Go 官方提交了 Bugfix,已合入 Go 1.13 最新版,參見 internal/poll: avoid memory leak in Writev。