本文將會講到5G和HTTP。曾經在深入淺出經典面試題:從瀏覽器中輸入URL到頁面載入發生了什麼 - Part 3 提到為什麼有些RPC框架不選用HTTP,而5G會採用HTTP。
您可以從本文裡獲取到一些概念:5G用HTTP作為reference point interface的實現,HTTP/2,RESTful API/HATEOAS/OpenAPI等最佳實踐和標準,這些都是一些常見但是又容易忽略的知識點。
本文參考了一些文章,見文章末尾的連結列表。
HTTP的優點和缺點
我們大家知道HTTP協議包含的資訊太多,太繁重,導致訊息體會很大,但是其中有一些訊息根本用不上,這也是為什麼HTTP/1.1訊息效率不高的原因,所以一些RPC框架捨棄它,例如dubbo定義自己的協議等,如果大家定義過協議,例如類似TCP協議,就能明白協議定義的重要性。如果要效率高,訊息短,那就會太底層,如TCP,如果要想易於理解,例如HTTP,那就得長一些。
5G和HTTP
5G明年試商用,在5G裡採用HTTP協議,確實有意思。可以參看TS 29.501協議 5G System;Principles and Guidelines for Services Definition,Stage 3。先看看下圖:
在通訊領域,由原來的Diameter,AAA等轉變為HTTP,的確是一個大變化,但是開發的效率將會大大提高。
那麼5G將會應用到什麼HTTP相關技術呢?
- HTTP/2/0 (協議下載https://http2.github.io/http2-spec/)
- JSON
- HATEOAS
- RESTful
- OpenAPI
HTTP/2.0
還是先看看HTTP/2吧。談到HTTP/2,最先想到Google的SPDY,它是HTTP/2的前身。為什麼Google要做SPDY呢?原因很簡單,HTTP的效率不高。自從有了SPDY後,載入時間減少64%(http://dev.chromium.org/spdy/spdy-whitepaper),原話這麼說的, In lab tests, we have compared the performance of these applications over HTTP and SPDY, and have observed up to 64% reductions in page load times in SPDY.
SPDY並不用於取代HTTP,它只是修改了HTTP的請求與應答在網路上傳輸的方式;這意味著只需增加一個SPDY傳輸層,現有的所有服務端應用均不用做任何修改。 當使用SPDY的方式傳輸,HTTP請求會被處理、標記簡化和壓縮。比如,每一個SPDY端點會持續跟蹤每一個在之前的請求中已經傳送的HTTP報文頭部,從而避免重複傳送還未改變的頭部。而還未傳送的報文的資料部分將在被壓縮後被髮送。
HTTP/2主要特性包括:
二進位制協議
HTTP/1.1 版的頭資訊肯定是文字(ASCII編碼),資料體可以是文字,也可以是二進位制。HTTP/2 則是一個徹底的二進位制協議,頭資訊和資料體都是二進位制,並且統稱為"幀"(frame):頭資訊幀和資料幀。
二進位制協議的一個好處是,可以定義額外的幀。HTTP/2 定義了近十種幀,為將來的高階應用打好了基礎。如果使用文字實現這種功能,解析資料將會變得非常麻煩,二進位制解析則方便得多。
多工
HTTP/2 複用TCP連線,在一個連線裡,客戶端和瀏覽器都可以同時傳送多個請求或迴應,而且不用按照順序一一對應,這樣就避免了"隊頭堵塞"。
舉例來說,在一個TCP連線裡面,伺服器同時收到了A請求和B請求,於是先回應A請求,結果發現處理過程非常耗時,於是就傳送A請求已經處理好的部分, 接著迴應B請求,完成後,再傳送A請求剩下的部分。
這樣雙向的、實時的通訊,就叫做多工(Multiplexing)。
資料流
因為 HTTP/2 的資料包是不按順序傳送的,同一個連線裡面連續的資料包,可能屬於不同的迴應。因此,必須要對資料包做標記,指出它屬於哪個迴應。
HTTP/2 將每個請求或迴應的所有資料包,稱為一個資料流(stream)。每個資料流都有一個獨一無二的編號。資料包傳送的時候,都必須標記資料流ID,用來區分它屬於哪個資料流。另外還規定,客戶端發出的資料流,ID一律為奇數,伺服器發出的,ID為偶數。
資料流傳送到一半的時候,客戶端和伺服器都可以傳送訊號(RST_STREAM
幀),取消這個資料流。1.1版取消資料流的唯一方法,就是關閉TCP連線。這就是說,HTTP/2 可以取消某一次請求,同時保證TCP連線還開啟著,可以被其他請求使用。
客戶端還可以指定資料流的優先順序。優先順序越高,伺服器就會越早迴應。
頭資訊壓縮
HTTP 協議不帶有狀態,每次請求都必須附上所有資訊。所以,請求的很多欄位都是重複的,比如Cookie
和User Agent
,一模一樣的內容,每次請求都必須附帶,這會浪費很多頻寬,也影響速度。
HTTP/2 對這一點做了優化,引入了頭資訊壓縮機制(header compression)。一方面,頭資訊使用gzip
或compress
壓縮後再傳送;另一方面,客戶端和伺服器同時維護一張頭資訊表,所有欄位都會存入這個表,生成一個索引號,以後就不傳送同樣欄位了,只傳送索引號,這樣就提高速度了。
伺服器推送
HTTP/2 允許伺服器未經請求,主動向客戶端傳送資源,這叫做伺服器推送(server push)。
常見場景是客戶端請求一個網頁,這個網頁裡面包含很多靜態資源。正常情況下,客戶端必須收到網頁後,解析HTML原始碼,發現有靜態資源,再發出靜態資源請求。其實,伺服器可以預期到客戶端請求網頁後,很可能會再請求靜態資源,所以就主動把這些靜態資源隨著網頁一起發給客戶端了。
我給自己挖個坑,後面專門出一篇文章寫HTTP/2.
HATEOAS 約束
HATEOAS(Hypermedia as the engine of application state)是 REST 架構風格中最複雜的約束,也是構建成熟 REST 服務的核心。它的重要性在於打破了客戶端和伺服器之間嚴格的契約,使得客戶端可以更加智慧和自適應,而 REST 服務本身的演化和更新也變得更加容易。
在介紹 HATEOAS 之前,先介紹一下 Richardson 提出的 REST 成熟度模型。該模型把 REST 服務按照成熟度劃分成 4 個層次:(這個可以參考Richardson的成熟度模型,見後文連結)
- 第一個層次(Level 0)的 Web 服務只是使用 HTTP 作為傳輸方式,實際上只是遠端方法呼叫(RPC)的一種具體形式。SOAP 和 XML-RPC 都屬於此類。
- 第二個層次(Level 1)的 Web 服務引入了資源的概念。每個資源有對應的識別符號和表達。
- 第三個層次(Level 2)的 Web 服務使用不同的 HTTP 方法來進行不同的操作,並且使用 HTTP 狀態碼來表示不同的結果。如 HTTP GET 方法來獲取資源,HTTP DELETE 方法來刪除資源。
- 第四個層次(Level 3)的 Web 服務使用 HATEOAS。在資源的表達中包含了連結資訊。客戶端可以根據連結來發現可以執行的動作。
從上述 REST 成熟度模型中可以看到,使用 HATEOAS 的 REST 服務是成熟度最高的,也是推薦的做法。對於不使用 HATEOAS 的 REST 服務,客戶端和伺服器的實現之間是緊密耦合的。客戶端需要根據伺服器提供的相關文件來了解所暴露的資源和對應的操作。當伺服器發生了變化時,如修改了資源的 URI,客戶端也需要進行相應的修改。而使用 HATEOAS 的 REST 服務中,客戶端可以通過伺服器提供的資源的表達來智慧地發現可以執行的操作。當伺服器發生了變化時,客戶端並不需要做出修改,因為資源的 URI 和其他資訊都是動態發現的。
所以我們可以看到HATEOAS可以降低客戶端和伺服器之間的耦合。
我們看看在Spring官網上的例子。
下面是一個類 Customer
.
class Customer {
String name;
}
一個傳統的例子是:
{
"name" : "Alice"
}
如果變成HATEOAS風格的,可以是下面這樣:
{
"name": "Alice",
"links": [ {
"rel": "self",
"href": "http://localhost:8080/customer/1"
} ]
}
我們可以看到,不僅有了name,還多了一個links. links下的rel的值是self,意思就是說指向當前資源的連結。
關於ref的值,可以參考下表:
rel 屬性值 | 描述 |
---|---|
self | 指向當前資源本身的連結的 rel 屬性。每個資源的表達中都應該包含此關係的連結。 |
edit | 指向一個可以編輯當前資源的連結。 |
item | 如果當前資源表示的是一個集合,則用來指向該集合中的單個資源。 |
collection | 如果當前資源包含在某個集合中,則用來指向包含該資源的集合。 |
related | 指向一個與當前資源相關的資源。 |
search | 指向一個可以搜尋當前資源及其相關資源的連結。 |
first、last、previous、next | 這幾個 rel 屬性值都有集合中的遍歷相關,分別用來指向集合中的第一個、最後一個、上一個和下一個資源。 |
根據以上,我們可以清楚的看出根據rel不同的型別有不同的用處,這樣客戶端可以智慧的進行不同的操作,達到解耦的目的。
OpenAPI
其實RESTful API都是和OpenAPI相關的,為什麼會把OpenAPI單獨拿出來說?原理很簡單,那是因為現在很多API的定義,包括一些大廠的,都做的不是很好。RESTful API設計的最佳實踐文件就在這裡,但是大部分人還是沒有去遵守。關於RESTful API文件,建議去參考微軟的文章( https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design)。那麼OpenAPI是幹什麼的?說白了就是為了RESTful API,定義了一個標準,讓我們和機器不用再去檢視原始碼、文件,甚至不用像我前面檔案裡抓包那樣,去了解API的定義。
最典型的例子還是Swagger。Swagger的Editor等產品是支援OpenAPI的,總的來說,Open API的那些標準不是太難,因為現成的例子供參考。關鍵是如果利用這些將自己的產品變得更加標準,這是很重要的策略和思路。我原來在這個上面花了很多時間引入到專案裡,我覺得是值的,一個是讓產品規範了,有質的保證,二是讓自己和同事的思維提高了。
總的來說,這篇文章簡單介紹了5G和HTTP的關係,以及HTTP裡用到RESTful API,HTTP/2等技術,這和以前通訊領域是不一樣的。
參考文章:
- https://www.ibm.com/developerworks/cn/java/j-lo-SpringHATEOAS/
- https://spring.io/understanding/HATEOAS
- https://http2.github.io/http2-spec/
- https://martinfowler.com/articles/richardsonMaturityModel.html
- http://www.ruanyifeng.com/blog/2016/08/http.html
- https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design
- https://www.openapis.org/
- http://dev.chromium.org/spdy/spdy-whitepaper
- https://www.etsi.org/deliver/etsi_ts/129500_129599/129501/15.00.01_60/ts_129501v150001p.pdf