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% 的用戶的請求到新服務上,那麼這個節點很可能會出現性能問題。
“我們相信人人都可以成爲一個java開發大神,現在開始,找個師兄,帶你入門,學習的路上不再迷茫。這裏是java開發修真院,初學者轉行到互聯網行業的聚集地。"
我做開發十多年的時間,如果大家對於學習java的學習方法,學習路線以及你不知道自己應該是自學還是培訓的疑問,都可以隨時來問我,大家可以加我的java交流學習qun:615741636。qun內有學習教程以及開發工具。