微服務實戰(三):深入微服務架構的進程間通信

見:http://www.dockone.io/article/549

簡介

在單體式應用中,各個模塊之間的調用是通過編程語言級別的方法或者函數來實現的。但是一個基于微服務的分布式應用是運行在多臺機器上的。一般來說,每個服務實例都是一個進程。因此,如下圖所示,服務之間的交互必須通過進程間通信(IPC)來實現。

Richardson-microservices-part3-monolith-vs-microservices-1024x518.png


后面我們將會詳細介紹IPC技術,現在我們先來看下設計相關的問題。

交互模式

當為某一個服務選擇IPC時,首先需要考慮服務之間如何交互。客戶端和服務器之間有很多的交互模式,我們可以從兩個維度進行歸類。第一個維度是一對一還是一對多:

?? 一對一 :每個客戶端請求有一個服務實例來響應。
?? 一對多 :每個客戶端請求有多個服務實例來響應

第二個維度是這些交互式同步還是異步:

? 同步模式:客戶端請求需要服務端即時響應,甚至可能由于等待而阻塞。
? 異步模式:客戶端請求不會阻塞進程,服務端的響應可以是非即時的。

下表顯示了不同交互模式:

74.pic_.jpg


一對一的交互模式有以下幾種方式:

? 請求/響應:一個客戶端向服務器端發起請求,等待響應。客戶端期望此響應即時到達。在一個基于線程的應用中,等待過程可能造成線程阻塞。
? 通知(也就是常說的單向請求):一個客戶端請求發送到服務端,但是并不期望服務端響應。
? 請求/異步響應:客戶端發送請求到服務端,服務端異步響應請求。客戶端不會阻塞,而且被設計成默認響應不會立刻到達。

一對多的交互模式有以下幾種方式:

? 發布/ 訂閱模式:客戶端發布通知消息,被零個或者多個感興趣的服務消費。

? 發布/異步響應模式:客戶端發布請求消息,然后等待從感興趣服務發回的響應。

每個服務都是以上這些模式的組合,對某些服務,一個IPC機制就足夠了;而對另外一些服務則需要多種IPC機制組合。下圖展示了在一個打車服務請求中服務之間是如何通信的。

Richardson-microservices-part3-taxi-service-1024x609.png


上圖中的服務通信使用了通知、請求/響應、發布/訂閱等方式。例如,乘客通過移動端給『行程管理服務』發送通知,希望申請一次出租服務。『行程管理服務』發送請求/響應消息給『乘客服務』以確認乘客賬號是有效的。緊接著創建此次行程,并用發布/訂閱交互模式通知其他服務,包括定位可用司機的調度服務。

現在我們了解了交互模式,接下來我們一起來看看如何定義API。

定義API

API是服務端和客戶端之間的契約。不管選擇了什么樣的IPC機制,重要的是使用某種交互式定義語言(IDL)來精確定義一個服務的API。甚至有一些關于使用 API first的方法 (API-first approach)來定義服務的很好的理由。在開發之前,你需要先定義服務的接口,并與客戶端開發者詳細討論確認。這樣的討論和設計會大幅度提到API的可用度以及滿意度。

在本文后半部分你將會看到,API定義實質上依賴于選擇哪種IPC。如果使用消息機制,API則由消息頻道(channel)和消息類型構成;如果選擇使用HTTP機制,API則由URL和請求、響應格式構成。后面將會詳細描述IDL。

API的演化

服務端API會不斷變化。在一個單體式應用中經常會直接修改API,然后更新給所有的調用者。而在基于微服務架構應用中,這很困難,即使只有一個服務使用這個API,不可能強迫用戶跟服務端保持同步更新。另外,開發者可能會嘗試性的 部署新版本的服務 ,這個時候,新舊服務就會同事運行。你需要知道如何處理這些問題。

你如何處理API變化,這依賴于這些變化有多大。某些改變是微小的,并且可以和之前版本兼容。比如,你可能只是為某個請求和響應添加了一個屬性。設計客戶端和服務端時候應該遵循 健壯性原理 ,這很重要。客戶端使用舊版API應該也能和新版本一起工作。服務端仍然提供默認響應值,客戶端忽略此版本不需要的響應。使用IPC機制和消息格式對于API演化很有幫助。

