這篇純粹滿足自己的好奇心
我好像是一個在海邊玩耍的孩子,不時為拾到比通常更光滑的石子或更美麗的 貝殼而歡欣鼓舞,而展現在我面前的是完全未探明的真理之海。牛頓
寫本文的時候,想起高中物理課本的一句話:
我好像是一個在海邊玩耍的孩子,不時為拾到比通常更光滑的石子或更美麗的貝殼而歡欣鼓舞,而展現在我面前的是完全未探明的真理之海。
那個時候不懂這句話,忙於刷分,如今純粹是為了自己的好奇心而探究一些問題,腦海中又開始復現這句話。本文的問題來自於前面的一篇文章:《HTTP學習筆記(三) HTTP/2》, 這篇文章裏我們提到了HTTP/2的幾個特點:
- is binary, instead of textual
二進制代替了文本
- is fully multiplexed, instead of ordered and blocking
多路複用
- can therefore use one connection for parallelism
並行請求
- uses header compression to reduce overhead
壓縮請求頭,減少消耗
- allows servers to “push” responses proactively into client caches
允許服務器主動推送響應進入客户端的緩存中
其實對於1我是不理解的,畢竟在計算機的世界都是“二進制”嘛,當時我的想法是難道是跟JDK處理String一樣的操作,在JDK8之前,String本身是藉助於char來存儲的:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
到了JDK 8之後, JDK藉助byte來存儲字符串:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
@Stable
private final byte[] value;
}
畢竟一個char佔兩個字節, 一個byte只佔一個字節,因為我之前用程序連接過充電樁,接收充電樁的報文,給的報文都是byte類型的,byte更小,像String就帶了一些額外的信息,所以我猜想,是這個意義上的二進制,但是這只是猜想,我想過用抓包工具去驗證我的猜想,但是發現抓包工具我用的並部署,再加上HTTP/2.0都是加密報文,抓包挺麻煩的,我也想過看HTTP Client的源碼,但是這兩個見效都太慢了,最近偶然翻看MongDB的文檔,翻到了這方面的説明,這個問題就有了答案。其實HTTP也對上面的二進制進行了解釋:
Why is HTTP/2 binary?
Binary protocols are more efficient to parse, more compact “on the wire”, and most importantly, they are much less error-prone, compared to textual protocols like HTTP/1.x, because they often have a number of affordances to “help” with things like whitespace handling, capitalization, line endings, blank lines and so on.
二進制協議相對於文本協議,比如HTTP/1.x ,解析效率、傳輸效率更高,有更好的容錯性。還提供了一些機制可以幫助處理空白字符、大小寫、空行等等。
For example, HTTP/1.1 defines four different ways to parse a message; in HTTP/2, there’s just one code path.
例如,HTTP/1.1定義了四種解析數據的方式,但是在HTTP/2, 只有一種代碼路徑。
It’s true that HTTP/2 isn’t usable through telnet, but we already have some tool support, such as a Wireshark plugin.
雖然HTTP/2已經不能再使用Telnet了,但是我們也有其他工具的支持,比如 Wireshark plugin。
所以HTTP説自己是二進制的,潛台詞是HTTP/2的數據包採取了高度結構化的格式,因為在計算機中,最終一切都是二進制形式存在。在HTTP/2中傳輸的數據會被格式化為幀(frame), 每個幀都會被分配一個流。HTTP/2的幀具備特定的格式,如下圖所示:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
在HTTP/2中,每個幀都由兩部分組成: 幀頭(9個字節),幀頭是固定長度的,佔據9個字節,包含了關於該幀的一些信息,比如長度、類型等。幀頭後面是有效載荷,長度可變,取決於幀的類型和內容。這有點類似於TCP數據包,讀取HTTP/2幀可以遵循定義好的過程(先讀取數據長度,然後幀的類型)。相比之下,HTTP/1.1是由一個ASCII編碼的文本行組成的非結構化格式,雖然這些文本最終將以二進制形式傳輸,但是基本上它是一串字符的流,而不是明確地被分為獨立的幀。
HTTP/1.1的消息通過逐個字符地讀取字符來解析,直到達到換行字符為止,這種方式有點混亂,但是因為無法知道每行的長度,所以必須逐個字符進行處理。對於HTTP正文的長度可以提前知道,我們可以在HTTP頭裏獲知到這個信息。
這讓我想起了MongDB的BSON,我想BSON中的binary的語義應當和HTTP/2的binary語義是對等的,在BSON規範的官網可以看到我們的猜想是正確的:
BSON is a binary format in which zero or more ordered key/value pairs are stored as a single entity. We call this entity a document.
BSON是一種二進制格式,其中有零個或多個有序的鍵/值對被存儲為一個單一的實體,我們將這個實體稱之為文檔。
下面是一個JSON和其對應的BSON格式的示例:
{"hello": "world"} →
\x16\x00\x00\x00 // total document size 總的文檔大小 算上大小字段本身
\x02 // 0x02 = type String
hello\x00 // field name 字段值
\x06\x00\x00\x00world\x00 // field value 字段值
\x00 // 0x00 = type EOO ('end of object')
總結一下,在計算機中最終一切都是二進制格式,當我們在數據格式中看到二進制時,我們可以理解為這種存儲結構是高度結構化的 , 讀取效率更高,更為緊湊,將數據重新進行佈局。