微服務架構(一):單一應用架構與微服務架構

工作中使用了微服務架構,接下來的一段時間裡,我會寫一系列的文章來介紹微服務架構,同時我也會在github上寫一個microservices的應用框架(地址會在後續文章給出)。

這篇文章是對 

http://microservices.io/patterns/monolithic.html 和 http://microservices.io/patterns/microservices.html 的翻譯,
內容是比較單一應用架構與微服務架構。

一、單一應用架構

1.上下文

你正在開發一個伺服器端的企業級應用,它必須支援多種不同的客戶端,包括桌面瀏覽器、手機瀏覽器、原生手機APP。這個應用也許還會暴露一個API,給第三方程式去呼叫。它也許還會與其他應用整合,通過web services或者一個訊息代理。 這個應用通過執行業務邏輯、訪問資料庫、與別的系統交換訊息、然後返回一個HTML/JSON/XML響應的方式來處理HTTP或者訊息請求。對於這個應用的不同功能塊, 有不同的邏輯元件與之對應。

2.問題與強制條件

問題: 這個應用的部署架構是怎樣的?
強制條件:
  • 這個專案有一個開發者團隊
  • 新的團隊成員必須快速變得高效工作
  • 這個專案必須易於理解和修改
  • 你想要對這個專案實施持續部署
  • 你必須在多臺機器上執行多個這個專案的複製品,從而達到期望的可伸縮性、可用性
  • 你想要利用新技術(框架、程式語言等)的優勢

3.解決方案與例子

解決方案: 
構建一個單一應用架構的應用,比如一個單一的Java War檔案, 或者一個單一的Rails或者NodeJS程式碼的目錄結構
例子:
假設你正在構建一個電子商務應用, 這個應用從客戶那裡收到訂單,檢驗存貨和客戶的餘額,然後發貨。這個應用由多個元件構成,包括:前端UI,實現了使用者介面; 以及一些後臺服務,用來檢查餘額,維護存貨和發貨。
這個應用以一個單一應用架構部署, 比如是在Tomcat上執行的一個Java web應用。你可以在一個load balance後面執行多個應用例項,以達到提高可用性和可伸縮性的目的:

4. 這種解決方案的結果

這種解決方案有很多好處:

  • 易於開發:現在很多開發工具和IDE的目標都是支援開發單一應用結構的應用
  • 易於部署:你只要在合適的執行時環境上簡單的部署一個WAR檔案(或者目錄結構)
  • 易於伸縮:你可以在一個load balance後面執行多個應用例項,以達到提高可用性和可伸縮性的目的
但是,一旦這個應用變得龐大,團隊規模變大,這種解決方案的缺點變得越來越重大:
  • 巨大的單一應用結構的程式碼嚇壞了開發者,尤其是團隊裡的新人。這個應用變得難以理解和維護。結果是,開發工作變得緩慢,而且,因為沒有嚴格的模組界限,模組化常常會被打破。此外,因為很難去理解如何正確地實現一次變動,程式碼質量下降。

  • IDE負載嚴重:程式碼越多,IDE越慢,開發效率越低。

  • web容器負載嚴重: 專案越大,啟動時間越長。這對於開發效率有很大影響,因為有很多時間都浪費在了等待web容器啟動上面。這也影響了部署。

  • 持續部署很困難:一個龐大的單一應用架構的應用是頻繁部署的障礙。為了更新一個元件,你需要重新部署一整個應用。這會中斷後臺的任務,比如java裡的Quartz job,這可能會導致問題。還有一種可能是,未更新的元件會在啟動時失敗,導致的結果是,重新部署的風險升高,不利於頻繁更新。 這對UI開發者來說尤其是個問題,因為他們經常需要快速迭代和頻繁的重新部署。

  • 伸縮這個專案變得困難:一個單一應用架構的應用只能在一個維度上伸縮。一反面,成交量上升時,可以通過執行更多例項的方法來伸縮,一些雲服務甚至可以按需調節例項個數。但是在另一方面,這種架構難以根據資料量來調節。每個應用例項都會訪問資料,使得快取變得不那麼有效,提高了記憶體消耗和I/O傳輸。同時,不同的元件有不同的資源需求,一個元件可能是CPU密集型,而另一個可能是記憶體密集型。單一應用架構下,我們很難對每個元件分別進行伸縮。

  • 對於調節開發規模是個障礙: 一旦一個單一應用架構的應用達到了一個具體的規模,把團隊分別幾個更小的團隊,專注與不同的功能塊上是有效的。比如說,我們也許想要UI團隊、審計團隊、倉儲團隊等。但是單一應用架構阻礙了各個團隊之間的獨立工作。團隊之間必須共同合作。

  • 需要對一個技術棧有一個長期的承諾: 一個單一應用架構逼迫你與一開始的技術棧捆綁(甚至與某一個技術的具體版本)。在單一應用架構下,很難增量應用新的技術。比如說,你選擇了JVM技術,以後那些用非JVM語言寫的元件在你的專案裡就沒有一席之地了。再比如,你選擇了一個平臺框架,後來這個框架過時了,那就很難去遷移到一個新的更好的框架了。很可能你為了採用新的框架,需要重寫一整個應用,而這是很有風險的。

