1. 程式人生 > >聊聊微服務架構

聊聊微服務架構

1、微服務架構興起的原因

網際網路時代,在極端情況下,每天都有新需求要開發上線。隨著程式碼量及團隊成員的增加, 傳統單體式架構的弊端日益凸顯,嚴重製約了業務的快速創新和敏捷交付,與網際網路所追求的 “唯快不破”的目標越來越遠。這就是微服務架構興起的時代大背景。

微服務架構( Microservice Architecture) 是近兩年來最流行的架構術語之一,大名鼎鼎的 Martin Flower曾這樣描述它:

“微服務”只不過是滿大街充斥的軟體架構中的一個新名詞而已。儘管我們非常鄙視這樣的東西,但其所描述的軟體風格越來越引起我們的注意。在過去幾年裡,我們發現越來越多的專案開始使用這種風格,以至於我們身邊的同事在構建企業級應用時,理所當然地認為這是一種預設開發形式。然而 ,很不幸,微服務風格是什麼,應該怎麼開發,關於這樣的理論描述卻很難找到。

為什麼微服務架構會如此快速地流行?為什麼越來越多的人理所當然地認為微服務架構是一種預設的開發形式?

為了弄明白上面兩個問題,我們需要先弄明白另外兩個基本問題,即我們通常講的架構是怎樣一種架構,而微服務架構又是怎樣一種架構?下面這張圖給出了兩者間的區別與對比。傳統的應用架構又稱為單體應用(Monolithic), 表現為業務系統的各個模組是緊耦合的關係,各模組執行在一個程序中,每次升級系統基本上都要重啟整個應用程序,如果某個模組有問題,則可能導致整個系統無法正常啟動。而微服務架構則是將業務系統中的不同模組以安裝微服務的方式進行拆分,每個微服務變成一個獨立的Project,獨立編譯並且部署為一個獨立的程序, 每個微服務都可以部署多個獨立的程序對外提供服務,對外的介面方式通常是REST或 者RPC, 此外,不同的微服務程序可以部署到多個伺服器上。我們通過對比就會發現,微服務架構通過將一個龐大的單體程序分解為相互獨立的多個微小程序,以分散式的思想巧妙解決了傳統單體 應用在網際網路時代遭遇的各種問題。所以微服務架構這種新的理念被大家快速接受並且迅速流 行,是有深刻原因的。

這裡寫圖片描述

為了解決傳統的單體架構面臨的挑戰,軟體架構先後演出了SOA架構、RPC架構、分散式服務框架,最後進化為微服務架構。但我們需要牢記一點:軟體開發從來不存在銀彈,因此微服務架構也不是銀彈,它更多是一種架構思想與開發模式的改變,而在具體的實施過程中還存在大量的技術問題及團隊問題需要妥善解決。

微服務架構實施過程中所面臨的最大的一個技術問題就是開發運維過程中的自動化。假如我們要把原來某個中等規模的系統改造為微服務架構,則最少也能拆分出十幾個微服務,於是這麼多微服務程序的編譯、部署、調測及升級就演化成一個浩大的工程了,如果沒有自動化的手段,則微服務化這種勞民傷財的事情恐怕是沒有人願意去推動的。說到自動化,就不得不提容器技術,它是促進微服務架構發展的得力功臣,也是微服務架構得以快速流行的第二個重要原因。

2、容器技術

容器技術其實很早就被一些網際網路公司廣泛使用了,早在Docker興起前的十幾年內,Google就一直採用容器技術支撐著世界上最大的分散式叢集,只是一直對外保守祕密,外界無從知曉,Google直到祭出容器之上的微服務架構神器Kubemetes後,才首次對業界公佈了這個保守了幾十年的最大祕密。Google之所以開源自己的王牌技術,原因之一是容器技術己經被業界公認為IT 界最重要的平臺級技術,如果不能搶佔先機和掌握話語權,就會逐步失去技術領先 性所帶來的市場份額。

