歡迎光臨
每天分享高質量文章

解讀 | TARS 開源專案釋出 Go 語言版本

近日,Tars 開源專案在上海釋出並開源了 Go 語言版本,其效能與 C++ 版本相當,比 gRPC 的效能高 5 倍。  
— 騰訊開源


致謝

 作者 | 騰訊開源

導語:近日,Tars 開源專案在上海釋出並開源了 Go 語言版本,其效能與 C++ 版本相當,比 gRPC 的效能高 5 倍。  

Tars 是騰訊開源的一款微服務框架,它於去年 4 月份開源,並於今年 6 月捐贈給了 Linux 基金會。Tars 為使用者提供了涉及到開發和運維的一整套解決方案,幫助一個產品或者服務快速開發、釋出、部署、上線和維護。它集可擴充套件協議編解碼、高效能 RPC 通訊框架、名字路由與發現、釋出監控、日誌統計、配置管理等於一體,透過它可以快速用微服務的方式構建穩定可靠的分散式應用,並實現完整有效的服務治理。經過一年多的發展,目前 Tars 已經被許多企業使用,如閱文集團、虎牙直播、科大訊飛,優品財富、龍圖遊戲和金太陽教育等。

據悉,9 月 15 日,騰訊宣佈正式開源 Tars 的 Golang 版本 Tars-Go。編者從 Tars 的開源公告中瞭解到 Tars 與當前市面上其它微服務框架的差異、技術架構、效能資料與相關技術細節,本文將詳細介紹 Tars 此次釋出的 Golang 版本。

專案地址: https://github.com/TarsCloud/TarsGo 

支援服務治理、多語言,只 Tars 一家 

微服務架構這兩年變得格外火熱,它已經成為當前最主流的架構樣式。提起微服務框架,我們可以自然地舉出 Dubbo、gRPC 與 Spring Cloud 等眾多的知名專案,依據是否支援服務治理是否支援多語言兩個維度可將這些微服務框架分為以下四類:

 

◈ 只有服務呼叫沒有服務治理類的框架。典型的代表有 gRPC、Thrift 等,他們很好地解決了服務間通訊的問題,大部分也支援多語言,但使用這類框架時需要自己去解決服務治理問題。
◈ 帶服務治理但支援單一語言的框架。典型的代表有 Spring Cloud 和 Dubbo,它們都是用 Java 實現的框架,使用者整合多個開源專案一起並能滿足服務治理等的需求。
◈ Service Mesh它支援服務治理,並透過 Sidecar 樣式解決框架對多語言支援,業務需要再封裝一套通訊元件去解決通訊問題,以及非同步呼叫等問題,同時會增加架構和維護的複雜度。
◈ 帶服務治理並支援多語言的框架。目前業界比較少,除了 Tars 目前還沒有發現其它具有代表性的框架。

從上面的分析大概可以發現,Tars 是支援服務治理的同時又提供多語言支援的微服務框架,這是 Tars 的獨特之處,也是其優勢。 

Tars 可以執行在物理機、虛擬機器和容器,其協議主打的是基於 IDL 實現的 Tars 協議,它是一種二進位制解析協議,與 pb 類似,同時 Tars 還擴充套件支援其它協議,乃至使用者自定義。 

呼叫方式主要以 RPC 為主,支援同步、非同步和單向呼叫幾種方式。在服務治理方面除了支援服務註冊、發現等業界常說的能力之外,還提供面對海量訪問的一些其它治理能力,如 Set 模型、自動區域感知、過載保護等,語言上除了此次新支援的 Golang,目前已經支援了 C++、Java、NodeJS 與 PHP,同時整體框架可以和 DevOps 很好地協同工作。

Tars 整體分為三個部分:Registry、服務節點和基礎服務叢集。

Registry

Registry 是微服務叢集的管理和控制節點,提供服務註冊和發現等功能。

服務節點

服務節點是 Tars 執行的原子單元,可以是一個容器也可以是一個虛機或物理機,一個業務服務透過部署多個服務節點來解決容量和容錯問題。服務節點上包括一個 node 管理服務和一個或多個業務服務,node 服務對本節點的服務進行統一管理,提供啟停、監控服務節點等功能,同時接收業務服務節點上報過來的心跳,上報給 Registry 作為服務發現的資料來源。

基礎服務叢集

基礎服務叢集是為解決微服務治理而設計的一系列服務,服務節點數量不定,為了自身的容錯容災,一般也要求在多個伺服器上進行部署,具體的節點數量與業務規模有關,比如,如果業務規模大需要打較多的日誌,就需要部署更多的日誌服務節點。基礎服務主要包括監控統計、配置中心、日誌聚合、認證鑒權和分散式呼叫鏈等。Tars 具有非常完善的服務治理能力。

 

