【docker&spring cloud】微服務化改造

天府雲創發表於2018-01-12

微服務化改造系列之一:總覽

1 寫在前面

背景

技術圈流行一句話,凡脫離業務談架構的,都是耍流氓。作為微服務改造系列的第一篇部落格,首先介紹一下實施這次技術改造的背景。

第一,我所在公司(簡稱XR)的後臺服務採用的主技術棧是Scala,雖然開發效率很高,但也帶來一系列的副作用。1.由於Scala語言強大的表達能力和豐富的函式式特性,很容易寫出俗稱“義大利麵條”式的程式碼,一個類檔案動輒上千行,程式碼的可讀性非常差,導致可維護性也很差。2.編譯Scala原始碼時首先需要將Scala原始碼轉換成Java原始碼然後再通過JVM進行編譯,加上隱式型別的存在進一步拖慢了編譯期間的型別推導,Scala的編譯速度比Java足足慢了一個數量級,這個差異在程式碼量少的時候還不明顯,但隨著程式碼量的上升,就成了團隊的一個nightmare,試想本地全量編譯一次需要10+分鐘。3.Scala小眾語言的標籤決定了Scala程式設計師的稀缺性,晦澀難懂的官方文件拔高了學習曲線,後果就是高昂的招聘成本和漫長的培養時間。以上這些副作用不但抵消了先期開發效率上的優勢,而且使得對新需求的響應能力越來越慢,技術負債也越壘越高。

第二,歷經2年多的產品迭代,整個後臺服務專案越來越龐大,已經成為一個典型意義上的單體應用(也就是Martin Fowler常說的monolithic application):1.各個業務模組犬牙交錯,重複程式碼隨處可見,補丁程式碼越打越多。2.任何一個改動都需要一次全量釋出,哪怕是修改一句文案。

第三,與微服務化改造同時進行的是容器化改造,如果不對上述單體應用進行拆分,很多容器化帶來的好處就會被削弱,甚至毫無意義,比如提高資源利用率(CPU型應用和記憶體型應用搭配部署),異構應用的環境隔離能力等。

侷限

谷歌前研發總監Tiger曾經說過,一個系統的演化一般會經歷三個階段,首先是under-engineer,然後是over-engineer,最後才是right-engineer。考慮到參與此次微服務改造的人員有限(一人主導,多人配合),同時也是團隊第一次嘗試做這類系統性的改造,最後我們決定採取一條比較實用的改良式路線:

  1. 最小化對已有應用的侵入性
  2. 偏好主流的微服務框架
  3. 只做必要的微服務治理

第一條定下了此次改造的基調,降低了方案無法落地的風險,確保了專案的整體可行性。第二條讓我們站在巨人的肩膀上,不重複造輪子,聚焦在問題本身,而不是工具。第三條縮減專案範圍,避免過度工程,以戰養兵,不打無用之仗。

2 微服務簡介

3個關鍵詞

有關微服務的定義,最權威的版本莫屬微服務之父Martin Fowler在microservices一文中所述:

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. – James Lewis and Martin Fowler

注意其中有3個關鍵詞,small,independently deployable和automated deployment。small對應的就是微服務的微,很多初次接觸微服務的同學對微的理解往往會停留在實現層面,以為程式碼少就是微,但實際上,這裡的微更多的是體現在邏輯層面。微服務的一個重要設計原則是share as little as possible,什麼意思呢?就是說每個微服務應該設計成邊界清晰不重疊,資料獨享不共享,也就是我們常說的高內聚、低耦合。保證了small,才能做到independently deployable。而實現automated deployment的關鍵是DevOps文化,可參見Fowler另一篇談DevOps的文章。

需要提醒的是,隨著業務複雜度的上升,一個微服務可能需要拆分為更多更細粒度的微服務,比方說,一開始只是一個簡單的訂單服務,後面逐步拆分出清算,支付,結算,對賬等其他服務。

康威定律

與單體應用拆分為微服務的過程類似,隨著公司規模的不斷擴大,一個組織勢必會分化出多個更小的組織。根據康威定律,組織結構決定系統結構,因此,從這個層面來說,微服務也是一種必然。

康威定律(Conway’s Law):“Any organization that design a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure. - Melvin Conway, 1968

取捨

