REST 是微服務中最好的架構嗎?

jopen 8年前發布 | 39K 次閱讀 微服務

【編者的話】本文作者Craig Williams通過人們對微服務誤解切入,探討了微服務的各種架構,最終給出開發者的啟示是根據自己的業務邏輯,構建適合自己的微服務架構。

在我的微服務的旅程中,明顯表明大多數關于在線樣例/如何類的文章只專集中在REST作為微服務相互通訊的手段。正是因為如此,你可能會誤認為REST風格的微服務時事實的標準,并努力使用這種方法設計和實施一個基于微服務的系統。事實并非如此。

REST

基于REST服務的例子這么流行不僅僅因為它們的簡單,服務直接通訊和在http之上的相互同步,而且因為它們不需要任何額外的底層架構。

比如考慮一個通知客戶特定產品庫存的系統。這可以通過RESTful實現如下:

REST 是微服務中最好的架構嗎?

  1. 一個外部實體發送一個清單更新請求到一個REST網關地址。
  2. 網關轉發這個請求給清單管理服務。
  3. 清單管理服務基于它接收的請求做清單的更新然后發送一個請求給后端庫存通知服務。
  4. 后臺庫存通知器發送一個請求給訂閱管理器,請求所有的注冊條目已經存儲在后臺庫存中的用戶。
  5. 接著emails通過email服務會輪流給每個用戶發送一個email REST請求。
  6. 每個服務則依次進行響應,退繞回網關并返回結果給客戶端。

應當指出的是,盡管通信是點至點,硬編碼的服務地址將是一個非常糟糕的設計選擇,這極大違背了微服務的設計初衷。至于服務發現機制的使用,像Eureka 或者 Consul,它們機制是服務注冊它們可用的API到一個中心的服務器上,然后客戶端請求一個指定的API從這個服務器。

更深入討論,在這種方式的實現上,也有一些底層的缺陷或事情需要考慮。

阻塞

由于REST的同步的特性,更新庫存操作將不返回,直到通知服務已完成通知所有相關客戶的任務。想象一下這樣做的效果,若果一個特定的產品非常受歡迎,別的庫存的客戶希望1000s被通知到。性能可能被嚴重影響并且可擴展性將會受到阻礙。

‘當一個產品到貨,客戶應當被通知’的認識被根深蒂固的認為應當由清單管理服務來做,但是我不這樣認為。該服務的單一職責應及時更新系統庫(庫存的合并),別無其他。事實上,它不應該甚至不需要了解通知服務的存在。這兩個服務緊密的耦合在這個模型中。

服務何時發布

未來服務失敗時,在這些情況下,基于系統的微服務應該繼續盡可能的發揮作用。由于上面描述的系統是緊耦合的,庫存管理內部需要一個故障策略處理方案(比如)告知后臺庫存通知器實在哪里不可用。可能是庫存更新失敗?可能是服務重試?同樣重要的是,請求到通知的失敗應盡可能快,一些斷路器模式(例如Hystrix)會對此有所幫助。盡管在失敗的情況下將考慮通信的方法來處理,然而把所有邏輯封裝到調用服務將會使調用服務變得臃腫。說回單個服務負責的問題,我的意見是不應該由清單管理負責處理,清單管理負責處理只會使通知器變黑。

管道

克服服務緊耦合的一個方法是把負責的程序從一個微服務移動到如下的企業模式的管道。我們的子系統現在是這樣的:

REST 是微服務中最好的架構嗎?

通信可能一直是基于REST的,但是不在是點到點的;它現在是管道實體來編排的數據流,而不是服務本身負責。這克服了耦合的問題(通過一部管道,阻塞一些工作),它被認為是微服務通訊中的好的做法,盡可能爭取服務的獨立和一致性。采用這種方法,這些服務實體必須依靠第三方實體(管道編排),以便作為一個系統運行,因為沒有特別的自給自足。

比如,通知管道將接受一個單獨相應從后臺訂單通知器(即使有兩個訂閱者),但是必須配置這樣一種方式,它可以解析響應,以便隨后它可以為每個訂閱者發送單個‘發送郵件’請求到郵件通知器。可以這樣認為,電子郵件發送者可以通過單個請求批量修改發送郵件給許多不同的訂閱者,但是如果以此為例,每一個用戶名必須包括郵件體并且還要有某種令牌替換功能。在通知器有特定的知識關于郵件發送者的地方引入了額外的耦合行為。

