做者:Kyle Galbraith翻譯:瘋狂的技術宅javascript
原文:https://blog.logrocket.com/me...前端
未經容許嚴禁轉載java
在微服務架構的世界中,咱們經過一系列服務構建應用。集合中的每項服務都符合如下標準:程序員
微服務架構中的每一個服務都解決了應用中的業務問題,或至少支持一個。一個團隊對應用中的一個或多個服務負責。面試
微服務架構能夠解鎖許多好處。數據庫
這些好處是微服務愈來愈受歡迎的一個重要緣由。但有一些可能會破壞這些好處的坑。若是不當心掉進去了,你將獲得一個不斷產生技術債的架構。json
微服務之間的通訊就是一個坑,假如不提早考慮就會形成嚴重的破壞。segmentfault
該體系結構的目標是建立鬆散耦合的服務,而且通訊在實現這一目標中起着關鍵做用。在本文中,咱們將重點關注在微服務架構中進行通訊的三種方式,每一種都有其本身的利弊和權衡。api
選擇服務如何相互通訊時,最直接的方式每每是 HTTP。事實上,咱們能夠提出一個案例,即全部通訊渠道都來自這個渠道。可是除此以外,服務之間的 HTTP 調用是服務到服務通訊的可行選擇。服務器
若是咱們的架構中有兩個服務,它可能看起來像這樣: ServiceA
能夠請求並調用 ServiceB
來獲取另外一條信息。
function process(name: string): Promise<boolean> { /** do some ServiceA business logic .... .... */ /** * call ServiceB to run some different business logic */ return fetch('https://service-b.com/api/endpoint') .then((response) => { if (!response.ok) { throw new Error(response.statusText) } else { return response.json().then(({saved}) => { return saved }) } }) }
這是一段很容易理解的適合微服務架構的代碼。 ServiceA
提供了一個業務邏輯。它運行其代碼而後調用 ServiceB
來運行另外一個業務邏輯。在這段代碼中,第一個服務在返回以前完成等待第二個服務完成。
這裏有兩個服務之間進行同步的 HTTP 調用。這是一種可行的通訊模式,但它確實在兩種服務之間創建了耦合。
另外一個選擇是異步 HTTP。這多是這樣的:
function asyncProcess(name: string): Promise<string> { /** do some ServiceA business logic .... .... */ /** * call ServiceB to run some different business logic */ return fetch('https://service-b.com/api/endpoint') .then((response) => { if (!response.ok) { throw new Error(response.statusText) } else { return response.json().then(({statusUrl}) => { return statusUrl }) } }) }
這種變化是微妙的。如今, ServiceB
不返回 saved
屬性,而是返回一個 statusUrl
。這意味着此服務如今正在接收來自第一個服務的請求,而且當即返回一個URL。此 URL 可用來檢查請求的進度。
將兩種服務之間的通訊從同步轉換爲異步,第一個服務再也不停留等待第二個服務完成,而後再返回其工做。
經過這種方法可使服務彼此隔離,而且耦合鬆散。
缺點是須要在第二個服務上建立額外的 HTTP 請求,它從外部進行輪詢,直到請求完成。這也引入了客戶端的複雜性,由於必須檢查請求的進度。
可是,異步通訊容許服務直接保持鬆散耦合。
另外一種通訊模式是基於消息的通訊。
與HTTP通訊不一樣,所涉及的服務不直接相互通訊。相反,服務將消息推送到其餘服務訂閱的消息代理。這消除了許多與 HTTP 通訊相關的複雜性。
它不須要服務知道該如何相互交流,它消除了直接相互調用的服務需求。相反,全部服務都知道消息代理,而且它們將消息推送到該代理。其餘服務能夠訂閱代理中本身關心的消息。
若是咱們的應用在 Amazon Web Services 中,能夠用簡單通知服務(SNS)做爲消息代理。如今 ServiceA
能夠將消息推送到 ServiceB
監聽的 SNS 主題。
function asyncProcessMessage(name: string): Promise<string> { /** do some ServiceA business logic .... .... */ /** * send message to SNS that ServiceB is listening on */ let snsClient = new AWS.SNS() let params = { Message: JSON.stringify({ 'data': 'our message data' }), TopicArn: 'our-sns-topic-message-broker' } return snsClient.publish(params) .then((response) => { return response.MessageId }) }
ServiceB
偵聽 SNS 主題上的消息,當收到一個關心的消息時,就會執行它的業務邏輯。
這引入了它本身的複雜性。請注意,ServiceA
再也不接收狀態 URL 檢查進度。這是由於咱們只知道消息已經被髮送,而不知道 ServiceB
是否已經收到了它。
這能夠經過許多不一樣的方式解決。一種方法是將 MessageId
返回給調用者。能夠用它來查詢 ServiceB
,它將存儲它收到的消息的 MessageId
。
注意,使用此模式的兩個服務之間仍然存在一些耦合。例如,ServiceB
和 ServiceA
必須就消息結構的定義以及其中包含什麼達成一致。
最後一種模式是事件驅動模式。這是另外一種異步方法,它看起來徹底消除了服務之間的耦合。
與消息傳遞模式不一樣,事件驅動方法不須要服務必須知道公共消息結構。服務之間的通訊經過各個服務產生的事件進行。
此處仍然須要消息代理,由於各個服務會將其事件寫入其中。可是與消息方法不一樣,消費服務不須要知道事件的細節,它們對事件的發生作出反應,而不是產生能會或可能不會傳遞的信息。
在形式上,這一般被稱爲「僅事件驅動的通訊」。下面的代碼和消息傳遞方法相似,但推送到SNS的事件是通用的。
function asyncProcessEvent(name: string): Promise<string> { /** do some ServiceA business logic .... .... */ /** * call ServiceB to run some different business logic */ let snsClient = new AWS.SNS() let params = { Message: JSON.stringify({ 'event': 'service-a-event' }), TopicArn: 'our-sns-topic-message-broker' } return snsClient.publish(params) .then((response) => { return response.MessageId }) }
注意,咱們的 SNS 主題消息是一個簡單的 event
屬性。每一個服務都贊成以這種格式將事件推送到代理,這使得通訊鬆散耦合。服務能夠監聽他們關心的事件,而且提供爲響應它們而須要運行的邏輯。
此模式使服務的耦合鬆散,由於事件中不包含任何有效負載。此方法中的每一個服務都會響應事件的發生並運行其業務邏輯。在這裏,咱們經過 SNS 主題發送事件。也可使用其餘事件,例如文件上傳或數據庫行更新。
這些是基於微服務的架構中全部可能的通訊模式嗎?固然不是。基於同步和異步模式進行通訊的方式還有不少種。
可是這三個突出了支持同步與異步的優缺點。在選擇時要考慮耦合因素,但也須要考慮開發和調試的具體狀況與注意事項。