從本質上來看,相對單體應用,微服務是以犧牲強一致性、提高部署複雜性為代價,換取更徹底的分散式特性,比如異構性和強隔離性。對應CAP理論,就是用Consistency換Partition。異構性比較容易理解,通過定義統一的API規範(一般採用REST風格),每個微服務團隊可以根據各自的能力矩陣選用最適合的技術棧,而不是所有人必須使用相同的技術棧。強隔離性指的是,對於一個典型的單體應用,隔離性最高只能體現到模組級別,由於共享同一個程式碼倉庫,模組的邊界往往比較模糊,需要人為定義很多規範來保證良好的隔離性,但無論如何強調,稍一疏忽,就會產生“越界”行為,時間愈長,維護隔離性的成本愈高。而到了微服務階段,自帶應用級別的隔離性,“越界”的成本大大提升,無需任何規範,架構本身就保證了隔離性。

另一方面,由於採用了分散式架構,微服務無法再簡單的通過資料庫事務來保證強一致性,而是通過訊息中介軟體或者某種事務補償機制來保證最終一致性,比如微信朋友圈的點贊,淘寶訂單的物流狀態。其次,在微服務階段,隨著應用數量的激增,一次釋出往往涉及多個應用,加上異構性帶來的部署方式的多樣性,對團隊的運維水平尤其是自動化水平提出了更高的要求,運維和開發的邊界進一步模糊。

領域知識

除了組織架構和技術取捨,領域知識是另一個非常重要的決策因素。對於不熟悉的業務領域,很難第一次就把各個微服務的邊界和介面定義正確,一旦開始開發,重構成本就會非常可觀。反過來說,當對領域知識有了一定的積累,再重構一個單體應用就會容易的多。

小結

綜上所述,雖然微服務看上去很美,但在決定採用微服務架構之前,不僅要仔細考量團隊的技術水平(包括知識結構,理論深度,經驗積累和技術氛圍),還應綜合考慮專案的時間範圍,領域知識的熟悉程度,以及所在組織的規模架構。除非這些前提條件都滿足,否則單體應用是更適合的選擇,就像Fowler建議的那樣。

3 微服務化總覽

上圖是XR微服務化第一階段的整體架構圖。可以看到,一些支撐微服務的必要元件都已包含其中:

  • 服務註冊中心:所有服務註冊到Consul叢集,整合Nginx實現負載均衡,使用Hystrix實現簡單的服務降級和熔斷機制
  • CI/CD:利用Jenkins Pipeline實現不停機發布
  • 日誌平臺:擴充套件ELK加上Redis快取
  • 配置中心:使用自研的Matrix系統,最小化對已有應用的侵入性,保證異構系統的相容性
  • 授權中心:基於Spring Security OAuth,同時支援SSO
  • 訊息中心:選用RabbitMQ作為訊息中介軟體
  • 監控平臺:利用Consul API獲取服務狀態,通過Zookeeper觸發告警

在微服務化系列的後續文章中,我會針對服務註冊、配置中心和授權中心分別展開介紹實施過程中的一些細節和經驗。敬請期待。

參考


微服務化改造系列之二:服務註冊中心

服務註冊中心概述

這篇文章是微服務化改造系列的第二篇,主題是服務註冊中心。作為微服務架構最基礎也是最重要的元件之一,服務註冊中心本質上是為了解耦服務提供者和服務消費者。對於任何一個微服務,原則上都應存在或者支援多個提供者,這是由微服務的分散式屬性決定的。更進一步,為了支援彈性擴縮容特性,一個微服務的提供者的數量和分佈往往是動態變化的,也是無法預先確定的。因此,原本在單體應用階段常用的靜態LB機制就不再適用了,需要引入額外的元件來管理微服務提供者的註冊與發現,而這個元件就是服務註冊中心。

設計或者選型一個服務註冊中心,首先要考慮的就是服務註冊與發現機制。縱觀當下各種主流的服務註冊中心解決方案,大致可歸為三類:

  • 應用內:直接整合到應用中,依賴於應用自身完成服務的註冊與發現,最典型的是Netflix提供的Eureka
  • 應用外:把應用當成黑盒,通過應用外的某種機制將服務註冊到註冊中心,最小化對應用的侵入性,比如Airbnb的SmartStack,HashiCorp的Consul
  • DNS:將服務註冊為DNS的SRV記錄,嚴格來說,是一種特殊的應用外註冊方式,SkyDNS是其中的代表

