Rest API

 

 

 

 

1、前言

  在軟件行業快速發展的今天,傳統的軟件受權已經不能足以知足一個IT類的公司的發展。雖然在大部分公司裏,它仍是現金池的直接源頭。可是在可碰見的將來,受摩爾根理論的失效、物聯網的發展等影響,應用的架構會愈來愈趨於簡單化,架構愈來愈傾向於分佈式水平擴展,對外的服務提供也會愈來愈SaaS化。在這種大背景下,不少公司都開始提供所謂的開放平臺。html

  查閱各個大公司的開放平臺,咱們不難發現,都是Rest API,都是HTTP請求,響應報文都是大同小異的XML或者是JSON等衆多雷同的特色。這是爲何呢?讓咱們嘮嘮API平臺的那些事。git

插播:推薦一個張善友大神的關於web api的專欄:http://www.cnblogs.com/shanyou/category/307401.htmlgithub

rpc的專欄http://www.cnblogs.com/duanxz/p/3769872.htmlweb

2、定義算法

  查看歷史,咱們驚訝地發現,其實Rest的概念早在2000年就被人提出。用一句話描述它,就是用固定的URI和可變的參數訪問某個服務,來完成一系列業務請求。編程

  1. 每個URI表明一種資源;
  2. 客戶端和服務器之間,傳遞這種資源的某種表現層;
  3. 客戶端經過幾個HTTP動詞,對服務器端資源進行操做,實現"表現層狀態轉化"。

2.1 Rest API 格式

  Rest API,不管它的名字多麼高大上,它本質仍是一個HTTP請求,POST也好,GET也罷,都是不一樣的數據提交方式。因此,可以決定一個Rest API的也就:URI、參數、請求方式、請求頭等。json

  咱們通常用URI來定義但願對外暴露的服務。結構基本相似 schema://yourCompanyDomain/rest/{version}/{application}/{someService}schema能夠是http,也能夠是httpsversion指的是你這個API的版本,application通常會指向底層的某個子系統,someService就是這個子系統對外提供的服務。固然,若是按照業務爲邊界劃分,也可將業務維度相同但隸屬於底層不一樣的系統的服務定義爲一個application後端

  對於這種Rest請求,常見的響應結果就是XML或者是JSON形式,每每結果中會包含請求狀態,和時間戳,業務系統響應結果。api

  具體的格式約定,能夠看底部的參考文獻。數組

2.1.1 API的版本概念

  在 URI 的格式定義中,咱們包含了version這個字段,這在早期,其實被認爲是不優雅的方式。阮一峯有一篇文章就專門抨擊這種設計,後面他又本身打臉說仍是拼接version的好。(不知道是否是由於Github的設計緣故)

  API設計時常會考慮版本這個概念,不管是在URI仍是在請求參數裏面,至少有一個地方得指明含版本,爲何呢?

  這很簡單,就和系統迭代同樣,API也是快速迭代開發的。也許初期,你的老大腦殼一熱,咱們要上API,因而大家就加班加點作,設定了一版請求協議。當時大家想,寫完就能賺錢,必然帶來一堆問題,好比說,代碼難以維護,功能單一。後面這個API的第二版,大家要基於它去作一些新的變動,好比請求參數多了,返回內容多了,必然就有了第二版。可是又不能影響現有的業務。這個API基本的URI沒變,可是會同時存在兩個版本,老用戶繼續請求舊版,沒問題,你無需動舊版?有需求的新用戶,你要請求新版才能提供服務。這樣經過版原本區分了不一樣的服務,便於之後的升級和維護。(就像BAE那貨能夠毫無壓力地廢棄掉2.0的API)

  因此一開始設計API時,就要定義好版本。其次,版本化能夠騙錢。咱們能夠滿懷惡意地猜想,1.0爲了搶用戶,免費。2.0老牛逼了,你用的爽了,想更爽,付費。233333333

2.2 架構特色

  API平臺的架構,其實和底層公司的業務系統架構有着密切的關係,基於一個好的系統架構寫一個Rest API平臺基本是水到渠成的。咱們先從業務系統的架構變遷提及,再來分析上面的API平臺。   