但是有時候,API需要進行大規模的改動,并且可能與之前版本不兼容。因為你不可能強制讓所有的客戶端立即升級,所以支持老版本客戶端的服務還需要再運行一段時間。如果你正在使用基于基于HTTP機制的IPC,例如REST,一種解決方案是把版本號嵌入到URL中。每個服務都可能同時處理多個版本的API。或者,你可以部署多個實例,每個實例負責處理一個版本的請求。

處理部分失敗

在上一篇 關于API gateway 的文章中,我們了解到分布式系統中部分失敗是普遍存在的問題。因為客戶端和服務端是都是獨立的進程,一個服務端有可能因為故障或者維護而停止服務,或者此服務因為過載停止或者反應很慢。

考慮這篇文章中描述的 部分失敗的場景 。假設推薦服務無法響應請求,那客戶端就會由于等待響應而阻塞,這不僅會給客戶帶來很差的體驗,而且在很多應用中還會占用很多資源,比如線程,以至于到最后由于等待響應被阻塞的客戶端越來越多,線程資源被耗費完了。如下圖所示:

Richardson-microservices-part3-threads-blocked-1024x383.png


為了預防這種問題,設計服務時候必須要考慮部分失敗的問題。

Netfilix提供 了一個比較好的解決方案,具體的應對措施包括:

? 網絡超時:當等待響應時,不要無限期的阻塞,而是采用超時策略。使用超時策略可以確保資源不會無限期的占用。
? 限制請求的次數:可以為客戶端對某特定服務的請求設置一個訪問上限。如果請求已達上限,就要立刻終止請求服務。
?? 斷路器模式(Circuit Breaker Pattern) :記錄成功和失敗請求的數量。如果失效率超過一個閾值,觸發斷路器使得后續的請求立刻失敗。如果大量的請求失敗,就可能是這個服務不可用,再發請求也無意義。在一個失效期后,客戶端可以再試,如果成功,關閉此斷路器。
? 提供回滾:當一個請求失敗后可以進行回滾邏輯。例如,返回緩存數據或者一個系統默認值。

Netflix Hystrix 是一個實現相關模式的開源庫。如果使用JVM,推薦考慮使用Hystrix。而如果使用非JVM環境,你可以使用類似功能的庫。

IPC技術

現在有很多不同的IPC技術。服務之間的通信可以使用同步的請求/響應模式,比如基于HTTP的REST或者Thrift。另外,也可以選擇異步的、基于消息的通信模式,比如AMQP或者STOMP。除以之外,還有其它的消息格式供選擇,比如JSON和XML,它們都是可讀的,基于文本的消息格式。當然,也還有二進制格式(效率更高)的,比如Avro和Protocol Buffer。接下來我們將會討論異步的IPC模式和同步的IPC模式,首先來看異步的。
異步的,基于消息通信
當使用基于異步交換消息的進程通信方式時,一個客戶端通過向服務端發送消息提交請求。如果服務端需要回復,則會發送另外一個獨立的消息給客戶端。因為通信是異步的,客戶端不會因為等待而阻塞,相反,客戶端理所當然的認為響應不會立刻接收到。

一個 消息 由頭部(元數據例如發送方)和消息體構成。消息通過 channel 發送,任何數量的生產者都可以發送消息到channel,同樣的,任何數量的消費者都可以從渠道中接受數據。有兩類channel, 點對點 發布/訂閱 。點對點channel會把消息準確的發送到某個從channel讀取消息的消費者,服務端使用點對點來實現之前提到的一對一交互模式;而發布/訂閱則把消息投送到所有從channel讀取數據的消費者,服務端使用發布/訂閱channel來實現上面提到的一對多交互模式。

下圖展示了打車軟件如何使用發布/訂閱:

Richardson-microservices-part3-pub-sub-channels-1024x639.png


