面試造飛機系列:看架構師如何設計微服務接口



點擊上方碼農沉思錄,選擇「設爲星標」javascript

優質文章,及時送達css


本文是後端微服務架構系列的第二篇文章。在微服務設計中,服務間接口通訊設計常見的有兩種方式:RPC  和 REST
java

這篇文章主要介紹什麼是 REST 風格設計以及 RESTful 接口。閱讀完本文你將收穫如下知識點:c++

什麼是 REST 和 RESTfulweb

REST 接口設計規範是什麼面試

REST 爲何要設計成無狀態json

接口無狀態真的是沒有狀態嗎後端

RPC 和 REST 適用場景瀏覽器

REST和RESTfulruby

REST(Representational State Transfer,表述性狀態轉移) 是一種軟件架構風格。REST提出了一組架構約束條件和原則,任何知足 REST 約束條件和原則的架構,都稱爲 RESTful 架構。

微服務之間須要相互通訊以完成特定的業務處理,在典型的客戶端-服務端設計模型中,客戶端和服務端統統過消息請求-響應的方式交互協做,REST 就是這樣一套微服務之間交互接口的設計約束和原則規範。

乍一看 REST「表述性狀態轉移」每一個字都認得,連起來不知道什麼意思。

這是做者 Roy Thomas Fielding 在他的博士論文裏提出的概念,論文天然都是學術用語,不過感興趣的同窗能夠去看看做者論文原文,地址我貼出來:https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

今天 lemon 用大白話幫你透徹理解這個概念,咱們把「表述性狀態轉移」掰開來看,先搞明白什麼是「表述性」,什麼是「狀態轉移」。

表述性

「表述性」實際上是缺乏了主語的,主語是「資源」。完整的描述是「資源表述性」,也就是「資源的描述」。在網絡通訊中用什麼描述資源呢?沒錯就是 URI(Uniform Resource Identifier,統一資源標識符)。

這裏有幾個近義詞先給你們先科普一下:

URI  是統一資源標識符,用來惟一的標識一個資源。

URL 是統一資源定位器,它是一種具體的 URI,即 URL 能夠用來標識一個資源,並且還指明瞭如何定位這個資源,URL 是 URI 的子集。

URN  統一資源命名,是經過名字來標識資源。URN也是 URI 的子集。


在 HTTP 協議中用 URL 標識資源,也就是瀏覽器地址欄你看到的那一串網址。

地址欄URL資源表述性

爲了說明「資源描述性」接口設計的優勢,咱們來作一個接口設計方法的對比,舉個栗子就清楚了。

傳統的接口設計

先來看下傳統的網絡通訊模式是怎麼樣的。假設lemon這我的物對象,在服務端的存儲形式是一個c++的class類型存儲。

下面的過程展現,客戶端發送請求服務端建立一個 lemon 對象的過程。

  1. 服務端定義存儲結構頭文件 lemon.h

class lemon{ string name; string address; uint64 phone;}
  1. 客戶端代碼引用服務端定義的lemon.h,「互相引用頭文件,增長了服務耦合性!」

  2. 客戶端初始化一個 lemon 實例並序列化後經過網絡接口發送給服務端。

class lemon lm;lm.name = "lemon";lm.address = "Shenzhen";lm.phone = 18666666666;
  1. 服務端接收消息,反序列化,存儲傳輸過來的 lemon 對象

資源表述性接口設計

lemon 這個服務內部的對象,對外表現能夠用一張圖片來表示,也能夠用包含lemon 的姓名、地址、電話等信息的 xml 或 json 格式的數據表示。

{name : "lemon",address: "ShenZhen",phone : 18666666666}lemon
ShenZhen

18666666666

也就是說,lemon 這個「資源」在服務內部的存放形式對外不可見,外界客戶端發起請求能夠用不一樣的資源表述格式來獲取服務端的資源。

(若是服務器會說話,他心裏os 大概是這樣的:  客戶端你不用管我是如何保存這個對象的,只要你說的清楚想要什麼對象,只管發來請求即是!)。


