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

乾貨 | SOFA 微服務多語言演進

作者黃挺,螞蟻金服高階技術專家,螞蟻金服分散式架構 SOFA 的開源負責人。目前在螞蟻金服中介軟體團隊負責應用框架與服務化相關的工作。

本文根據黃挺在 2018/09/01 微服務實踐沙龍(上海站)分享整理,這篇文章中,一共列舉了 SOFA 在發展過程中四種多語言的支援方式。



  多語言的現狀


世界上的程式語言千千萬,每個人都有自己偏好的語言,

有人認為 PHP 是世界上最好的語言。也有人非常喜歡 Java,強型別,泛型,多型,效能也非常不錯。也有人很喜歡 Ruby,再比如 Paul Graham 在他著名的「駭客與畫家」的書中表達了對 Lisp 的無限喜愛。個人對於語言的喜好是無可厚非的。

 

相信大家都聽說過巴別塔,人類想要造巴別塔通天,上帝知道了,就讓不同族群的人類說不同的語言,最後人類之間由於語言溝通不流暢,巴別塔沒有造成。從這個故事中我們可以學到要做成一件事情,溝通是多麼地重要。


假設一家公司的業務的標的就是通天,我們都知道只使用一門語言是不能解決所有的問題,前端幾乎都是 JavaScript 系的語言,後端的語言則更加五花八門,從現實地角度來看,必須選擇不同的語言來解決不同的問題,那麼這些語言之間怎麼做相互通訊,就是我們需要去解決的問題。

 

回到微服務這個領域,如果要解決基本的通訊的問題,基本上只要解決三個問題就可以了,首先是基本的網路通訊的能力,然後是雙方需要協商好資料怎麼序列化和反序列化,最後要解決服務發現的問題,要呼叫對方的服務,總得知道從哪裡去尋找對方的服務吧。

 

但是解決完這些基本的問題之後,因為微服務把整個系統給分散式化了,接下來我們又得面對分散式的問題,這個領域的問題可就多了,包括負載均衡,鏈路追蹤,限流,熔斷,鏈路加密,服務鑒權等等一大堆的問題。那麼在微服務這個領域下去解決多語言的問題,我們就必然要在這些問題上做考量,做抉擇。

 

  簡單的解決方式

 

首先,我們嘗試一個比較簡單的方法來解決多語言的問題,假設現在有一個 Java 寫的 SOFA 系統,它透過 SOFARPC 裡面的預設的長連線基於 TCP 的協議 Bolt 暴露了一個服務,這個時候有一個 NodeJS 的系統需要去呼叫它,最簡單的方式可以怎麼做呢?

 


我們可以嘗試在 SOFA 這一端把原來的服務透過一種新的形式給暴露出來,提供一個JSONover  HTTP 的形式讓 NodeJS 的系統去呼叫,在 SOFA 裡面去提供一個 JSON over HTTP 的服務非常方便,只要對原來的配置稍作修改即可。


為什麼選擇 JSON over HTTP 的方式呢?因為 JSON 以及 HTTP 在每個語言裡面幾乎都有原生的支援,即使沒有原生的支援,也有三方庫來支援,而且相對來說,每個語言支援地都比較好。透過 JSON over HTTP,我們解決了網路通訊和序列化的問題。


服務發現的問題怎麼來解決呢?也很簡單,可以透過一些 F5 的這樣的裝置,然後 NodeJS 的應用透過 DNS 的方式去發現 F5 的裝置,然後透過 F5 這樣的裝置再將請求轉到後面的 SOFA 應用的叢集上面,就可以解決了,這邊可能需要去做的就是花點錢,買個負載均衡裝置而已,當然,如果你用 K8s 的話,那麼就可以透過 K8s 的 Service,Service 對於使用方來說,跟 DNS 是一模一樣的。



但是這種方式只能解決服務發現以及基本的通訊的問題,另外的一些高階的能力,比如熔斷,限流等能力,都無法透過這種方式來解決。所以這種方式比較適合於在一些相對來說邊緣的場景下去解決多語言通訊問題,如果呼叫過程中的流量可用,不會有突發的流量,錯誤也是可以忍受的,那麼採用這種方式可能是比較經濟實惠的。這個也是螞蟻金服早期很多多語言的場景的普遍的方式。

 

  “重覆造輪子”

 