我們先來回顧容器技術的代言人— Docker的發展歷史。Docker直到 2014年才釋出1.0 版本,但就在那年,Docker的聲勢就達到了前所未有的程度:它被愛好者冠以“雲端計算新星”“下一代虛擬機器”等稱號,成為2014年最熱門的技術之一。從 2014年年初的B輪融資到該年年末的 DockerCon歐洲大會,Docker在這一年裡順風順水,就連微軟、Google、AWS這樣的巨頭也敬它 三分,彼時,微服務架構、雲端計算、DevOps等技術理念如日中天,而 Docker恰恰可以完美地從技術上驅動這些概念落地,加速這些理念的實施。2015年爆發了容器技術史上最重要的戰爭一分裂之戰,那年CoreOS釋出了自己的容器引擎Rocket,引發容器技術的分裂與統一的大爭論,隨後在 Linux基金會的干預下,Docker公司與CoreOS公司握手言和,成立了 OCI(Open Container Initiative) 標準委員會,它類似當年Java的JCP組織,參與者包括Google、RedHat等巨頭,OCI組織負責制定與領導容器技術標準規範,2016年釋出的Docker1.11成為第一個符合OCI標準的容器引擎。從2014年到2016年的兩年時間裡,Docker經歷了指數級的增長,DockerHub的下載量從1億增加到60億。而2017年最大的事件莫過於Docker公司開始將Docker的版本區分為企業版和社群版,開始面向企業收費,隨後又將Docker開源專案的程式碼遷移到新的Moby專案上。

我們知道,在軟體開發過程中有很多環節是靠人工的,比如搭建環境、釋出安裝包(釋出安裝包到某個FTP伺服器上或者以U盤方式複製)、部署應用、升級系統等,這些過程都比較耗時耗力,很難保證任務的質量與完成時間。在分散式系統的情況下,一旦叢集上了規模,上述人工操作極易因為大量重複性的勞動而導致各種難以排查的錯誤。Docker公司敏銳地察覺到了傳統軟體開發中的上述痛點問題,以創新性的標準化映象(Docker Image)打包釋出應用技術為突破口,成功地定義了 “軟體生命週期中的標準化與自動化”的新標準。下面這張圖給出了Docker的標準化映象的示意圖。

這裡寫圖片描述

Docker映象是一個包含了目標程式所有依賴檔案的一個“All in one”的分層壓縮包,你可以認為它是一個沒有Linux核心但有Linux檔案系統和基礎命令的一個最精簡版虛機,這個虛機裡包括了己經安裝和配置好的目標應用二進位制程式碼,以及執行目標程式中的所有其他依賴包, 比如一個執行在Tomcat中的完整應用的Docker映象組成如下。

這裡寫圖片描述

而啟動映象的過程就是啟動打包製作映象時指定的目標程式,比如對於Tomcat來說,就是執行tomcat.sh命令,此外由於映象本身已經固化了安裝過程及配置引數,所以Docker映象建立和啟動一個容器就變成了一個非常簡單並且不會出錯的命令:

docker run xxximage

而執行期間需要指定的引數可以通過環境變數及啟動命令的引數等方式傳遞到容器中,不同的容器之間相互隔離,因此你可以在一個主機上同時啟動不同映象版本的多個版本的容器並測試資訊。更進一步地,Docker將製作映象(build image)、建立容器,以及啟動、停止、掛起、恢復、銷燬容器的所有功能都做成了 REST API, 於是我們可以使用程式設計的方式來實現自動化控制能力,後面提到的Kubemetes即採用了Docker的API來實現了全自動的微服務架構平臺。

打包好的Docker映象是否可以像原始碼一樣能夠進行版本管理、集中託管並且被全球任意聯網的機器下載執行呢? Docker公司的第二個創意就模仿了GitHiib的做法,建立了全球唯一的開放性 Docker倉庫一 Docker Hub, 任何組織和個人都可以註冊賬號,並且分享自己打包的 Docker映象,現在你所能想到的任何中介軟體或基礎應用幾乎都在Docker Hub上存在映象,比如下面這句命令就自動從Docker Hub拉下來一個MySQL映象,並且在本機啟動了一個MySQL 伺服器,可以讓遠端機器訪問。正是Docker Hub的存在大大加速了 Docker技術的普及和發展。

docker run -it -e MYSQL_ROOT_PASSWORD-123456 mysql/mysql-server

私有的映象倉庫被稱為Docker Registry,通常每個使用Docker的公司都需要自己建立一個私有的Docker Registry,存放從Docker Hub拉取的標準基礎映象,以及基於這些基礎映象而打包的私有映象。下圖給出了Docker映象打包、執行過程中與Docker Registry之間的互動過程。

這裡寫圖片描述

綜上所述,我們通過Docker技術可以很容易地將軟體開發從原始碼編譯、映象打包、測試環境部署、版本釋出、系統升級到生產環境釋出等生命週期中的所有重要環節自動化,這是加速微服務架構實施的重要技術保障手段。

3、如何理解微服務架構