注1:對於第一類註冊方式,除了Eureka這種一站式解決方案,還可以基於ZooKeeper或者Etcd自行實現一套服務序號產生器制,這在大公司比較常見,但對於小公司而言顯然價效比太低。

注2:由於DNS固有的快取缺陷,本文不對第三類註冊方式作深入探討。

除了基本的服務註冊與發現機制,從開發和運維角度,至少還要考慮如下五個方面:

  • 測活:服務註冊之後,如何對服務進行測活以保證服務的可用性?
  • 負載均衡:當存在多個服務提供者時,如何均衡各個提供者的負載?
  • 整合:在服務提供端或者呼叫端,如何整合註冊中心?
  • 執行時依賴:引入註冊中心之後,對應用的執行時環境有何影響?
  • 可用性:如何保證註冊中心本身的可用性,特別是消除單點故障?

以下就圍繞上述幾個方面,簡單分析一下Eureka,SmartStack,Consul的利弊。

Eureka

從設計角度來看,Eureka可以說是無懈可擊,註冊中心、提供者、呼叫者邊界清晰,通過去中心化的叢集支援保證了註冊中心的整體可用性,但缺點是Eureka屬於應用內的註冊方式,對應用的侵入性太強,且只支援Java應用。

SmartStack

SmartStack可以說是三種方案中最複雜的,涉及了ZooKeeper、HAProxy、Nerve和Synapse四種異構元件,對運維提出了很高的要求。它最大的好處是對應用零侵入,且適用於任意型別的應用。

Consul

Consul本質上屬於應用外的註冊方式,但可以通過SDK簡化註冊流程。而服務發現恰好相反,預設依賴於SDK,但可以通過Consul Template(下文會提到)去除SDK依賴。

最終方案

最終我們選擇了Consul作為服務註冊中心的實現方案,主要原因有兩點:

  1. 最小化對已有應用的侵入性,這也是貫穿我們整個微服務化改造的原則之一
  2. 降低運維的複雜度,Consul Agent既可以執行在伺服器模式,又可以執行在客戶端模式

Consul Template

上文提到使用Consul,預設服務呼叫者需要依賴Consul SDK來發現服務,這就無法保證對應用的零侵入性。所幸通過Consul Template,可以定時從Consul叢集獲取最新的服務提供者列表並重新整理LB配置(比如nginx的upstream),這樣對於服務呼叫者而言,只需要配置一個統一的服務呼叫地址即可。改造後的呼叫關係如下:

Spring Cloud Consul

由於我們選用了Spring Boot作為統一的微服務實現框架,很自然的,可以利用Spring Cloud提供的Consul元件進一步簡化服務註冊流程,省去額外的服務提供端的Consul配置。

參考


微服務化改造系列之三:配置中心


配置中心概述

這篇文章是微服務化改造系列的第三篇,主題是配置中心。上一篇我們談到服務註冊中心,即通過提供某種註冊和發現的機制,解決服務互通的問題。那麼問題來了,一個服務如何知道服務註冊中心的地址呢?這就涉及到服務配置了。我們知道,大至一個PaaS平臺,小至一個快取框架,一般都依賴於特定的配置以正常提供服務,微服務也不例外。

配置分類

  • 按配置的來源劃分,主要有原始碼(俗稱hard-code),檔案,資料庫和遠端呼叫。
  • 按配置的適用環境劃分,可分為開發環境,測試環境,預釋出環境,生產環境等。
  • 按配置的整合階段劃分,可分為編譯時,打包時和執行時。編譯時,最常見的有兩種,一是原始碼級的配置,二是把配置檔案和原始碼一起提交到程式碼倉庫中。打包時,即在應用打包階段通過某種方式將配置(一般是檔案形式)打入最終的應用包中。執行時,是指應用啟動前並不知道具體的配置,而是在啟動時,先從本地或者遠端獲取配置,然後再正常啟動。
  • 按配置的載入方式劃分,可分為單次載入型配置和動態載入型配置。

演變