隨著 NodeJS 在業界關註度越來越高,螞蟻金服也希望將 NodeJS 應用在更加核心的場景,在當前的螞蟻的整個體系中,BFF 層更多都是由 NodeJS 的應用來承擔。


把 NodeJS 應用在核心場景下,上面提到的第一種方式當然是完全不夠的,我們需要的不僅僅是一個簡單的通訊的方式,而是一個完整的微服務的解決方案。恰好螞蟻的 NodeJS 團隊的技術實力也非常地深厚,所以就想到了可以透過將 Java 體系中的各個微服務的元件做一個 NodeJS 的版本,比如對於通訊協議 Bolt,在 NodeJS 這邊,也搞了一個 sofa-bolt-node,對於 Hessian 序列化的協議,也搞了一個 NodeJS 的 Hessian 的支援 sofa-hessian-node,對於 SOFARPC,也搞了一個 sofa-rpc-node,其他的能力,比如服務發現,負載均衡,限流/熔斷,鏈路追蹤等等,都有對應的 NodeJS 的實現。


這種對應的一個語言重覆造一遍輪子的方式比較好的支撐了 NodeJS 在螞蟻金服作為 BFF 層的發展,並且透過這種方式,各項功能基本上沒有太大的損失,基本上 Java 能夠實現的能力,NodeJS 也都能夠實現。



但是隨著時間的推移,我們發現這種造輪子的方式也有一些無法繞過的問題,前面說到 Java 的微服務框架的能力 Node 基本上都能夠實現,但是對於一些能力已經深入地使用了 Java 語言特性的一些能力,比如註解,在 NodeJS 中是無法直接實現的,導致 NodeJS 這一端只能用一些非常 Hack 的方式來解決這種型別的問題。另外,就是在多語言的維護成本上,同樣的功能,需要 Java 和 NodeJS 維護兩套,這種方式不但對於每個語言的團隊的要求比較高,而最終也導致同一個功能,往往需要相比於原來兩倍的能力來實現,同樣的 Bug,可能也需要 Fix 兩次。

 

總結來說,這種方式可以讓大部分的功能在不同的語言中都得到比較好的實現,但是一旦一些功能和特定的語言特性系結,別的語言實現起來就會非常 Hack,並且不容易維護。另外,這種方式需要整個團隊付出比較多的成本,成本往往和語言的個數成正比,兩個語言可能還好,但是一旦出現更多的語言需要支援,成本可能就難以承受。

 

  多語言閘道器

 

相信每個公司多多少少都有一個 API 閘道器,這個閘道器往往負責多端的接入,並且也會有多協議的支援,瀏覽器端可能會採用  HTTPS 來接入,iOS 和 Android 可能會採用私有的協議來對接,API 閘道器會將接入端的協議最終再轉換成內部的協議,並且作為一個 API 閘道器,往往也會有鑒權,限流等等能力。


 API 閘道器作為微服務體系裡面的一部分,其需要解決的問題和整個微服務體系需要去解決的問題非常類似,作為 API 閘道器,本身就需要去對接多語言的客戶端(iOS,Android),可以說非常適合用 API 閘道器類似的方式來解決多語言問題。



我們可以基於 API 閘道器改造出一個作用於內部的多語言的閘道器出來,在多語言閘道器這一層來實現微服務上的一些限流,熔斷,服務鑒權,負載均衡等等能力,這樣,這些能力就不用每個語言單獨去實現,只需要實現一次就夠了。但是對於服務發現這一塊,業務系統還是需要用某種服務發現的機制來發現多語言閘道器,這部分還是需要選擇一個通用的方案,比如 DNS + F5,或者每個語言單獨實現。


除此之外,多語言閘道器因為是集中式地部署,還會引入一個新的問題,就是資源隔離的問題,假設多語言閘道器後端的一個服務突然變慢了,那麼可能會將多語言閘道器自己給拖垮掉,進而影響到多語言閘道器代理的其他服務。



要解決這個問題,有兩種方式可以去解決,一種是做執行緒池的隔離,可以給一些重要的業務一些單獨的執行緒池,不重要的業務再放到一個大的單獨的執行緒池裡面。