2.2.1 業務系統架構變遷

 

  基本的軟件公司的業務系統,一開始都是單機,單節點,單庫。慢慢隨着業務量的增長。這個系統愈來愈複雜,機器性能愈來愈不能知足需求。因而,第一種可能,領導說,換機器。上一臺牛逼機器。可是機器性能有限,越牛逼的機器價格越貴,有時候都能買3~5臺現有配置機器。因而就有了方案二,三個臭皮匠勝過一個諸葛亮。單應用,多機器,多節點

 

 

 

 

  可是慢慢過了幾年。你發現,這代碼寫的愈來愈屎,愈來愈複雜。基本上新來的開發要熟悉好幾個月的業務。就代碼由於快速上線。一堆坑,沒法改。因而,你們如今都在作的事情,就是拆分。也就是如今常說的SOA。拆分,也有拆的好的,拆的很差的。很差的,就是一個大的噁心系統,變成了一堆噁心的小系統,互相調用,成一團亂碼。小系統看似很好。可是某個不起眼的小系統。一掛,那麼所有的系統都癱了。這個時候這個萬年不維護的小系統還找不到負責人,他麼早就滾了。這就是拆分的技術欠債,你沒法避免。

 

 

  那麼,就談到拆分的架構設計。其實這塊分兩個架構,技術架構和業務架構。技術架構,就是要分清技術系統和業務系統。技術系統也多是一個業務系統,但它必定是一個通用的服務組件。它提供的服務無任何定製需求,就是純簡單服務。好比,發短信發郵件發微信的通知系統,它就是通知。你業務有何特殊需求,就在上面本身實現一個XXX通知系統,那麼業務系統的拆分,纔是最關鍵的。就是要劃清邊界

  這個邊界問題很可怕,什麼你該作,什麼你不應作。每一個系統的職責都要明確。不要你也實現一個他也實現一個,而後相互調。這種可怕性就是在兩個服務都癱瘓的時候,徹底都沒法啓用。最後你的系統架構變成了一個通用技術組件系統,完成各個基礎服務,每一個產品線,業務端,基於你的技術系統包裝出業務定製化服務系統,而後最上層就是業務子系統。業務子系統組合在一塊兒,就是一個大的業務系統,也叫服務化平臺。

  這個時候,你須要作開放平臺。暴露一套Restful API,就是水到渠成了。

  PS,實際的架構遠比這個複雜,截圖選自《大型網站系統與Java中間件實踐》。

2.2.2 基於不一樣系統架構的 API 平臺

一、演變

 

  初期的API平臺每每是上圖左側那種,某個龐大的業務系統但願暴露一套API,因而你們就在這個系統上作,直接設計一套協議。可是,這樣子帶來的缺點十分明顯,第一,它與業務聯繫過重,理想的API平臺是通用的,不是隻給你設計一個。第二,它很差擴展,每次變動都得和業務系統一同上線,糟糕的狀況下代碼還會影響原有正常的業務。第三,性能問題,理論上會下降原有應用的性能。

  這種狀況下,若是應用部署了多臺機器,多個節點,咱們就能夠獨立出來。也就是右邊所示的API Gateway,它作的事情本質上就是反向代理,將外部的請求校驗完合法性以後反代至內部實際想要對外暴露服務的服務集羣上。

  因此,這種場景下,API Gateway也就如名稱所說,就是一個入口。實際的Rest API的東西仍是創建在各個業務子系統上,只是只須要提供最簡單的服務,無需考慮受權等東西。用戶管理,API註冊發佈,調用統計等,均由API Gateway實現處理。對於想要快速上線的開發人員而言,實在是一個不錯的福音。

  然而,當系統應用拆分到了SOA化以後,API的架構由有了新的變革,咱們有了註冊中心的概念。由於SOA化,因此每一個業務子系統其實都有了對外的統一接口,有了ESB(註冊中心)。實際的內部系統間請求也有了較好的路由、熔斷等策略。

  在這種大背景下,API平臺對外暴露的Rest API無需底層的業務專門開發了。直接使用現有的內部接口,選擇性暴露便可。問題點就在於,如何根據定義的Rest API請求,實際模擬內部的RPC協議請求。

  某種程度上,這時候的API平臺,已經不只僅是HTTP Rest請求了。咱們徹底能夠實現相同RPC協議的透傳,好比你就是一個Hessian接口想對外暴露,我只需包上一層認證,直接註冊於API Gateway,外部Hessian請求直接透傳至內部子系統。

  在這個基礎上的 Rest API 平臺,纔是靈活的,可擴展的,易於維護的。然而有得必有失,Mock請求必然會有性能上的損耗,可是這個架構的公司,已經不在意錢了,上10臺虛機,不夠加唄。

二、特色

  1. C/S結構
  2. 無狀態(API平臺無需存儲業務狀態,只作認證和轉發)
  3. 有緩存,API會對指定URI的請求轉發作緩存,保證併發性,業務系統也對一樣的請求針對性緩存。
  4. 結構分層,每層間沒法直接訪問。

API平臺的背後,就是龐大的各個業務子系統。每一個API,就至關於一個業務子系統。API平臺要作的事,就很是清晰和簡單。就是業務子系統註冊發佈API,對外部請求校驗計費,模擬請求內部業務子系統,對子系統結果包裝序列化爲JSON返回。

