微服務設計學習(二)關於服務的整合
前言
微服務之間如何整合應該可以說是微服務相關技術中最重要的知識之一。具體可以表示成服務之間的呼叫方式、通訊協議、序列化協議等。 如果服務整合做得好,你的微服務可以最大程度地保持自治,你可以獨立地修改和釋出,相反,前期考慮得不周全的話,會給你帶來災難。
本篇是微服務設計學習系列的第二篇(繼上一篇釋出居然已經一個多月了,慚愧慚愧)。
歡迎閱讀往期系列:
一、對於整合技術的期望
服務整合的方式方法如此非常的多樣化,我們如何在紛雜的技術中選出最適合的?
首先,我們要知道我們想要從這些整合技術中得到什麼?我們期望的效果是怎麼樣的。這樣,我們才能有目的去選型。雖然根據業務不同會有不同的考慮,但是我們總希望得到這麼幾點的好處:
1. 避免破壞性的修改
我們不希望當我們對某一個服務進行修改釋出的時候,需要對該服務的消費者們也進行修改釋出。我們希望選用的技術能夠儘量避免破壞性修改的發生。比如,一個微服務在一個響應中添加了某個欄位,已有的消費者不應該受到影響。
2. 服務通訊的技術無關性
幹我們這一行的,應該最清楚這個領域唯一不變的就是不斷地變化。
新的工具、框架、語言層出不窮。比如現在的你是一個Java開發者,可能幾年後你會想嘗試Go語言這種更適合雲原生應用的語言來構建微服務。
微服務靈活開放的特性來自於構建微服務的技術異構性,但是整合技術的選用不當,就會對微服務的具體實現技術產生限制。保證微服務之間通訊方式的技術無關性是非常重要的。這就意味著,不應該選擇那種對微服務的具體實現技術有限制的整合方式。
3. 服務易於消費方使用
消費方應該能很容易地使用我們的服務。如果消費方使用該服務比登天還難,那麼無論該微服務多漂亮都沒有任何意義。
理想情況下,消費方應該可以使用任何技術來實現,從另一方面來說,提供一個客戶端庫也可以簡化消費方的使用。但是通常這種庫與其他我們想要得到的東西不可兼得。舉個例子,使用客戶端庫對於消費方來說很方便,但是會造成耦合的增加。
(這一點常常會前面兩點衝突起來)
4. 隱藏內部實現細節
服務的內部實現細節應該最大程度地隱藏起來,不暴露出來。這也是從解耦的角度出發的。
所有傾向於暴露內部實現細節的技術都不應該被採用。
二、服務通訊方式
服務之間的方式可以分為同步通訊和非同步通訊兩種方式。
如果使用同步通訊,發起一個遠端服務呼叫後,呼叫方會阻塞自己並等待整個操作的完成。如 RPC
,HTTP
呼叫。
如果使用非同步通訊,呼叫方不需要等待操作完成就可以返回,甚至可能不需要關心這個操作完成與否。如MQ
。
兩種通訊模式中,同步通訊的協作方式是 請求/響應 的方式,客戶端發起一個請求,然後等待響應。這種模式能夠與同步通訊模式很好地匹配,但非同步通訊也可以使用這種模式。我可以發起一個請求,然後註冊一個回撥,當服務端操作結束之後,會呼叫該回撥。
非同步通訊的主要協作方式則是 基於事件 的方式,客戶端不是發起請求,而是釋出一個事件,然後期待其他的協作者接收到該訊息然後進行處理。
基於事件的系統天生就是非同步的。整個系統都很聰明,也就是說,業務邏輯並非集中存在於某個核心大腦,而是平均地分佈在不同的協作者中。基於事件的協作方式耦合性很低。客戶端釋出一個事件,但並不需要知道誰或者什麼會對此做出響應,這也意味著,你可以在不影響客戶端的情況下對該事件新增新的訂閱者。
延伸閱讀: 微服務模式-同步與非同步,裡面對微服務通訊架構提出了更多的探討。上面的圖片也是從中引用。
通常來講,使用基於事件的協同工作的方式可以有效降低系統的耦合度,並且你能更加靈活地對現有系統進行修改。但是,確實需要額外的工作來對業務流程做跨服務的監控。
這裡有好幾個因素需要考慮。同步呼叫比較簡單,而且很容易知道整個流程的工作是否正常。如果想要請求 / 響應風格的語義,又想避免其在耗時業務上的困境,可以採用非同步請求加回調的方式。另一方面,使用非同步方式有利於協同方案的實施,從而大大減少服務間的耦合,這恰恰就是我們為了能獨立釋出服務而追求的特性。
當然,在大部分的生產場景下,我們看到的應該都是根據業務需要或者一些其他原因,混用不同的方式。
三、RPC or REST
我們先來看看目前流行的幾種整合方案:
- RPC
- REST over HTTP
- 通過訊息代理(如訊息中介軟體)進行訊息傳遞
本節主要針對 RPC(Remote Procedure Call,遠端過程呼叫)和 REST(REpresentational State Transfer,表述性狀態轉移)進行討論。
RPC
遠端呼叫(Remote Procedure Call,RPC)是一種網路間的通訊方式,允許程式呼叫共享網路中其他伺服器的方法或函式,而嚮應用開發者遮蔽遠端呼叫的相關技術細節。RPC應該儘量做到簡單、高效和透明化。客戶端應用可以像呼叫本地物件方法一樣直接呼叫另一臺伺服器上的服務端應用的物件方法。
RPC是一種技術思想而非一種規範。協議只規定了 Client 與 Server 之間的點對點呼叫流程,包括 stub、通訊協議、RPC 訊息解析等部分,在實際應用中,還需要考慮服務的高可用、負載均衡等問題。
RPC要解決的兩個問題:
- 解決分散式系統中,服務之間的呼叫問題。
- 遠端呼叫時,要能夠像本地呼叫一樣方便,讓呼叫者感知不到遠端呼叫的邏輯。
核心是通訊協議、序列化和呼叫框架。
現今流行的RPC 的實現會幫你生成服務端和客戶端的樁程式碼,從而讓你快速開始編碼。基本不用花時間,我就可以在服務之間進行內容互動了。這通常也是 RPC 的主要賣點之一:易於使用。從理論上來說,這種可以只使用普通的方法呼叫而忽略其他細節的做法簡直是給程式設計師的巨大福利。
(常見的RPC框架有:Java RMI,gRPC,Thrift,Dubbo等等)
然而有一些 RPC 的實現確實存在一些問題,比如和技術的強耦合特性,如Java RMI。這一點上有些RPC框架會通過不同語言的客戶端來解決呼叫的問題(如Dubbo),也有像gRPC一樣通過一個通用的服務契約,來生成程式碼的方式來支援多種程式語言。
談談REST 和 RPC
有很多的文章和書籍會將這兩者放在一起講,慣性思維中,REST的實現方式是基於HTTP,RPC的通訊協議一般是TCP,但是RPC 這種方式也可以實現REST風格的服務,只不過你需要自己定義動詞( GET,POST,PUT,DELETE),RPC 也可以使用HTTP 實現,只用到 HTTP 很少的特性,而動詞和 HTTP 的錯誤碼都被忽略了。
最關鍵的是我認為這兩者不是一個維度的概念,很早之前我看一些文章把這兩個一起對比的時候,我看著總會有點迷糊。
我覺得RPC 是面向過程(面向遠端服務函式呼叫),REST 則是面向資源。比如你提供一個查詢使用者的介面,用RPC風格,你可能會這樣寫:
/queryUser?userId=123
用Restful風格呢?
Get
/user?userId=123
再精煉一點,甚至可以這樣:
Get
/user/123
複製程式碼
RPC的思想是把本地函式對映到API,也就是說一個API對應的是一個function,我本地有一個getAllUsers,遠端也能通過某種約定的協議來呼叫這個getAllUsers。至於這個協議是Socket、是HTTP還是別的什麼並不重要;
PS:實現一個RPC不難,難的是如何實現一個高效能高可靠的RPC框架
至於REST,是一種架構風格。REST 風格包含了很多原則和限制,我覺的最核心的點就在於規範,例如使用HTTP status code做錯誤碼,使用HTTP METHOD來表達本次請求要做的動作,使不同的業務系統都有一個“統一”的規範可以參照。
但是這個統一的規範,真的好統一嗎?
我個人覺得在實際業務開發中,REST 這種設計思路是反程式設計師直覺的,因為在本地業務程式碼中仍然是一個個的函式,是動作,但表現在介面形式上則完全是資源的形式。就像面向物件的「萬物皆物件」理論在習慣了純粹面向過程開發的程式設計師眼裡顯得十分別扭一樣:我的程式碼本來就是按順序、迴圈、分支這麼執行的啊,為啥非得在很明確的結構上封裝一層一層的基類子類介面,還要故意給兩個函式起同一個名字,呼叫時才選擇用哪一個呢?使用「萬物皆資源」的思想編寫實際專案中的API介面時,最常見的問題就是「這玩意到底是個什麼資源?……算了,我就直接寫吧,不管什麼風格了」
主要是想說明,RESTful API在很多實際專案中並不實用。因此真的做了專案,要設計介面了,你可能會發現只能用HTTP+JSON來定義介面,API的語義和設計無法嚴格遵守REST風格。(JSON+HTTP != REST)
這兩者可以共存嗎?有啊,SpringCloud 使用 Feign 元件將 REST 介面封裝成 RPC 呼叫,這不就是嗎?在這裡你的SpringBoot 應用介面可以是REST風格的,只不過Feign 元件做了面向服務的包裝。
我個人覺得,REST 在前後端分離 的場景會比較有價值,或者說這個服務可能會被多個客戶端(網頁、移動端、其他服務)整合的時候,REST的統一“規範”就顯示出了他的價值。
如果只是內部服務的話,至少我瞭解的更多的使用的是成熟的RPC框架,**因為一個成熟的RPC框架,更多的是封裝了“服務發現”,"負載均衡",“熔斷降級”一類面向服務的高階特性。**可以這麼理解,RPC框架是面向服務的更高階的封裝,Java 中 SpringCloud 是這樣,Dubbo也是這樣。
關於REST、RPC、HTTP之間的概念,我發現網上很多的文章講的實際上不是很清楚。
我也是看了這篇文章Debunking the Myths of RPC & REST之後,才慢慢理解了這三者之間的概念。推薦你閱讀哦~
如果你覺得英文理解有難度的話,可以閱讀這篇,非常的大白話:RPC-與-Restful
REST 風格包含的內容很多,強烈建議你看一看 Richardson 的成熟度模型,其中有對 REST 不同成熟度的比較。
REST 就像是講普通話,好處就是誰都聽得懂,誰都會講。
RPC 就像是講黑話,好處是可以更精簡、更加保密、更加可定製,壞處就是要求“說”黑話的那一方(client端)也要懂,而且一旦大家都說一種黑話了,換黑話就困難了。
以上對於RPC和REST的討論,雖然思考了很久,但終歸還是一家之言,非常希望有經驗有心得體會的同道們有不同意見可以指明出來。
小結
因為時間精力的原因,本文並沒有過多的涉及代理訊息傳遞這種方式的討論。但需要在這裡重提的是,REST、RPC和代理訊息傳遞不是互斥的,它們都可以在你的微服務體系結構中一起工作。 大公司的系統(基於雲)都在某種程度上有效利用了這幾種方式。最重要的還是要根據業務場景合適的選擇適合的技術,不同通訊方式如何界定服務域邊界和解析方式。
關於RPC/REST/Brokered Messaging的更深入的討論,推薦閱讀這一篇文章 REST,RPC,and Brokered Messaging
以上,是我對微服務整合技術的一些學習。希望能對你有所啟發和幫助。
如果本文有幫助到你,希望能點個贊,這是對我的最大動力。