執行緒池隔離的方案裡面僅僅做到了執行緒池的隔離,其他的資源的隔離其實並沒有做,比如 CPU 和 Memory 之類的隔離等等,如果想要更加徹底的隔離方式,可以採用和執行緒池隔離類似的方式,給重要的服務用獨立的多語言閘道器來為其服務,不重要的服務,再給一個大的獨立的叢集去服務。

 

  Service Mesh

 

如果說要進一步地去解決多語言閘道器資源隔離的問題,能否進一步地將集中式的閘道器分散式化呢,假設每一個應用的實體都能夠給它單獨地配置一個多語言閘道器的話,那麼就可以徹底解決多語言閘道器的資源隔離的問題。


實際上,最近在業界非常火的 Service Mesh 就是這樣的方法,在 Service Mesh 裡面,這個「分散式的多語言閘道器」叫做 Sidecar


在 Service Mesh 的體系下,我們可以讓微服務裡面的一些能力下沉到 Sidecar,不管是什麼語言開發的系統接入到整個微服務體系中,都只需要接入這個Sidecar 就可以了,這個 Sidecar 裡面可以做服務發現,限流熔斷,服務鑒權等等的能力,對於特定語言的業務系統來說,只需要和另一個語言的協議能夠對接起來就可以了,其他的一些能力都交給 Sidecar 即可。

 


但是,對於 Service Mesh 這樣的架構,雖然只需要實現一次就可以達到支援多語言的目的,並且沒有多語言閘道器那樣的資源的瓶頸,但是相對來說,運維成本卻高上不少,畢竟,Sidecar 是需要和業務系統部署在一起。在 VM 的場景下,Sidecar 和業務系統部署在同一個 VM 下,那麼對於 Sidecar 的保活,升級,回滾都是需要單獨去解決的。如果在 K8s 的體系下,那麼 Sidecar 的運維的問題就比較好解決了,K8s 的 Pod 的概念非常適合於 Sidecar 這種方式,Sidecar 和業務可以跑在不同的 Container 裡面,可以做到 Sidecar 和業務之間的資源的進一步的隔離。

 

在 Service Mesh 這個方向上,SOFA 也開源了自己的 ServiceMesh 的方案 SOFAMesh,歡迎大家詳細了

 

  總結

 

在這篇文章中,一共列舉了 SOFA 在發展過程中四種多語言的支援方式:

1.  簡單的方式:直接使用 JSON over HTTP + DNS,可以解決基本的通訊的問題,但是缺失了微服務下的其他的高階能力。


2.  造輪子的方式:每個語言單獨實現微服務的各種能力,適合需要適配的語言比較少的情況,需要投入比較大的人力,並且個別特性可能無法在多個語言中完全實現。


3.  多語言閘道器的方式:可以實現一次,把微服務裡面的大部分的能力集中到多語言閘道器裡實現,需要用額外的手段去解決資源隔離的問題,多語言系統依舊需要面臨如何發現多語言閘道器的問題。


4.  Service Mesh 方式:相當於「分散式多語言閘道器」,給每一個服務的實體都加上一個 Sidecar,由 Sidecar 來提供微服務需要的能力,業務系統只需要專註於選擇通訊以及序列化協議即可,這種樣式在沒有 K8s 的支援下有比較大的運維難度。

這些都是我們實踐過程中的一些做法和體會,希望大家可以結合自己的業務階段來參考。


  補充


SOFA 中介軟體是螞蟻金服自主研發的金融級分散式中介軟體,包含了構建金融級雲原生架構所需的各個元件,包括微服務研發框架,RPC 框架,服務註冊中心,分散式定時任務,限流/熔斷框架,動態配置推送,分散式鏈路追蹤,Metrics 監控度量,分散式高可用訊息佇列,分散式事務框架,分散式資料庫代理層等元件,也是在金融場景裡錘煉出來的最佳實踐。


SOFAMesh 相關內容:

專案地址:http://github.com/alipay/sofa-mesh


長按關註,獲取最新分散式架構乾貨

歡迎大家共同打造 SOFAStack https://github.com/alipay


贊(0)

分享創造快樂