行程管理服務在發布-訂閱channel內創建一個行程消息,并通知調度服務有一個新的行程請求,調度服務發現一個可用的司機然后向發布-訂閱channel寫入司機建議消息(Driver Proposed message)來通知其他服務。

有很多消息系統可以選擇,最好選擇一種支持多編程語言的。一些消息系統支持標準協議,例如AMQP和STOMP。其他消息系統則使用獨有的協議,有大量開源消息系統可選,比如 RabbitMQ Apache Kafka Apache ActiveMQ NSQ 。它們都支持某種形式的消息和channel,并且都是可靠的、高性能和可擴展的;然而,它們的消息模型完全不同。

使用消息機制有很多優點:

?? 解耦客戶端和服務端 :客戶端只需要將消息發送到正確的channel。客戶端完全不需要了解具體的服務實例,更不需要一個發現機制來確定服務實例的位置。

?? Message Buffering :在一個同步請求/響應協議中,例如HTTP,所有的客戶端和服務端必須在交互期間保持可用。而在消息模式中,消息broker將所有寫入channel的消息按照隊列方式管理,直到被消費者處理。也就是說,在線商店可以接受客戶訂單,即使下單系統很慢或者不可用,只要保持下單消息進入隊列就好了。

? 彈性客戶端-服務端交互:消息機制支持以上說的所有交互模式。

?? 直接進程間通信 :基于RPC機制,試圖喚醒遠程服務看起來跟喚醒本地服務一樣。然而,因為物理定律和部分失敗可能性,他們實際上非常不同。消息使得這些不同非常明確,開發者不會出現問題。

然而,消息機制也有自己的缺點:

?? 額外的操作復雜性 :消息系統需要單獨安裝、配置和部署。消息broker(代理)必須高可用,否則系統可靠性將會受到影響。

?? 實現基于請求/響應交互模式的復雜性 :請求/響應交互模式需要完成額外的工作。每個請求消息必須包含一個回復渠道ID和相關ID。服務端發送一個包含相關ID的響應消息到channel中,使用相關ID來將響應對應到發出請求的客戶端。也許這個時候,使用一個直接支持請求/響應的IPC機制會更容易些。

現在我們已經了解了基于消息的IPC,接下來我們來看看基于請求/響應模式的IPC。

同步的,基于請求/響應的IPC
當使用一個同步的,基于請求/響應的IPC機制,客戶端向服務端發送一個請求,服務端處理請求,返回響應。一些客戶端會由于等待服務端響應而被阻塞,而另外一些客戶端也可能使用異步的、基于事件驅動的客戶端代碼(Future或者Rx Observable的封裝)。然而,不像使用消息機制,客戶端需要響應及時返回。這個模式中有很多可選的協議,但最常見的兩個協議是REST和Thrift。首先我們來看下REST。

REST

現在很流行使用 RESTful 風格的API。REST是基于HTTP協議的。另外,一個需要理解的比較重要的概念是,REST是一個資源,一般代表一個業務對象,比如一個客戶或者一個產品,或者一組商業對象。REST使用HTTP語法協議來修改資源,一般通過URL來實現。舉個例子,GET請求返回一個資源的簡單信息,響應格式通常是XML或者JSON對象格式。POST請求會創建一個新資源,PUT請求更新一個資源。這里引用下REST之父Roy Fielding說的:

當需要一個整體的、重視模塊交互可擴展性、接口概括性、組件部署獨立性和減小延遲、提供安全性和封裝性的系統時,REST可以提供這樣一組滿足需求的架構。
下圖展示了打車軟件是如何使用REST的。

Richardson-microservices-part3-rest-1024x397.png


乘客通過移動端向行程管理服務的 /trips 資源提交了一個POST請求。行程管理服務收到請求之后,會發送一個GET請求到乘客管理服務以獲取乘客信息。當確認乘客信息之后,緊接著會創建一個行程,并向移動端返回201(譯者注:狀態碼)響應。