隨著業務複雜度的上升和技術架構的演變,對應用的配置方式也提出了越來越高的要求。一個典型的演變過程往往是這樣的,起初所有配置跟原始碼一起放在程式碼倉庫中;之後出於安全性的考慮,將配置檔案從程式碼倉庫中分離出來,或者放在CI伺服器上通過打包指令碼打入應用包中,或者直接放到執行應用的伺服器的特定目錄下,剩下的非檔案形式的關鍵配置則存入資料庫中。上述這種方式,在單體應用階段非常常見,也往往可以執行的很好,但到了微服務階段,面對爆發式增長的應用數量和伺服器數量,就顯得無能為力了。這時,就輪到配置中心大顯身手了。那什麼是配置中心?簡單來說,就是一種統一管理各種應用配置的基礎服務元件。

框架選型

選型一個合格的配置中心,至少需要滿足如下4個核心需求:

  • 非開發環境下應用配置的保密性,避免將關鍵配置寫入原始碼
  • 不同部署環境下應用配置的隔離性,比如非生產環境的配置不能用於生產環境
  • 同一部署環境下的伺服器應用配置的一致性,即所有伺服器使用同一份配置
  • 分散式環境下應用配置的可管理性,即提供遠端管理配置的能力

現在開源社群主流的配置中心框架有Spring Cloud Config和disconf,兩者都滿足了上述4個核心需求,但又有所區別。

Spring Cloud Config

Spring Cloud Config可以說是一個為Spring量身定做的輕量級配置中心,巧妙的將應用執行環境對映為profile,應用版本對映為label。在服務端,基於特定的外部系統(Git、檔案系統或者Vault)儲存和管理應用配置;在客戶端,利用強大的Spring配置系統,在執行時載入應用配置。

disconf

disconf是前百度資深研發工程師廖綺綺的開源作品。在服務端,提供了完善的操作介面管理各種執行環境,應用和配置檔案;在客戶端,深度整合Spring,通過Spring AOP實現應用配置的自動載入和重新整理。

最終方案

不管是Spring Cloud Config還是disconf,預設提供的客戶端都深度繫結了Spring框架,這對非Spring應用而言無疑增加了整合成本,即便它們都提供了獲取應用配置的API。最終我們還是選用了微服務化改造之前自研的Matrix作為配置中心,一方面,可以保持新老系統使用同一套配置服務,降低維護成本,另一方面,在滿足4個核心需求的前提下,Matrix還提供了一些獨有的能力。

  • 分離配置檔案和配置項。對於配置檔案,通過各類配套打包外掛(sbt, maven, gradle),在打包時將配置檔案打入應用包中,同時最小化對CI的侵入性;對於配置項,提供SDK,幫助應用從服務端獲取配置項,同時支援簡單的快取機制。
  • 增加應用版本維度,即對於同一應用,可以在服務端針對不同版本或版本區間維護不同的應用配置。
  • 應用配置的版本化支援,類似於Git,可以將任一應用配置回退到任一歷史版本。

進一步資訊可參考我之前寫的Matrix設計文件

Matrix架構圖

下一篇我將給大家介紹微服務架構的另一個基礎元件——授權中心,敬請期待!

參考

微服務化改造系列之四:授權中心

授權中心概述

這篇文章是微服務化改造系列的第四篇,主題是授權中心。有了服務註冊中心和配置中心,下一步應該就可以發起服務呼叫了吧?Wait, 還有一個關鍵問題要解決。不同於單體應用內部的方法呼叫,服務呼叫存在一個服務授權的概念。打個比方,原本一家三兄弟住一屋,每次上山打獵喊一聲就行,後來三兄弟分了家,再打獵就要挨家挨戶敲門了。這一敲一應就是所謂的服務授權。

嚴格來說,服務授權包含鑑權(Authentication)和授權(Authorization)兩部分。鑑權解決的是呼叫方身份識別的問題,即敲門的是誰。授權解決的是呼叫是否被允許的問題,即讓不讓進門。兩者一先一後,缺一不可。為避免歧義,如不特殊指明,下文所述授權都是寬泛意義上的授權,即包含了鑑權。