Tars 透過 Registry、服務節點和基礎服務叢集協同工作,透明完成服務發現/註冊、負載均衡、鑒權、分散式跟蹤等服務治理相關工作。如框架透過 Registry 來註冊 xxxsvr,Client 透過訪問 Registry 獲取到被調服務的地址資訊串列,Client 再根據需要選擇合適的負載均衡方式來呼叫服務。負載均衡支援輪詢、hash、權重等多種方式。

為了更及時地遮蔽故障節點,Client 根據呼叫被調服務的異常情況判斷是否有故障來更快地進行故障遮蔽。具體策略是,當 Client 呼叫某個伺服器出現呼叫連續超時超過設定閾值,或者呼叫的超時比率超過一定百分比閾值,Client 就會對此伺服器節點進行遮蔽,讓流量分發到正常的節點上去。對遮蔽的伺服器節點,每隔一定時間進行重連,如果正常,則進行正常的流量分發。 

隨著業務增長,服務的部署難免會跨機房或者地域,常規的負載均衡方式面對跨地區或者跨機房部署的服務會因為網路原因造成延時增大,為了加快服務間的訪問速度,減少因建設跨地區、跨機房呼叫帶來的網路資源消耗,減少網路故障帶來的影響,Tars 提供自動區域感知的服務治理功能。

 

透過 Registry 和開發框架配合實現自動區域感知,這樣的優勢有:

◈ 運維簡單
◈ 降低延時減少頻寬消耗
◈ 更強的容災能力

此外 Tars 還提供 Set 模型

Set 模型是根據業務功能特徵對部署進行規範化和標準化,以 Set 為單元進行部署。Set 模型的優點有:

◈ 有效防止故障擴散
◈ 方便進行容量管理 

對於流量控制,服務釋出上線主要面對的問題是“如何做對業務無損的服務變更”與“如何做灰度驗證”,在 Tars 中,可透過 Registry 和開發框架配合實現按需進行流量控制,達到無損釋出和灰度流量的目的。

語言支援方面,除了此前已經支援的 PHP、C++、NodeJS 與 Java,此次還加入了 Golang 支援。 

此外,Tars 還提供一個 OSS 平臺,可使運營視覺化、Web 化。

它主要包含以下特點:

◈ 業務管理:包括已部署的服務,以及服務管理、釋出管理、服務配置、服務監控、特性監控等
◈ 運維管理:包括服務部署、擴容、模版管理等
◈ 提供 Open API ,可定製自己的 OSS 系統

Tars-Go,Tars Go!

多語言支援是 Tars 的一大優勢,在此之前 Tars 已經推出了 C++、Java、PHP、NodeJS 版本。Go 語言的協程併發機制使它非常適用於大規模高併發後端伺服器程式開發,同時隨著容器化技術的飛速發展,諸如 Docker、Kubernetes 與 Etcd 等專案興起,使得 Go 語言越來越流行,併成為雲原生的首選語言。Tars 的 Go 語言版本也因此應運而生,此次 Tars-Go 的推出,在大環境整體逐漸走向雲原生的當下,意義非凡。

此次新推出的 Go 版本 Tars-Go 整體架構主要可以分為三個部分,如下圖所示:

◈ 左邊是 tars2go 工具,tars2go 基於巴科斯正規化(BNF),這是一種描述程式語言結構的形式化方法,用來對 Tars 檔案進行語法和詞法分析,生成相應的程式碼,供客戶端和服務端使用。同時它提供 Tars 協議二進位制流的編解碼功能,將二進位制包轉成相應的 Go 資料結構。
◈ 右邊部分是 package tars,它包含了 Client 和 Server 兩部分的功能:
◈ Client 由 Servantproxy、Communicator、ObjProxy、adapterproxy 等邏輯結構組成,這些邏輯結構用來管理 servant,obj 對應到的服務端節點的 ip 埠,和 C++ 的邏輯保持一致。底層使用 net.Conn 來建立具體的連線,並用 SendQueue chan 來控制併發數量。Client 還包含一些 Goroutine,用來做特性監控和 stat 監控上報。
◈ Server 使用 package net 的 listener 來管理 TCP 和 UDP 連線,使用多個 Goroutine 進行 accept,並將 accept 之後的 net.Conn 經過 SendQueue chan 交給後端的 Handler 進行處理。Handler 由一堆 woker Goroutine 組成,每個 Goroutine 基於 net.Conn 進行收發包、Tars 協議解碼,並經過 dispatcher(由 tars2go 生成) 來呼叫使用者的程式碼實現,然後將結果編碼成二進位制流傳回給 Client。Server 也包含一些 Goroutine 實現遠端日誌非同步上報等功能,防止同步呼叫阻塞請求。