2.3 交互流程

 

 

  上面是一個簡單的交互,簡單顯示了外部系統和內部系統經過Rest API的交互過程:開發者(企業)註冊,申請APP_KEY,開通API。按照開發接入,請求籤名。轉發至後端調用返回結果,API平臺計費(預付費或者後收費),統計調用狀況。

  外部通訊本質上仍是HTTP,那麼必然存在了受權問題,生產的API平臺是直接暴露於公網的,若是認證受權策略出現紕漏,影響是可怕的。

  好比,這個API是羣發短信,你要是沒有好的受權體系,容許人隨意推送。某我的想搞你,調用發佈反共信息,你整個公司都會跨。HTTP協議本質上沒有這一塊的內容,因此咱們必然要在這上面考慮安全策略的內容。

2.3.1 如何保證Rest API的安全性

  若是單純考慮加解密,或者簽名方式來保證請求合法,實際上是遠遠不夠的。事實上,一個安全的API平臺每每須要多方面一塊兒考慮,保證請求安全合法。

一、是否是實際客戶端的請求?

  1. 設計專門的私有請求頭:定義獨有的Request headers,標明有此請求頭的請求合法。
  2. 請求包含請求時間:定義時間,防止中間攔截篡改,只對指定超時範圍內(如10秒)的請求予以響應。
  3. 請求URI是否合法:此URI是否在API平臺註冊?防止僞造URI攻擊
  4. 請求是否包含不容許的參數定義:請求此版本的這個URI是否容許某些字段,防止注入工具。
  5. 部分競爭資源是否包含調用時版本(Etag):部分競爭資源,使用If-Match頭提供。如用戶資金帳戶查詢API,能夠返回此時的帳戶版本,修改扣款時附加版本號(相似樂觀鎖設計)。

二、API平臺是否容許你調用(訪問控制)?

  訪問控制,主要是受權調用部分。API都對外暴露,可是某些公共API能夠直接請求,某些,須要受權請求。本質的目的,都是爲了驗證發起用戶合法,且對用戶能標識統計計費。

  以HMac Auth爲例,咱們簡單設計一個簽名算法。開發者註冊時獲取App Key、App Secret,而後申請部分API的訪問權限,發起請求時:

  1. 全部請求參數按第一個字符升序排序(先字母后數字),如第一個相同,則看第二個,依次順延。
  2. 按請求參數名及參數值相互鏈接組成一個字符串。param1=value1&param2=value2...(其中包含App Key參數)
  3. 將應用密鑰分別添加到以上請求參數串的頭部和尾部:secret + 請求參數字符串 + secret。
  4. 對該字符串進行 SHA1 運算,獲得一個二進制數組。
  5. 將該二進制數組轉換爲十六進制的字符串,該字符串爲這次請求的簽名。
  6. 該簽名值使用sign系統級參數一塊兒和其它請求參數一塊兒發送給API平臺。

服務端先驗證是否是實際客戶端的請求,而後按照App Key查找對應App Secret,執行簽名算法,比較簽名是否一致。簽名一致後查看此App Key對應的用戶是否有訪問此API的權限,有則放行。

執行成功後包裝返回指定格式的結果,進行統計計費。

3、需求與實現

3.1 系統需求

3.1.1 目標

  1. 支持rest類API接口動態發佈及運營,包括但不限於:
    • 安全認證
    • 會話管理
    • 流量統計及限流
    • 計費收費
    • 熔斷
  2. 支持現有子系統RPC協議的API動態發佈及運營,外部請求透傳。
  3. 支持json、xml響應報文,能夠請求時選取所需報文格式。
  4. 支持動態直接將後端SOA服務暴露爲API。
  5. 支持動態將普通Web接口暴露爲API。
  6. 支持動態將MQ服務暴露爲API。
  7. 支持多個服務組合編排後暴露爲API。

3.1.2 業務需求

一、API管理

全部API可後臺查詢管理,包括動態發佈、參數映射配置、後端服務接口配置、API禁用、啓用,多版本、分組、分級別等。

二、應用管理

後臺管理開放平臺接入的應用(第三方應用),包括查詢、禁用、啓用、審覈。

三、API鑑權&受權

  1. 應用申請審覈經過後生成公鑰,開放平臺需提供支持分佈式系統的密鑰管理
  2. 服務可設置爲兩個安全等級:需受權訪問和無需受權訪問(後者即任意客戶端均可以發起調用),默認全部API都需受權訪問。
  3. 非正常狀態(禁用、停用、黑名單等)的應用直接拋異常不容許訪問——熔斷機制
    • 調用次數、調用頻率、併發數可運行時控制,避免某請求量過大影響其餘應用的調用。
    • 可對某個應用某個API設置強制熔斷,全部請求無視閥值直接拋出異常。
  4. 易用性
    • 與SOA集成,SOA服務一鍵發佈到API平臺。
    • 支持後臺動態發佈API,而不是新上一個API就需上線一次。