二、微服務架構

1.上下文

與上文單一應用架構相同

2.問題與強制條件

與上文單一應用架構相同

3.解決方案與例子

解決方案:
定義一個架構,使得這個應用的結構是一系列鬆耦合、互相合作的服務(services)。這個方法對應了伸縮立方體的Y軸。每個服務實現了一系列互相關聯的功能。比如說,一個應用可能由訂單管理服務、客戶管理服務等構成。
服務之間用同步協議比如HTTP/REST或者非同步協議比如AMQP(譯者注: 實現如RabbitMQ等)來進行通訊。 服務可以被獨立的開發和部署。每個服務有自己獨立的資料庫,從而與別的服務解耦。服務之間的資料一致性通過使用事件驅動架構來維護。
例子:一個虛構的電子商務應用
假設你正在構建一個電子商務應用, 這個應用從客戶那裡收到訂單,檢驗存貨和客戶的餘額,然後發貨。這個應用由多個元件構成,包括:前端UI,實現了使用者介面; 以及一些後臺服務,用來檢查餘額,維護存貨和發貨。這個應用由一系列服務構成:

4. 這種解決方案的結果

優點

這個解決方案有很多優點:

  • 每個微服務相對都比較小
    • 易於開發者理解
    • IDE更快
    • 應用啟動更快,利於開發和部署
  • 每個服務可以獨立於其他服務單獨部署,利於頻繁部署新版本

  • 更容易伸縮開發資源。使你可以把開發資源分成多個團隊。每個團隊負責一個或多個服務。

  • 提高了錯誤的隔離。比如說,在一個服務中產生了內容洩露,只有那一個服務會受影響。

  • 不會對一個技術棧產生長期的依賴。在開發一個新服務或者重寫服務時,可以選擇新的技術棧。

缺點

這個解決方案有很多缺點:

  • 開發者需要處理建立一個分散式系統時的額外的複雜度
    • 測試更復雜
    • 開發者必須實現服務間的通訊機制
    • 如果不使用分散式事務,會很難實現跨多個服務的用例
    • 實現跨多個服務的用例需要團隊間的更認真的合作
  • 部署複雜性。在生產環境下,部署或者管理一個微服務系統有運維方面的複雜性。
  • 更高的記憶體消耗。微服務架構需要N*M個例項,而單一架構只需要N個例項。如果每個服務在它自己的JVM上執行, 就需要M倍的JVM執行時。

5.問題

你有很多問題需要解決。

什麼時候使用微服務架構?

使用這種方法的一個挑戰是判斷什麼時候用它是有意義的。當開發一個應用的第一個版本時,你經常不會碰到這個方法所解決的問題。此外,使用一個精細的分散式的架構會拖慢開發進度。這對於一些初創公司來說可能是一個大問題,因為他們的挑戰經常是如何快速的把業務模型實現到應用中。使用Y軸劃分(譯者注:這裡應該指的是採用微服務架構)也許會使得快速迭代更難。但是過一段時間,當挑戰變成了如何伸縮,以及你需要拆分功能塊,這種緊密的依賴也許會使得拆分一個單一應用架構到一系列服務變得困難。

如何把應用拆解成多個服務?

另一個挑戰是決定如何分離系統,成為一系列服務。這更像是一種藝術,但是也有很多策略可以幫到你:

  • 根據業務能力分離, 然後定義一個個服務
  • 根據子域來拆解
  • 根據用例/動詞來拆解。比如說Shipping Service負責訂單的輸送。
  • 根據資源/名詞來拆解。比如說,Account Service負責管理使用者的賬號。

理想情況下,每個服務應該只有一小部分的職責。(Uncle) Bob Martin 談論過使用單一職責原則(SRP)來設計類,使用SRP來設計Service同樣也是講得通的。

另一個幫助設計Service的類比是Unix工具包的設計。Unix提供了很多工具,比如說grep, cat和find。每個工具只做一件事,然後可以和其他工具混合在一起,去做複雜的事情。

如何保持資料一致性?

為了保證鬆耦合,每個service有它自己的資料庫。維護service之間的資料一致性是一個挑戰,因為二階段提交事務/分散式事務並不是很多應用的一個選項。相反的,一個應用必須使用事件驅動架構。一個服務在它的資料變化時,會釋出一個事件。別的服務消費這個事件,然後更新自己的資料。有很多種可靠的資料更新和事件釋出的方法,包括事件源事務日誌跟蹤

如何實現查詢?

另一個挑戰是實現需要從多個服務那裡獲取資料的查詢。一個普遍的做法是使用命令查詢職責分離,維護一個或多個view,它們通過訂閱事件流的方式一直保持最新,事件流中的事件是別的service在資料變化時釋出的。

6.相關的模式

有很多模式與微服務架構相關。這些模式解決了你在採用微服務架構時會碰到的問題。

(PS: 再加上從http://microservices.io/ 上拉下來的一段, 這段就不翻譯了:

How to apply the patterns

Core patterns

Decomposition

Deployment patterns

Cross cutting concerns

Communication style

External API

Service discovery

Reliability

Data management

Security

Testing

Observability

UI patterns