很多開發者都表示他們基于HTTP的API是RESTful的。但是,如同Fielding在他的 博客 中所說,這些API可能并不都是RESTful的。Leonard Richardson為REST定義了一個 成熟度模型 ,具體包含以下4個層次(摘自 IBM ):
  • 第一個層次(Level 0)的 Web 服務只是使用 HTTP 作為傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬于此類。
  • 第二個層次(Level 1)的 Web 服務引入了資源的概念。每個資源有對應的標識符和表達。
  • 第三個層次(Level 2)的 Web 服務使用不同的 HTTP 方法來進行不同的操作,并且使用 HTTP 狀態碼來表示不同的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。
  • 第四個層次(Level 3)的 Web 服務使用 HATEOAS。在資源的表達中包含了鏈接信息。客戶端可以根據鏈接來發現可以執行的動作。

使用基于HTTP的協議有如下好處:

? HTTP非常簡單并且大家都很熟悉。
? 可以使用瀏覽器擴展(比如 Postman )或者curl之類的命令行來測試API。
? 內置支持請求/響應模式的通信。
? HTTP對防火墻友好的。
? 不需要中間代理,簡化了系統架構。

不足之處包括:

? 只支持請求/響應模式交互。可以使用HTTP通知,但是服務端必須一直發送HTTP響應才行。
? 因為客戶端和服務端直接通信(沒有代理或者buffer機制),在交互期間必須都在線。
? 客戶端必須知道每個服務實例的URL。如之前那篇關于 API Gateway的文章 所述,這也是個煩人的問題。客戶端必須使用服務實例發現機制。

開發者社區最近重新發現了RESTful API接口定義語言的價值。于是就有了一些RESTful風格的服務框架,包括 RAML Swagger 。一些IDL,例如Swagger允許定義請求和響應消息的格式。其它的,例如RAML,需要使用另外的標識,例如 JSON Schema 。對于描述API,IDL一般都有工具來定義客戶端和服務端骨架接口。

Thrift

Apache Thrift 是一個很有趣的REST的替代品。它是Facebook實現的一種高效的、支持多種編程語言的遠程服務調用的框架。Thrift提供了一個C風格的IDL定義API。使用Thrift編譯器可以生成客戶端和服務器端代碼框架。編譯器可以生成多種語言的代碼,包括C++、Java、Python、PHP、Ruby, Erlang和Node.js。

Thrift接口包括一個或者多個服務。服務定義類似于一個JAVA接口,是一組方法。Thrift方法可以返回響應,也可以被定義為單向的。返回值的方法其實就是請求/響應類型交互模式的實現。客戶端等待響應,并可能拋出異常。單向方法對應于通知類型的交互模式,服務端并不返回響應。

Thrift支持多種消息格式:JSON、二進制和壓縮二進制。二進制比JSON更高效,因為二進制解碼更快。同樣原因,壓縮二進制格式可以提供更高級別的壓縮效率。JSON,是易讀的。Thrift也可以在裸TCP和HTTP中間選擇,裸TCP看起來比HTTP更加有效。然而,HTTP對防火墻,瀏覽器和人來說更加友好。
消息格式
了解完HTTP和Thrift后,我們來看下消息格式方面的問題。如果使用消息系統或者REST,就可以選擇消息格式。其它的IPC機制,例如Thrift可能只支持部分消息格式,也許只有一種。無論哪種方式,我們必須使用一個跨語言的消息格式,這非常重要。因為指不定哪天你會使用其它語言。

有兩類消息格式:文本和二進制。文本格式的例子包括JSON和XML。這種格式的優點在于不僅可讀,而且是自描述的。在JSON中,一個對象就是一組鍵值對。類似的,在XML中,屬性是由名字和值構成。消費者可以從中選擇感興趣的元素而忽略其它部分。同時,小幅度的格式修改可以很容器向后兼容。

XML文檔結構是由XML schema定義的。隨著時間發展,開發者社區意識到JSON也需要一個類似的機制。一個選擇是使用JSON Schema,要么是獨立的,要么是例如Swagger的IDL。