在前面講述RPC時,我們講到了微服務架構的概念,也明白了軟體架構從SOA 架構、RPC框架、分散式服務框架到目前的微服務架構的持續演化之路。既然微服務架構是從 之前這些架構演變而來的,那麼它也具有以下共性。

首先,微服務架構是一個分散式的系統架構。也就是說分散式系統設計的原則、經驗,以及常用的分散式基礎設施和中介軟體依然是微服務架構中的重要組成部分,如果拋開分散式架構中的這些技術,只是空談微服務架構,則好像空中樓閣。

其次,與 SOA架構一樣,微服務架構與開發語言無關,它並沒有公認的技術標準規範與實施方案指南,它更多地體現了一種被普遍接受的新的設計理念和指導思想,歸納下來有以下幾點。

  1. 輕量級的服務:每個服務例項只提供或者密切相關的一種或幾種服務,粒度小、輕量級, 便於微團隊快速開發、部署、測試與升級。
  2. 鬆耦合的系統:微服務之間的呼叫也是客戶端的一種呼叫方式,僅限於介面層的耦合, 避免了服務實現層的深耦合,因此服務之間的依賴性被降到最低,系統的整體穩定性與平衡升級 (滾動升級)能力得到切實保障。
  3. 平滑擴容能力:由於微服務架構平臺中都原生地提供了某種微服務負載均衡機制,因此對於無狀態的微服務,可以通過獨立部署多個服務程序例項來提升整體的吞吐量。由於每個微服務可以單獨擴容,因此微服務架構具有很強的執行時的效能調優能力。
  4. 積木式的系統:每個微服務通常被設計為複雜業務流程中一個最小粒度的邏輯單元(積木),某個完整的業務流程就是合理編排(搭積木)這些微服務而形成的工作流,升級或者新開發一個新業務流程變成了簡單的積木遊戲,而隨著微服務越來越多,業務單元 (微服務)的複用價值越來越大,因此新業務快速上線的需求變成了一個可準確評估和預測的計劃任務。

最後,微服務架構也有某些事實上公認的框架與工具, 目前最經典的有以下三個微服務架構開源平臺。

  1. 從RPC框架進化而來的Ice Grid微服務架構平臺。
  2. 基於REST介面演化的Spring Cloud微服務架構平臺。
  3. 最新的基於容器技術而誕生的Kubemetes微服務架構平臺。

上述這三個經典微服務架構平臺各自能提供完備的微服務架構框架與管理工具,在技術上各有千秋,從總體上來看Google出品的Kubemetes平臺是當之無愧的微服務架構之王。

接下來,我們一起看看在實施一個微服務架構專案的過程中可能遇到的問題及應對策略。

首先,架構師需要對專案組的全體成員進行培訓,讓大家明白微服務架構的思想和優點,培訓的一個重要目標是要讓大家明白,微服務架構在實施過程中會給專案組帶來很明顯的技能升級需求,不管是對於開發人員還是對於運維測試人員來說,這都是一個難得的新技術學習與 自我技能提升的好機會,希望大家頂住壓力完成專案。

其次,要正確選擇一個合適的微服務架構平臺而不是自己研發。這種大型基礎平臺的研發成本很高、開發週期長而且平臺可持續升級的可能性較低,因此目前很少有公司會自己進行研發,即使是RedHat這樣有實力、有經驗的開源軟體服務型公司,也放棄了自己的微服務架構轉而應用Google的 Kubemetes 那麼,如何選擇適合自己公司和專案組的微服務架構平臺呢?以上文提到的三個典型的微服務架構平臺為例,可參考如下條件進行選擇。

  1. 如果整個團隊對容器技術沒有什麼經驗,則排除Kubemetes,否則優先選擇它。
  2. 如果系統的效能要求很高,同時很多高頻流程中涉及大量微服務的呼叫,以及微服務之 間也存在大量呼叫,則這種情況下優先考慮以RPC二進位制方式通訊的微服務平臺,優先考慮Ice,其次是Kubernetes,最後是Spring Cloud。
  3. 如果系統更多地是自己內部開發的各種服務之間的遠端呼叫,很少使用中介軟體,只需要高效能的通訊及水平擴充套件能力,則Ice可能是最佳選擇,其次是Spring Cloud,最後才 是 Kubemetes。因為Kubemetes並沒有提供一個RPC框架,在這種情況下,反而增加 了系統的複雜性。
  4. 如果有專案是用多個語言協同開發的,則在這種情況下,優先選擇Kubemetes架構與 Ice。