這樣作最顯然的好處是,減小了服務之間的耦合。客戶端訪問服務資源以前不須要知道資源在服務端的具體存儲格式,只需描述資源形式便可修改、建立、更新、刪除服務端的資源。

狀態轉移

搞懂了「資源描述性」接下來看下什麼是「狀態轉移」?狀態轉移就是客戶端經過一系列請求動做,推進服務端的資源狀態發生變化,資源的狀態能夠在「建立-修改-查看-刪除」之間轉移。


資源狀態轉移

資源狀態的變化在宏觀上的反應就是業務流程推動。打個比方,你去銀行系統開戶、查餘額、銷戶,這個過程你推進了你的銀行帳戶這個「資源」經歷了不一樣的狀態轉移讓你完成了不一樣的業務操做。

REST的約束條件協議選擇

REST 自己並無提到底層應該使用什麼協議,平常實踐案例中最經常使用的是基於 HTTP 的 RESTful 實現。

這是由於 HTTP 協議自帶的動詞 GET/POST/PUT/DELETE 能夠做爲推進狀態轉移的方法,另外HTTP 的制定了規範的狀態碼。還有其餘的一些 HTTP 特性,這些特性使得在HTTP 之上實現 REST 要簡單得多,而若是使用其餘協議的話,就須要本身實現這些特性。

請求規範

RESTful 架構中,發生狀態轉換的是「資源」,因此URI 中通常只能包含表明「資源」的名詞,而且推薦是複數,而不該該在 URI 中包對資源進行操做的動詞。

對資源執行的CURD「增刪改查」動做應該在HTTP請求方法的GET/POST/PUT/DELETE中體現。

符合REST規範的寫法:

POST http://www.test.com/lemon // 建立Get http://www.test.com/lemon // 查詢PUT http://www.test.com/lemon // 修改DELETE http://www.test.com/lemon //刪除

不符合REST規範的寫法:

POST http://www.test.com/Createlemon // 建立POST http://www.test.com/Querylemon // 查詢POST http://www.test.com/Modifylemon // 修改POST http://www.test.com/Deletelemon //刪除狀態碼

服務端消息響應攜帶狀態碼,指示客戶端進行下一步處理。符合 RESTful 規範的接口返回狀態碼都是通用的,不須要額外約定,利用HTTP Status Code 狀態碼 表示請求處理結果,下降了微服務間互操做成本。

狀態碼 狀態碼含義
2xx 成功,操做被成功接收並處理
3xx 重定向,須要進一步的操做以完成請求
4xx 客戶端錯誤,請求包含語法錯誤或沒法完成請求
5xx 服務器錯誤,服務器在處理請求的過程當中發生了錯誤

下面是常見的HTTP狀態碼:

200 - 請求成功

301 - 資源(網頁等)被永久轉移到其它URL

404 - 請求的資源(網頁等)不存在

500 - 內部服務器錯誤

無狀態

RESTful接口要求是「無狀態」。無狀態指的是任意一個Web請求必須徹底與其餘請求隔離,當客戶端發起請求時,消息自己包含了服務端識別這一請求上下文所需的所有信息。

無狀態不是真的沒有狀態

接口「無狀態」更確切的說是服務端無狀態,整個會話仍是須要狀態維持的。要完成一個業務流程,通常客戶端與服務端須要屢次的消息交互,咱們知道HTTP 協議是「無狀態協議」,這就須要服務端可以識別幾個獨立 HTTP 請求的「狀態信息」,從而將他們關聯到一個業務流程中。

仍是舉例子銀行系統取款的例子:

用戶lemon要登陸銀行系統,首先須要在登陸頁面輸入用戶名和密碼,這時候產生一個登陸請求

服務端收到登陸請求,執行登陸邏輯並返回操做結果

lemon登陸以後點擊取款100萬,產生一個取款請求

服務端收到取款請求,執行取款邏輯並返回操做結果


取款業務流程

這裏有個問題,服務端在不一樣時間點收到登陸請求和取款請求,這兩個請求都是用戶 lemon 產生的,若是不在技術層面作對獨立的 HTTP 請求作關聯的話,服務端就沒法知道這兩個請求實際上是都是用戶lemon 「取款業務」的組成部分。

技術方案