基于文本的消息格式最大的缺點是消息會變得冗長,特別是XML。因為消息是自描述的,所以每個消息都包含屬性和值。另外一個缺點是解析文本的負擔過大。所以,你可能需要考慮使用二進制格式。

二進制的格式也有很多。如果使用的是Thrift RPC,那可以使用二進制Thrift。如果選擇消息格式,常用的還包括 Protocol Buffers Apache Avro 。它們都提供典型的IDL來定義消息架構。一個不同點在于Protocol Buffers使用的是加標記(tag)的字段,而Avro消費者需要知道模式(schema)來解析消息。因此,使用前者,API更容易演進。這篇 博客 很好的比較了Thrift、Protocol Buffers、Avro三者的區別。

總結

微服務必須使用進程間通信機制來交互。當設計服務的通信模式時,你需要考慮幾個問題:服務如何交互,每個服務如何標識API,如何升級API,以及如何處理部分失敗。微服務架構有兩類IPC機制可選,異步消息機制和同步請求/響應機制。在下一篇文章中,我們將會討論微服務架構中的服務發現問題。

原文鏈接:Building Microservices: Inter-Process Communication in a Microservices Architecture(翻譯:楊峰 校對:李穎杰)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/449727.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/449727.shtml
英文地址,請注明出處:http://en.pswp.cn/news/449727.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python輸出與刪除某行或某列