再者,微服務架構的專案在實施過程中經常需要考慮如下工作。

  1. 引入自動化工具與集中運維管理工具。自動化工具用於程式編譯打包、自動化部署和升級等工作過程中。在集中化的運維監控工具方面主要包括日誌收集與查詢展示系統,用於收集分佈在各個節點上的系統日誌、應用日誌,以及資源監控與故障系統,用於展示資源使用狀態與應用告警。
  2. 研究、測評大量相關開源產品(與工具)並引入微服務架構中。微服務架構本質上是一種分散式架構,所以之前單體應用開發中所寫的一些通用程式碼是無法應用到微服務架構系統中的,比如最常見的配置模組、定時任務、同步邏輯等。此外,對於很多中介軟體來說,原先可能只用了單節點的方式,而在微服務架構下往往要切換成叢集模式,這種情況下也需要對這些中介軟體進行更為深入的研究測試,甚至可能會因此轉向其他類似的中介軟體。
  3. 團隊的重構。在微服務模式下,整個系統從架構層來看基本只分為展現層與微服務層。考慮到微服務在整個系統中的重要性,建議團隊中的骨幹技術人員成為微服務層的開發主力,大家作為一個總體對所有微服務程式碼進行負責,一起設計每個微服務介面,一起評審所有微服務程式碼,而在具體的開發過程中,則可以將相似度較高的幾個微服務交由一個人研發。這種模式基本符合二八定律,即 20%的重要事情(微服務)決定全域性,20%的人(微服務研發組)決定整個專案的成敗。
  4. 高質量的文件。微服務架構下,文件特別是每個微服務的介面文件的重要性越來越高,因為每個使用微服務的人都要清楚當前所要呼叫的微服務是哪個、應該呼叫哪個介面、引數有什麼含義,以及返回值的意義。因此,我們需要一份詳細並且準確的微服務介面文件,並且保 持文件與程式碼同步更新。

接下來,我們來看看如何設計系統中的微服務。一開始,我們其實並不很清楚哪些功能和服務要設計成一個微服務,以及一個微服務宄竟應該包括多少個介面?每個介面應該如何設計? 筆者的建議是先粗粒度地劃分微服務,每個微服務包括比較多的介面以減少微服務的個數,這樣可以減少開發、程式打包、測試及部署的工作量,有利於快速推進專案。

系統中的微服務按照呼叫客戶端的不同,可以劃分為流程控制類、介面類及基礎核心類三種,如下圖所示。

這裡寫圖片描述

一般來講,這三種不同的微服務的介面設計也有所不同。

流程控制類微服務主要面向U I呼叫,所以它的介面設計應該以頁面展示的便利性為第一目標,即大部分情況下采用JSON或 TEXT文字的方式傳遞引數與返回值,並且考慮在呼叫邏輯 出錯的情況下,要告訴客戶端錯誤的程式碼與原因,於是這類微服務的返回值通常會是下面的結構體:

public class CallResult 
int resultCode; //返回程式碼,0 為成功,其他為呼叫錯誤 
String resultData;//呼叫結果,通常為JSON的字串 
String errmsg;//呼叫錯誤的時候,展示給使用者的可讀錯誤資訊

介面類微服務主要面向第三方系統,所以特別需要注意安全問題,因此介面設計中必須有安全措施,比較常見的方案是在呼叫引數中增加Token,並考慮引數加密的問題,同時建議介面類微服務在實現過程中重視日誌的輸出問題,以方便介面聯調,以及方便在執行期間排查介面故障,日誌中應該記錄入口引數、關鍵邏輯分支、返回結果等重要資訊。

基礎核心類微服務主要被UI及其他兩種微服務所呼叫,在這類微服務的介面設計中主要考慮效率和呼叫的方便性。建議設計得與普通Java類的介面看起來一樣,這樣可以避免將很多複雜 Bean物件作為引數及返回值時不但增加呼叫者的負擔而且降低介面效能。

在微服務設計中,我們還需要考慮介面相容性的問題,舉例說明,對於如下微服務介面設計:

public void doBusiness(paraml,paramS,param3);

如果引數的個數存在增加的可能性,那麼為了相容舊版本,最好改為如下設計:

public class XXXBean { 
private String paraml;
private String param2; 
private String param3; 
private String param4;
}
public void doBusiness(XXXBean thebean);

這樣一來,舊介面無須重新編譯,只要升級XXXBean所在的Jar包即可相容舊版本。