常見的服務授權有三種,簡單授權,協議授權和中央授權。

  • 簡單授權:服務提供方並不進行真正的授權,而是依賴於外部環境進行自動授權,比如IP地址白名單,內網域名等。這就好比三兄弟互相留了一個後門。
  • 協議授權:服務提供方和服務呼叫方事先約定一個金鑰,服務呼叫方每次發起服務呼叫請求時,用約定的金鑰對請求內容進行加密生成鑑權頭(包含呼叫方唯一識別ID),服務提供方收到請求後,根據鑑權頭找到相應的金鑰對請求進行鑑權,鑑權通過後再決定是否授權此次呼叫。這就好比三兄弟之間約定敲一聲是大哥,敲兩聲是二哥,敲三聲是三弟。
  • 中央授權:引入獨立的授權中心,服務呼叫方每次發起服務呼叫請求時,先從授權中心獲取一個授權碼,然後附在原始請求上一起發給服務提供方,提供方收到請求後,先通過授權中心將授權碼還原成呼叫方身份資訊和相應的許可權列表,然後決定是否授權此次呼叫。這就好比三兄弟每家家門口安裝了一個110聯網的指紋識別器,通過遠端指紋識別敲門人的身份。

一般來說,簡單授權在業務規則簡單、安全性要求不高的場景下用的比較多。而協議授權,比較適用於點對點或者C/S架構的服務呼叫場景,比如Amazon S3 API。對於網狀結構的微服務而言,中央授權是三種方式中最適合也是最靈活的選擇:

  1. 簡化了服務提供方的實現,讓提供方專注於許可權設計而非實現。
  2. 更重要的是提供了一套獨立於服務提供方和服務呼叫方的授權機制,無需重新發布服務,只要在授權中心修改服務授權規則,就可以影響後續的服務呼叫。

OAuth

說起具體的授權協議,很多人第一反應就是OAuth。事實上也的確如此,很多網際網路公司的開放平臺都是基於OAuth協議實現的,比如Google APIs微信網頁授權介面。一次標準的OAuth授權過程如下:

對應到微服務場景,服務提供方相當於上圖中的Resource Server,服務呼叫方相當於Client,而授權中心相當於Authorization Server和Resource Owner的合體。

想了解更多關於OAuth的資訊,可移步OAuth2或者OAuth2中文版

Beared Token

在標準的OAuth授權過程中,Resource Server收到Client發來的請求後,需要到Authorization Server驗證Access Token,並獲取Client的進一步資訊。通過OAuth 2.0版本引入中的Beared Token,我們可以省去這一次呼叫,將Client資訊存入Access Token,並在Resource Server端完成Access Token的鑑權。主流的Beared Token有SAMLJWT兩種格式,SAML基於XML,而JWT基於JSON。由於大多數微服務都使用JSON作為序列化格式,JWT使用的更為廣泛。

框架選型

在選型OAuth框架時,我主要調研了CAS,Apache Oltu,Spring Security OAuthOAuth-Apis,對比如下:

不考慮實際業務場景,CAS和Spring Security OAuth相對另外兩種框架,無論是整合成本還是可擴充套件性,都有明顯優勢。前文提到,由於我們選用了Spring Boot作為統一的微服務實現框架,Spring Security OAuth是更自然的選擇,並且維護成本相對低一些(服務端)。

最終方案

最後我們基於Spring Security OAuth框架實現了自己的服務授權中心,鑑權部分做的比較簡單,目前只支援私網認證。大致的服務授權流程如下:

值得一提的是,除了服務呼叫,我們的服務授權中心還增加了SSO的支援,通過微信企業號實現各個服務後臺的單點登入/登出,以後有機會再詳細介紹。

冰山一角

至此,這個微服務化改造系列就算告一段落,等以後有了更多的積累,我會繼續寫下去。微服務是一個很大的話題,自Martin Fowler於2014年3月提出以來,愈演愈熱,並跟另一個話題容器化一起開創了一個全新的DevOps時代,引領了國內外大大小小各個網際網路公司的技術走向,也影響了我們這一代程式設計師尤其是後端和運維的思維方式。從這個角度說,我寫這個微服務化改造系列文章也是偶然中的必然,希望能給讀過這些文章的你帶來一些新的啟發和思考。如果你對微服務也感興趣或者有一些心得想跟我交流,歡迎在讚賞榜上留下你的微訊號。

少年讀書如隙中窺月,中年讀書如庭中望月,老年讀書如臺上玩月,皆以閱歷之淺深為所得之淺深耳。– 張潮 《幽夢影》

參考




相關文章