python輸出字符,主要為結合變量形成新的變量名 year 2016 event Referendum fResults of the {year} {event}Results of the 2016 Referendum yes_votes 42_572_654 no_votes 43_132_495 percentage yes_votes / (yes_votes no_votes) {:-9} YES votes {:2…

為什么應該用模塊取代C/C++中的頭文件?

摘要:本文整理自Apple C工程師Doug Gregor的演講Slide,他表示希望使用模塊(Module)這一概念替代C/C中的頭文件,現已被C標準化委員會任命為Module研究組的主席,研究該提議的可能性。考慮到Apple的開源項目LL…

北向資金進行股票、期貨指數、基金策略

#%%導入包 import tushare as ts import datetime import pandas as pd import numpy as np import akshare as ak import warnings warnings.filterwarnings("ignore")#獲取北向資金數據 df_data2 ak.stock_em_hsgt_north_acc_flow_in(indicator"北上") d…

Kong Api 初體驗、Kong安裝教程

見:https://blog.csdn.net/forezp/article/details/79383631Kong是一個可擴展的開源API層(也稱為API網關或API中間件)。 Kong運行在任何RESTful API的前面,并通過插件擴展,它們提供超出核心平臺的額外功能和服務。 Kon…

Spring Boot2.0+中,自定義配置類擴展springMVC的功能

在spring boot1.0,我們可以使用WebMvcConfigurerAdapter來擴展springMVC的功能,其中自定義的攔截器并不會攔截靜態資源(js、css等)。 Configuration public class MyMvcConfig extends WebMvcConfigurerAdapter {Overridepublic v…

從谷歌宕機事件認識互聯網工作原理

摘要:谷歌服務器經歷了短暫的宕機事件,持續大概27分鐘,對部分地區的互聯網用戶造成了影響。此次事件的原因深究起來需要進入互聯網絡那深邃的、黑暗的角落。 譯者注:本文中提到CloudFlare是一家總部位于美國舊金山的內容分發網絡(…

聊聊技術寫作的個人體會

有群友問過,是什么原因使我開始寫技術公眾號,又是什么動力讓我堅持寫的。 在我看來,寫作是一件不能敷衍的事,通過寫作來學習,反而要比單純地學習的效果要好。為了寫成一篇“拿得出手”的文章,我要反復查找資…

「2019冬令營提高組」全連

傳送門 顯然的 $dp$ 設 $f[i]$ 表示點擊第 $i$ 個音符時的最大價值,$t[i]$ 表示音符 $i$ 的準備時間 那么可以枚舉 $1$ 到 $i-t[i]$ 的所有音符,如果 $j$ ,如果 $jt[j]$ 小于等于 $i$ ,那么 $f[i]max(f[i],f[j]t[i]*val[i])$ 考慮…

Docker常用命令、超實用、講解清晰明了(rm、stop、start、kill、logs、diff、top、cp、restart ...)

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. 查看docker信息(version、info) # 查看docker版本 $docker version # 顯示docker系統的信息 $docker i…

推薦給開發人員的實用命令行工具

摘要:優秀的工具對于定位問題出在何處有著無可估量的價值,而且能在一開始就幫助我們阻止問題的出現,總的來說能使我們的工作更有效率。本文介紹了6個非常強大且靈活的工具,熟練使用這些工具能使你的生活變得更輕松一些。 作為一名…

雷軍:啟動手機+AIoT雙引擎戰略 5G春天到來前打持久戰

雷帝網 樂天 1月11日報道 小米CEO雷軍今日在小米年會上宣布,2019年,小米將正式啟動“手機AIoT”雙引擎戰略,這將是小米未來五年的核心戰略。未來5年,小米將在AIoT領域持續投入超過100億元。從2019年起,AIoT&#xff0c…

Jenkins自定義主題

x下載自定義樣式 http://afonsof.com/jenkins-material-theme/ 打開連接 最后點擊:DOWNLOAD TOUR THEME! 得到樣式文件:jenkins-material-theme.css 上傳樣式文件到jenkins 將jenkins-material-theme.css 上傳到: /var/jenkins_home/userCont…

SSH (Secure Shell)詳解

Secure Shell(SSH)是一種加密 網絡協議,用于在不安全的網絡上安全地運行網絡服務。 SSH通過客戶端 - 服務器體系結構中的不安全網絡提供安全通道,將SSH客戶端應用程序與SSH服務器相連接。 常見的應用程序包括遠程命令行登錄和遠程…

股票配對收益

import pandas as pd import numpy as npimport matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 字體設置 import matplotlib matplotlib.rcParams[axes.unicode_minus]False # 負號顯示問題from arch.unitroot import ADF …

YUV420、YUV422、RGB24轉換

//平面YUV422轉平面RGB24 static void YUV422p_to_RGB24(unsigned char *yuv422[3], unsigned char *rgb24, int width, int height) { int R,G,B,Y,U,V; int x,y; int nWidth width>>1; //色度信號寬度 for (y0;y<height;y) { for (x0;x<width;x) { …

最長非下降子序列(O(nlogn))(offer收割)

題目 如題 思路 核心思想是&#xff0c;維護一個數組ends&#xff0c;它記錄了長度為k的子序列的末尾元素的最小值。聽起來很抽象&#xff0c;我們不妨手動演示一遍整個過程。 假設數組a{2,9,4,27,29,15,7}&#xff0c;令length表示當前找到的最長非下降子序列的長度。初始時le…

[Python]小甲魚Python視頻第026課(字典:當索引不好用時2)課后題及參考解答

# -*- coding: utf-8 -*- """ Created on Fri Mar 8 10:32:20 2019author: Administrator """"""測試題&#xff1a;0. Python的字典是否支持一鍵&#xff08;Key&#xff09;多值&#xff08;Value&#xff09;&#xff1f;不支…

2021-08-12 畫蠟燭線

畫蠟燭線 pip install https://github.com/matplotlib/mpl_finance/archive/master.zip from mpl_finance import candlestick_ochl import matplotlib.pyplot as plt from matplotlib.pylab import date2num# 先畫日K線 fig, axes plt.subplots(nrows1, ncols1, figsize(20, …

替換字符串列表中字符串

//替換字符串列表中字符串 procedure StringsReplace(var S : TStrings; OldPattern, NewPattern: string; Flags: TReplaceFlags);var i : integer; tmpstr : string;begin for i : 0 to S.Count -1 do begin tmpstr : S[i]; s[i] : StringReplace(tmpstr, Ol…

TCP/IP協議族 詳解(TCP/IP四層模型、OSI七層模型)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 TCP/IP協議族&#xff08;TCP/IP Protocol Suite&#xff0c;或TCP/IP Protocols&#xff09;&#xff0c;簡稱TCP/IP。由于在網絡通訊協…