在最近的一些部落格裡我們解釋了採用四層的架構對於開發和部署微服務的應用程式是很重要的。


如果你仍然採用十年前的開發流程和應用架構,你不能很快地獲取和滿足移動端使用者的需求,移動端使用者可以從越來越多的APP中進行選擇。

向微服務架構的轉換給市場上的公司帶來了很多的機會。對於系統架構和開發人員,它在為使用者提供新的使用者體驗的同時又帶來了一種前所未有的控制力和速度。但在現在這樣緊張的節骨眼上,感覺上是不允許出一點差錯的。現實世界中,你不可能革新期間就停止APP的開發和部署的。你深深明白未來的成功取決於能否成功遷移到微服務架構中來,但是你該怎麼來做呢?

幸運的是,微服務的早期實踐者本著開源的精神慷慨地分享他們的專業知識,不僅有開原始碼,也會在會議上做一些演講,寫一些部落格。Netflix就是其中之一。Adrian Cockcroft作為web工程和雲端計算架構師總監負責監督了公司內負責DVD租賃系統的100人的工程師團隊,從傳統開發模式到只需要很少人員負責數百個後臺服務的微服務架構來為數百萬的Nrtflix客戶提供數字娛樂服務。

Battery Ventures公司的技術人員Cockcroft是微服務和雲架構方面著名的佈道者,目前供職於Nginx技術諮詢委員會。

什麼是微服務架構?

Cockcroft 把微服務架構定義為:由鬆耦合的有相應語境的元素構成的一種面向服務的架構,鬆耦合意味著你可以獨立更新這些服務。更新其中一個服務並不會改變其他的服務。

如果你的系統裡有大量的特殊服務,但是又必須同時更新它們,它們又不是微服務,因為它們不是鬆耦合的。在向微服務遷移的時候人們常常會把資料庫的耦合看的過重,也就是所有服務都連的是同一個資料庫,更新其中一個服務就意味著要改變資料庫的schema。這種情況你需要對資料庫進行拆分。

bounded contexts的概念來源於Eric Evans的書 Domain Driven Design 。就軟體開發的目的而言,擁有恰當語境的微服務本身是自包含的。由於微服務和其他微服務之間互動是嚴格通過API來進行的,你不需要共享資料結構、資料庫表結構和物件的內部表達形式,在不瞭解其他服務的內部結構的情況下你也可以理解和更新一個微服務的程式碼。

如果你開發的是網際網路應用,你已經很熟悉這些概念了,實際上只不過用的不是同樣的叫法。大多數移動APP都用到了一些後臺服務,這樣使用者可以在APP裡實現Facebook裡共享、從Google Map裡得到地理位置、在Foursquare找到一家餐館。假如你的APP與這些服務是緊耦合的,這樣你在更新之前必須與開發團隊進行協調來確保你的更新不會破壞任何東西。

在使用微服務架構時,你要把其他的開發團隊看作是這些後臺服務:也就是那些你的微服務通過API互動的外部服務。微服務之間最通用的協議就是它們的API足夠穩定,也是向前相容的。

就跟Google Map 的API不可能事先提醒就進行更改是不能所接受的,這樣的話,你是API可以演進,但是必須要與之前的版本相容

微服務架構設計的最佳實踐

Cockcroft解釋他在Netflix的職位是雲架構師,他的職責不是管理架構,而是發現和標準化公司工程師所形成的架構。Netflix開發團隊提出了幾條設計和實現微服務架構的最佳實踐。

每個微服務的資料單獨儲存

不同微服務不要使用同一個後臺資料儲存。讓開發團隊選擇適合每個微服務的資料庫。或許,不同團隊使用同樣的資料結構來儲存會減少工作量,但當其中某個團隊想要更改資料結構的時候,其他服務不得不跟著改變。

資料的拆分會使得資料管理異常複雜,是因為單獨的儲存系統不容易同步,易於出現不一致的情況,外來鍵也會發生意外的改變。你需要一個後臺執行的主資料管理的工具來發現和修復不一致的情況。比如,你需要檢查每個儲存訂閱者ID的資料庫來確保所有的ID都是同一個。這個工具可以自己寫或者直接買。很多商用的關係型資料庫都提供此類核對,它常常過於耦合,不能支援很好的伸縮性。

使用類似程度的成熟度來維護程式碼

微服務中所有程式碼都保持同樣的類似程度的成熟度和穩定度。也就是說,你想要重寫或給一個執行良好的已部署好的微服務新增一些程式碼的話,最好的方式常常是對於新的或要改動的程式碼,新建一個微服務,現有的微服務丟著不管就行。 [編輯注:這種架構常常稱之為immutable infrastructure principle.] 這樣的話,你可以迭代式的部署和測試新程式碼,直至沒有bug,效能足夠好,現有的微服務不會出現故障或效能下降。一旦新的微服務和原始的服務一樣穩定,如果確實需要進行功能合併的話,你可以將其合併在一起,或者處於效能的考慮合併它們。然而,就Cockcroft的經驗來講,常常是你發現你的服務太大而要進行拆分。

每個微服務都單獨進行編譯構建

每個微服務都單獨進行編譯構建,這樣你就從程式碼庫裡某個版本中抽取單獨的元件。這樣,你可以拿到多個類似檔案的微服務,但卻是不同的版本的。這樣如果要對codebase進行清理會比較麻煩,但對於在新建微服務時新增新檔案時的便利性的話,是值得的。The asymmetry is intentional: 你想要引入新的微服務、檔案或者功能,很容易又不會存在風險。

部署到容器之中

將微服務部署到容器中很重要是因為這意味著你需要一款部署的工具。只要一個微服務是在容器之中,該工具就應該知道如何部署。無論是那種容器都沒有關係。也就是說,Docker看起來很快會成為容器的行業標準。

將伺服器看做是無狀態的

將那些特別是部署了客戶端程式碼的伺服器視作是可替換的一組之中的一個。這些伺服器的功能都是一樣的,你無須關心某一個。只需要關心要實現你的目標是否數量足夠,你可以使用自動伸縮來按需調整數目。如果其中一個伺服器宕機了,可以由其他一個替換。避免了那些單個伺服器完成特殊功能的系統中存在的雪崩現象。

Cockcroft打了個比方,你把伺服器看做奶牛而不是寵物。如果生產系統中某個伺服器負責某個特殊的功能,你通過名稱認識這個伺服器,這個伺服器宕機後大家都會很難過,這也就是一個寵物。相反,如果你把伺服器看作是一些奶牛。你關心的是你每天能擠多少奶,如果有一天你發現今天擠的奶少了,你知道是哪頭牛有問題,你可以換掉它。

本文作者王海生,歡迎關注好雨雲官方微信平臺,獲得有價值的技術分享。