編者瞭解到,Tars 開源團隊在研發 Tars-Go 的過程中經歷的對其各個方面的效能調優改造,Tars-Go 在早先的版本註重於功能的開發和完善,沒有體系化地進行壓測和效能分析。在業務使用一段時間後,開始註重效能最佳化。Tars 開源團隊對 tars2go 工具先進行了一輪最佳化,在生成語樹的時候生成好了型別資訊,避免使用反射進行型別判斷,編解碼的效率提升了 2 倍,然後對再對整體 servant 進行了一輪輪壓測,併進行 CPU profile 效能分析。

下麵是效能提升最佳化的幾個實體: 

Timer 效能問題

每個請求進來,Tars-Go 會建立一個協程進行處理,因為要處理呼叫超時,會建立一個 timer,在結束的時候會刪掉 timer,當併發量一上來,就會頻繁建立和刪除 timer,佔用服務大量 CPU 時間。

研發團隊在一個 issue 中發現 ,在多 CPU 的場景下,如果存在大量的 timer,效能就會大量損耗,最佳化方式是每個 p 有自己的 timer,這樣可以大幅提升整體併發效能。於是 Tars-Go 將編譯環境升級至 1.10.3,從 profile 來看,效能得到了很大的提升,並且基於時間輪詢演演算法實現了自己的 timer,以精度換取效能和效率。

net 包的 SetDeadline 呼叫效能問題

為了設定網路連線的讀寫超時,Tars-Go 使用了 net 包的 SetReadDeadline/SetWriteDeadline 等相關呼叫,但從 profile 發現,當併發非常大的時候,會導致這兩個呼叫佔用了大量的 CPU 時間。為了繞開這兩個相關呼叫,使用了 Sysfd 進行 Socket 讀寫超時的設定。 

bytes 的 Buffer 帶來的效能問題

從下圖可以看出,有相當大的一部分時間耗在了 slice 相關的操作上,原來在包的編解碼過程中,使用 bytes.Buffer 進行臨時存放,當 bytes.Buffer 底層用的 byte slice 大小不夠的時候,就會分配一定的記憶體空間,頻繁地分配效率很低,所以導致大包情況下效能下降比較明顯。

聯想到了 Redis 的記憶體模型和 Linux 的 slab 機制,對於頻繁建立銷毀的物件,採用預先建立和重覆利用的方式。而 Go 本身提供了一種 sync.Pool 機制,供臨時物件的復用,以減少 GC,Tars-Go 在此基礎上,實現了類似 Linux slab 機制分配的 buffer 管理方案,透過這種方案,效能大幅提高。

其他方面的最佳化

經過上面的效能最佳化後,Tars-Go 在小包的併發表現提升了 5 倍。

◈ 壓測機型:4 核/8執行緒 CPU  3.3Ghz 主頻  16G 記憶體
◈ 壓測邏輯:客戶端帶著一定大小的資料給服務端,服務端原樣傳回給客戶端
◈ 服務端單行程,多個客戶端發起測試

Tars-Go 程式設計示例 

Tars 協議是一個二進位制協議,它是與語言無關的IDL語言,由工具自動生成服務端和客戶端程式碼,下邊是一個 Tars 協議的示例:

具體程式設計的時候,首先需要定義一個 Tars 檔案,如下所示:定義介面 Mult ,a 和 b 為入參,c 為出參,均為整型。

接著生成介面程式碼。使用 tars2go JesseTest.tars 即可自動生成 pacakge Prajna JesseTest 的 servant 和 Mult 方法的框架實現,業務無需關心實現細節:

最後,實現介面程式碼,將入參 a、b相乘後的結果放到 c 傳回給客戶端:

之後 go build 就可以進行編譯。

而客戶端只需關註出入參,引入由 Tars 檔案轉化成的包即可完成一次 RPC 呼叫。

未來,Linux 基金會將加強 Tars 專案的社群運作機制,讓 Tars 的影響力從中國走向國際。

◈ Tars:https://github.com/TarsCloud
◈ Tars-Go:https://github.com/TarsCloud/TarsGo
◈ 微信掃碼加入TARS-GO官方交流群:

以上內容參考 Tars 開源團隊核心成員陳明傑的演講 PPT 《億級規模高可用架構原始碼剖析——騰訊 Go 語言開發框架 TARS-GO》。

贊(0)

分享創造快樂