異步消息

在一個基于消息傳遞系統中,來自服務的輸入和輸出都被定義為命令或者事件。每個服務訂閱它們感興趣的消耗事件,然后當事件通過其他服務被放到隊列后,它通過一個機制像消息隊列/代理可靠地接收事件。

通過這種方法,庫存通知子系統現在能被重構如下:

REST 是微服務中最好的架構嗎?

通過隊列名的共享信息獲得凝聚力,并且提供一致的和眾所周知的命令/事件格式;一個被事件或命令觸發的服務應該能夠被訂閱服務消耗。在這個架構中,獲得了很大的處理靈活性,服務的隔離性和自治性。

拿庫存編目管理來說,它有一個單一的職責,僅僅負責更新編目,一旦它完成它執行的任務就不去關心別的觸發的服務。因此附加服務可以增加消耗庫存更新的事件,而無需修改清單管理服務,或任何管道編排器。

此外,它真的不關心(或者有任何的信息)是否早在后臺訂單通知之前已經死于可怕的死亡;庫存已經更新,這對于庫存管理而言這是一個出色的工作。對于庫存管理服務故障的這種遺忘其實是一件好事;我們必須有一個處理后臺通知器失敗的方案,但是正如我之前說過的,可以說這不是庫存管理本身的責任。

當設計一個基于系統的微服務時需要考慮的最重要的兩件事是處理交易如添加,移除或修改服務不影響別的服務的操作或代碼,以及適當的處理壓力比如服務的失敗。

但是在異步消息傳遞的世界上的一切并非完美無缺,目前仍有一些缺陷需要考慮:

設計/實現/配置困難

和同步編程模型比較起來異步編程模型通常更加復雜,這使得異步編程模型設計和實現起來更加復雜。這是因為有許多可能必須克服額外的問題,如消息排序,重復消息和消息冪等性。另外,消息代理的配置也需要一些考慮。例如,有相同服務的多個實例,那么消息被傳遞到兩個服務或者僅僅只傳遞一個?兩種方案都有使用案例。

異步消息的性質

未立即返回一個動作的結果的事實也可以增加的系統和用戶界面設計的復雜性,在某些情況下它甚至不能清晰的劃分邏輯的意義對于工作在異步方式的子系統。拿后臺庫存通知器為例,他和訂閱管理的關系,如果沒有沒有關于訂閱者應該被通知的消息它是不可能正常工作的,因此在這種情況下一個同REST調用才有意義。這和郵件發送任務是不同的,因此不需要直接發送郵件。

消息流的可視化

由于基于微服務的消息傳遞的分散和自治性,它很難獲得一個完全的清晰的消息流圖在系統內部。和管道的方法比起來,這使得調試起來更加困難,并且系統的業務邏輯也很難管理。

注意:基于時間的消息機制可以進一步擴展通過應用事件源和CQRS模式,但是這超出了本文的討論范圍。可以查看鏈接獲取更多信息。

因此在設計微服務的時候哪種通訊方法是最好的?這與軟件開發(和周期?)的大多數事情是一樣的,取決于具體的需求!如果一個微服務實際需要同步響應,或者如果它自己需要接受一個同步響應,那么REST可能也就需要采用同步通訊方法。如果一個企業要求通過系統的消息流容易被監控和審計,或者考慮到能夠修改和查看消息流從一個中心化的地方的優勢,那么就可以考慮采用一個管道。但是異步基于系統的異步消息擁有松耦合和擴展的特性非常符合微服務的總體精神。通常情況下,盡管有一些顯著的設計和實施的障礙,一個基于事件的消息傳遞方式將根據在微服務為基礎的系統默認的通信機制作出決定時是一個不錯的選擇。

更多閱讀

原文鏈接: is-rest-best-microservices (翻譯:喬要周)

譯者介紹

喬要周,目前就職于Allianz,sensior application administrator 崇尚開源精神,特別癡迷Docker。

來自: http://dockone.io/article/952

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!