(給ImportNew加星標,提高Java技能)
連結:webfe.kujiale.com/draft-fu-wu-bu-shu-fen-xiang/
web 服務是什麼
1. 定義
我們先來看一個很通俗的定義,來自於wiki。
Web service 指的是,一個平臺透過 web 向其它平臺來提供服務。
更專業一點的定義怎麼說呢?我們來看一下 W3C 對 web service 的定義。
Web service 是一個軟體系統,使得不同機器可以在網路間進行互動操作。
2. 要素
想要實現一個平臺在網路間呼叫另一個平臺的服務,至少需要明確三點:
- 如何將平臺上的程式碼作為服務暴露出去供其它平臺呼叫;
- 使用什麼樣的網路協議通訊;
- 使用什麼樣的格式作為通訊內容。
從 WSDL 理解 web service 的要素
要回答以上問題,我們可以先簡單的瞭解一下什麼是 WSDL,Web Services Description Language,網路服務描述語言。我們知道服務的提供方其實本質上是由程式碼編寫而成,而服務的呼叫方透過發起一個網路請求來呼叫服務。那麼通俗的說,WSDL 做的事情就是,描述瞭如何根據呼叫方傳送的網路請求,找到服務提供方,進而找到要執行哪一段程式碼,從而得到結果傳回給呼叫方。
WSDL 是基於 XML 格式的檔案,包括兩部分,抽象定義和具體定義。
WSDL的抽象定義
WSDL 的抽象定義,獨立於提供服務的平臺和服務實現的語言,定義了該服務透過什麼樣的網路協議、使用什麼樣的訊息格式與呼叫方通訊。網路協議是不受限制的,可以是 http、ftp、smtp 等其它網路傳輸協議,不過大部分情況下我們使用的是 http 協議。訊息格式也是多種多樣的,最初唯一被廣泛使用的訊息格式是基於 XML 格式的 SOAP,簡單物件訪問協議,後來 REST 流行了起來,出現了基於 REST + XML 的訊息格式,再後來發展為 REST + JSON 的訊息格式,也就是我們現在應用最廣泛的一種。
WSDL 的具體定義
WSDL 的具體定義,與平臺和語言相關,定義了一個具體的服務呼叫,請求引數和傳回引數是怎麼樣的、以及透過哪一部分程式碼的執行可以得到結果等等。
咖啡館的類比
我們使用一個咖啡館來類比 WSDL 的工作原理,咖啡館是服務提供方,提供了下單、取餐、付款等服務,咖啡館的員工手冊則相當於提供服務的程式碼,顧客是服務呼叫方。WSDL 的抽象定義,定義了顧客如何找到咖啡館的位置,以及顧客和咖啡館的員工使用哪國語言進行交流等等;而 WSDL 的具體定義,則定義了每一個具體的服務,如下單服務,顧客需要提供什麼,工作人員在員工手冊的哪一頁可以找到下單的操作流程,以及工作人員會傳回什麼給顧客,等等。
WSDL 檔案可以由服務的實現程式碼自動生成,反之也可以透過定義好的 WSDL 檔案生成程式碼框架。
3. 應用方式
最常見的兩種 web service 的組織形式是:RPC 遠端過程呼叫,REST 表述性狀態轉移。從本質上來說,兩者定義的都是規範,一個是面向過程的遠端呼叫規範,一個是面向資源的遠端呼叫規範。
RPC 遠端過程呼叫
RPC,Remote Procedure Call,遠端過程呼叫,定義了平臺與平臺之間面向過程進行服務呼叫的規範。它的本質思想是,將一個平臺上的多個函式過程,作為一個服務,提供給另一個平臺呼叫。所以以 RPC 為規範的服務,需要關心的是「我要做一件什麼事」。RPC 規範是協議無關的,可以使用各種網路協議實現。
REST 表述性狀態傳遞
那麼 REST 又是什麼?不知道 REST 沒關係,如果你接觸過 GET、POST、PUT、DELETE 這樣的請求,不要懷疑,這種我們通常意義上所說的 http 請求大部分都是基於 REST 規範而來的。基於 REST 規範設計的 api 也稱之為是 RESTful 的 api,這樣的 api 的主題必須是資源,它關心的是「我要對某個資源進行什麼樣的操作」。為什麼 REST 可以流行起來呢?這就跟我們為什麼要用面向物件的思想進行程式設計是一個道理,萬物皆物件,外物皆資源。這裡推薦一篇非常通俗的講解 REST 規範的文章 如何給老婆解釋什麼是RESTful。
RPC 與 REST 的比較
總的來說,PRC 與網路協議無關,關心的是過程;REST 基於 http 協議,關心的是資源。下圖演示了針對相同的有關使用者的操作,REST 形式的服務(左邊)和 RPC 形式的服務(右邊)設計上的區別。
那麼在具體的使用場景下,對於兩種設計規範,我們應該如何選擇呢?我覺得二者的取捨,可以類比於函式式程式設計與面向物件的程式設計,各自有各自適合的場景,甚至在某些場景下,使用二者皆可且各有利弊。重要的是要理解這兩個設計規範的本質和初衷,並根據實際場景和個人的使用習慣最初抉擇。
web service 與子服務
在談子服務之前,我們來繼續之前咖啡館的假設,從而理解什麼是子服務以及我們為什麼需要子服務。
為什麼我們需要子服務
設想一個咖啡館的正常運作,需要以下職能人員的參與。
- 前臺:負責建立、修改、刪除客戶的訂單
- 收銀員:收取訂單相應的費用、找零、管理咖啡館的日常支出
- 服務生:為客戶配送咖啡到相應的座位上,回收餐具
- 清潔工:維護店內清潔、桌椅擺放、空調燈光等硬體設施
- 經理:保證店鋪正常執行,解決問題和異常情況
對於這些職能人員來說,核心要素有三點:
- 他們所做的工作有明確的界限劃分;
- 他們互相之間可能需要進行交流;
- 他們共同維護了咖啡館的運作。
為什麼咖啡館不是由一個非常厲害的全能的人承擔所有的工作呢?這個很容易理解:
- 首先厲害的人比普通人更加難找到;
- 而且要同時兼顧這麼多的工作內容是更加容易出錯的;
- 還有最重要的一點是,如果他生病了,整個店就完全沒有辦法運作下去。
那麼將咖啡館的例子對映到 web 服務上,提供一個單一的 web 服務來支援整個咖啡館的運作自然也是不合理的:
- 想要維護好一個大型的系統比維護好一個小型的系統更加困難;
- 業務邏輯冗雜的系統更容易出錯;
- 如果這個系統的一小塊內容出現問題很容易導致整個系統的崩盤。
那麼如何拆分一個服務系統呢,答案就是子服務了。我們將整個系統根據職能的劃分拆分成5個子服務,分別對應到上文的5種職能人員。
- 訂單管理服務
- 賬戶管理服務
- 餐具管理服務
- 店內環境管理服務
- 效能監控與異常處理服務
同樣的這些子服務的核心要素如下:
- 這5個子服務所提供的介面有明確的界限劃分;
- 子服務之間可以互相呼叫;
- 共同保證了整個咖啡館的運作。
理解了子服務的概念以及 web service 為什麼需要子服務之後,新的問題出現了:子服務如何進行合理的拆分?如何管理多個子服務?子服務間如何通訊?
這裡就不得不提到 SOA 了。
透過 SOA 架構組織子服務
SOA,Service Oriented Architecture,是一個面向服務的架構設計,通俗的說它也是一個規範,定義瞭如何管理服務的集合及他們之間的通訊方式。它本質上和 web service 以及子服務都沒有絕對的依賴關係,它甚至比 web service 出現的更加早。然而人們在 web service 上發現了它的用武之地,也就是說 SOA 剛好可以在 web service 的管理上體現它的價值。於是乎,造成了幾乎所有 SOA 的應用場景都與 web service 相關這樣的現狀,也導致了這兩個概念一定程度上發生了混淆。
既然 SOA 框架是對服務的集合的管理,那麼它究竟比單純的服務拆分多做了哪些事情呢?
我們來看一下下圖這個簡單的例子。假設我們要將整個系統拆分成4個子服務:ACCOUNT、C2D、ASK、DESIGN。左邊為單純的服務拆分,右邊為基於 SOA 框架的服務拆分。
- 左邊:單純的進行了服務拆分,形成了4個互獨立的服務。這裡其實就出現了兩個問題:客戶端需要關心我請求的 api 到底是屬於哪個服務的,然後再往相應的服務端傳送請求;雖然服務做了拆分,但是如果其中一個服務出現問題掛掉了,那麼整個架構中的服務都不可用。
- 右邊:將這4個子服務作為一個服務的集合,並簡單地應用了 SOA 架構。可以看到除了四個子服務之外,最上層還多了一個 gateway,而最下層也多了三個底層模組。最下層的三個底層模組很好理解,有一些工作是每個子服務都需要做的,比如版本控制、效能監控等,底層就是抽出了這樣的公共模組以便子服務復用。最上層的 gateway,閘道器,顧名思義,你們如果想訪問我管理的這些子服務,直接訪問我就好了;也就是說客戶端只需要向 gateway 傳送請求,gateway 會根據所配置的規則將請求轉發到正確的子服務上,這也就解決了上文所述左邊的設計中遇到的兩個問題。
子服務及子服務的部署
1. 服務的實現
web 服務是一個軟體系統,軟體系統是透過程式碼形成的。那麼這樣一個軟體系統是如何從一大坨程式碼轉化為穩定的、可訪問的、可更新的服務的呢?
一個有一定流量的服務一般是由類似這樣的結構組成的。
上層是一個負載均衡器(load balancer),下層是多個相同的節點(node)。
- 負載均衡器:將針對這個服務的請求,合理的分發到下麵的某一個節點上,以儘量達到這樣的目的:請求盡可能的被完成(例如其中一個節點沒有正常執行不會導致請求失敗)、每個節點承擔均勻的壓力(例如同時有一萬個請求,不至於扎堆到同一個節點上去導致節點出現效能問題)。負責均衡器可以透過網路裝置、虛擬 ip、nginx 反向代理、甚至僅僅是一段程式碼來實現。
- 節點:每個節點都是等同的,每個節點上都執行著相同的服務,等待處理負載均衡器轉發過來的請求。節點可以是一個物理機、虛擬機器、也可以是一個 docker 容器。
2. 服務的部署
服務的部署,簡單來說就是將該服務的軟體系統的最新程式碼克隆到每一個節點上,再在每一個節點上將服務執行起來。那麼服務的更新無非就是重新對每一個節點進行一次部署。
但是不要忘記,在一個節點上重新執行服務會導致該節點的服務有一個短暫的罷工,那麼如何保證在完成對服務的更新的同時,保證對於客戶端來說服務不會出現掛掉的狀態?這時候就需要一定的部署策略。
註:下圖中綠色節點均表示未更新節點,藍色節點均表示已更新節點
1)滾動部署
滾動部署,每次只更新 n 個節點,等待前 n 個節點部署好了,再更新下 n 個節點,這樣可以保證同一時間只可能最多有 n 個節點處在不可用狀態。
下麵四張圖是一個滾動部署過程的例子,例子中 n 為 1。
首先更新第一個節點。
待第一個節點更新完畢之後,更新第二個節點。
待第二個節點更新完畢之後,更新第三個節點。
待第三個節點更新完畢之後,更新第四個節點。全部的節點都完成了更新。
滾動部署的缺點是,在部署過程中,客戶端可以同時訪問到更新前的服務和更新後的服務。
2)藍綠部署
藍綠部署,如下麵三張圖所示,分為三個步驟。
首先新增四個節點,並將新版的服務部署上去。
全部部署好之後將負載均衡器指向新的四個節點。
移除原有的舊版本服務的四個節點。
藍綠部署解決了滾動部署會同時出現新舊服務並存的缺點,但是對資源的要求更高。
3)灰度釋出
灰度釋出是平滑過渡的一種釋出方式。讓一部分使用者繼續用舊版本的服務,一部分使用者開始體驗新版本的服務,如果使用者對新版本沒有什麼反對意見,那麼逐步擴大範圍,將所有的舊版本服務更新為新版本服務。灰度釋出可以保證整體系統的穩定,在初始灰度的時候就可以發現、調整問題,以避免產生無法輓回的影響。灰度釋出的實現如下麵三張圖所示。
首先更新一個節點。
然後透過負載均衡器按照一定的規則篩選出 20% 的使用者(比如使用者 id 除以 5 餘 0),並將這 20% 的使用者所發出的全部請求都轉發到已經更新過的節點。
再更新第二個節點,並將包含之前那 20% 的使用者的 50% 的使用者的請求轉發到兩個更新過的節點上去。
以此類推直到所有節點更新完畢,100% 的使用者的請求都會請求到新的服務。這裡需要註意的是,灰發的使用者百分比最好和更新節點的佔比相近,這樣可以保證每個節點可以承受相似的壓力。如果只更新了一個節點,而轉發了 90% 的使用者的請求到新服務上,那麼這個節點很可能會出現效能問題。