四、計費統計

  1. API的調用統計,每筆請求時間,響應時間,響應狀態。
  2. API的計費計算,按照請求量和請求資源計費,實現多種計費模型。(預付費,後收費。按量,按時間週期。)

五、開發者平臺

  1. API開發者平臺,開發者註冊、訪問、申請API受權、計費統計、調用統計。
  2. API文檔系統,詳細的API文檔展現,SDK下載,用戶登陸後還可專門生成不一樣編程語言請求,在線模擬請求結果等。

3.1.2 角色定義

一、外部用戶

用戶 作什麼 使用目的
API平臺接入方 接入API平臺 使用XXXX提供的開放平臺服務

二、各個業務產品線

用戶 作什麼 使用目的
各個業務產品線 做爲外部應用接入API平臺 使用XXXX提供的開放平臺服務
各個業務產品線 提供服務 提供後端服務,發佈到API平臺供外部應用接入
公司後端應用 提供服務 提供後端服務,發佈到API平臺供外部應用接入
API平臺 API治理 運營,管理API、第三方應用等

3.2 請求模型

  API 的全部服務請求域名是相同的,區別在於Request Path等。請求參數分爲系統級參數和業務級參數兩部分,系統級參數是全部 API 都擁有的參數,而業務級參數由具體服務 API 定義。

3.2.1 統一服務 URL

  創建API Gateway接受全部請求,按照Request Path,Request Method,Request Head分發全部的請求。

一、 通用統一URL

格式:schema://<API Gateway URI>/DispatcherServlet?method=XXService.xxMethod?xxxObj.xxxParam=xxxValue。

說明:全部請求直接走DispatcherServlet分發,全部內容均定義於URL參數中。method爲後端某個子系統的某個方法。xxxObj.xxxParam爲方法參數實體的某個屬性的值定義。

示例: http://api.xxxxx.com/router?method=SMSService.sendSMS&user.phoneNumber=18888888888&sign=ds234324sdsad&date=20151229231232

二、Rest類型URL

格式:schema://<API Gateway URI>/rest/{version}/{service}/{method}/{params}

說明:請求按照Gateway定義的Rest地址匹配,動態映射至具體系統具體方法,模擬調用。請求中包含version字段。

示例: http://api.xxxx.com/rest/v1/XXService/xxMethod/{xxParam}http://api.xxxx.com/rest/v1/XXService/xxMethod?xxxParam=xxxValue

3.2.2 參數設計

一、系統級參數

  系統級參數是由 API 平臺定義的一組參數,每一個服務都擁有這些參數,用以傳送框架級的參數信息。如咱們前面提到的 method 就是一個系統級參數,使用該參數指定服務的名稱。

二、業務級參數

  業務級參數,顧名思義是由業務邏輯須要自行定義的,每一個服務 API 均可以定義若干個本身的業務級參數。API Getaway 根據參數名和請求屬性名相等的契約,將業務級參數具體的方法請求對象中。

3.3 常見框架

  1. Kong:https://github.com/Mashape/kong
  2. Zuul:https://github.com/Netflix/zuul
  3. ROP:https://github.com/itstamen/rop

4、優劣

4.1 好處

  1. 跨平臺,管你是Java,仍是PHP,仍是Node.js仍是Go,你丫都得支持HTTP請求。我API平臺只須要提供這個語言的SDK,保證能按照消息協議調用就好。

  2. 將複雜的內部業務系統抽象爲通用調用請求。包裝了複雜的業務邏輯,對外提供統一的,好管理的接口。並能夠定製化設計,計費,受權一類的容易管理。

4.2 壞處

  1. 協議描述能力弱化,RestfulURI沒法徹底對請求參數作強格式校驗。最後的方法參數綁定,模擬內部請求時每每容易出問題,尤爲是以Java等強格式語言的系統。不能像WebService同樣清晰描述請求報文。

  2. 一樣的道理,響應結果爲了是JSONXML。這當中,編碼,正反序列化,等操做,每每就會有性能瓶頸。並且,Java在這塊資源消耗極大。以Github的ROP這個框架爲例,當年測試時,它在併發請求太高的時候就會有一個內存泄漏問題。


附:參考資料

  1. REST Is Not About APIs, Part 1
  2. REST Is Not About APIs, Part 2
  3. RESTful API 設計指南
  4. 理解RESTful架構
  5. 撰寫合格的REST API
  6. Netflix 官網
相關文章
相關標籤/搜索