服務端要能識別請求的「狀態信息」,有兩種技術方案:

  1. Session 方式。服務端保存會話狀態,客戶端每次請求攜帶session-id。服務端維護一個會話狀態信息列表,用session-id惟一標識一個狀態信息,session-id通常包含在HTTP響應的Set-Cookie頭部返回給客戶端,後續客戶端請求攜帶包含session-id信息的cookie頭部,服務端解析cookie取出session-id,去維護的狀態列表中取回該消息對應的狀態信息,這樣就把無狀態的HTTP變成有狀態的了。


session會話

  1. Token 方式。服務端不保存會話狀態,客戶端每次請求都攜帶完整的會話狀態信息(通常是加密的)給服務端。Token也稱做是「令牌」或臨時證書籤名,狀態信息都被加密到token中,這樣每當服務器收到請求後解密token就能獲取該請求對應的狀態信息,也就能把不一樣的請求消息關聯到同一個業務流程中來,和session方式有相似的效果,只不過此次的狀態信息不保存在服務端。

 

Token會話

以上兩種實現中,第一種 Session 方式是有狀態的,第二種 Token 方式是無狀態的。

若是你要實現 RESTful 接口最好按第二種技術方案實現,固然要實現無狀態也還有其餘方式,思路都是「服務端不保持會話狀態」就對了。

爲何要無狀態

爲了高可用性和負載均衡需求,多個微服務經過負載均衡實現分佈式集羣化部署,集羣中每一個服務都是獨立和對等的。若是服務器在收到客戶端請求之時不可用或者宕機,無狀態請求能夠由任何其餘可用服務器處理並做出應答,這在分佈式應用中很是重要。


REST無狀態接口

想象一下若是服務端保存狀態,一個事務內的每一個請求都必須落到同一臺服務器去處理,這就失去了分佈式的意義和優點。

因此, RESTful 接口要求是無狀態的,是爲了更好的適應分佈式業務場景,發揮微服務集羣優點。

REST 和 RPC

這兩個概念常常出如今微服務架構設計中,REST 是一種軟件架構接口設計風格,RPC 是一種計算機通訊協議,看起來是兩個不一樣的概念,無法比較。

可是有些書中把它們放在一塊兒比較,真要比較的話,我我的傾向於把 REST 具體化爲一種基於HTTP 並按照 REST 約束設計的通訊協議,這樣兩個通訊協議纔有比較性。

回顧下RPC

RPC (Remote Procedure Call)遠程過程調用是一個計算機通訊協議。咱們通常的程序調用是本地程序內部的調用,RPC容許你像調用本地函數同樣去調用另外一個程序的函數,這中間會涉及網絡通訊和進程間通訊,但你無需知道實現細節,RPC框架爲你屏蔽了底層實現。

RPC 是一種服務器-客戶端Client/Server模式,經典實現是一個經過發送請求-接受迴應進行信息交互的系統。

適用場景

不少 RPC 框架提供的消息傳輸都是基於二進制的,好比Thrift、Protocol buffers。這樣作的好處是消息結構比較緊湊,對於頻繁調用或者大流量、低時延要求的應用場景,可以顯著減小網絡開銷;另外一個約束是某些 RPC 框架有很強的技術耦合性,好比 Dubbo 只能用於 java 技術棧。綜上,RPC 「更加適用於系統內部微服務之間的高效通訊」

RESTful接口因爲提供了統一的基於 HTTP的 REST 設計標準,只需 web 框架支持 HTTP 協議,並設計RESTful 風格的接口便可,極大的方便了第三方服務接入調用,「適合用於微服務系統對外暴露的接口設計標準」

寫在最後

本文是微服務架構設計中接口選型的一個小方面,不少人會以爲如今工做面試,無論是大廠仍是小公司,都是面試造飛機,工做擰螺絲。我的認爲即便你在入職以後接觸不到架構方面的工做,也要有一顆架構的心,高度決定認知,若是隻盯着手上的那顆螺絲那和鹹魚有什麼區別?

老規矩。感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反覆求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,咱們一塊兒在探討中學習。


本文分享自微信公衆號 - 碼農沉思錄(code-thinker)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索