1. 程式人生 > 實用技巧 >鞏固你的HTTP知識體系

鞏固你的HTTP知識體系

前言

這次梳理的篇幅主要是涉及網路部分,包括HTTP等,對鞏固自己的網路知識體系也是很有幫助的,進一步的對效能優化而言也是幫助很大的。

但更多的是拋磚引玉,希望對你們有所幫助。

感謝掘友的鼓勵與支援,往期文章都在最後梳理出來了(●'◡'●)

接下來就以問題的形式展開梳理

談一談HTTP協議優缺點

超文字傳輸協議,「HTTP 是一個在計算機世界裡專門在兩點之間傳輸文字、圖片、音訊、視訊等超文字資料的約定和規範」。

HTTP 特點

  1. 「靈活可擴充套件」。一個是語法上只規定了基本格式,空格分隔單詞,換行分隔欄位等。另外一個就是傳輸形式上不僅可以傳輸文字,還可以傳輸圖片,視訊等任意資料。
  2. 「請求-應答模式」,通常而言,就是一方傳送訊息,另外一方要接受訊息,或者是做出相應等。
  3. 「可靠傳輸」,HTTP是基於TCP/IP,因此把這一特性繼承了下來。
  4. 「無狀態」,這個分場景回答即可。

HTTP 缺點

  1. 「無狀態」,有時候,需要儲存資訊,比如像購物系統,需要保留下顧客資訊等等,另外一方面,有時候,無狀態也會減少網路開銷,比如類似直播行業這樣子等,這個還是分場景來說。
  2. 「明文傳輸」,即協議裡的報文(主要指的是頭部)不使用二進位制資料,而是文字形式。這讓HTTP的報文資訊暴露給了外界,給攻擊者帶來了便利。
  3. 「隊頭阻塞」,當http開啟長連線時,共用一個TCP連線,當某個請求時間過長時,其他的請求只能處於阻塞狀態,這就是隊頭阻塞問題。

HTTP/1.0 HTTP1.1 HTTP2.0版本之間的差異

HTTP 0.9

  • 1991年,原型版本,功能簡陋,只有一個命令GET,只支援純文字內容,該版本已過時。

HTTP 1.0

  • 任何格式的內容都可以傳送,這使得網際網路不僅可以傳輸文字,還能傳輸影象、視訊、二進位制等檔案。
  • 除了GET命令,還引入了POST命令和HEAD命令。
  • http請求和迴應的格式改變,除了資料部分,每次通訊都必須包括頭資訊(HTTP header),用來描述一些元資料。
  • 只使用 header 中的 If-Modified-Since 和 Expires 作為快取失效的標準。
  • 不支援斷點續傳,也就是說,每次都會傳送全部的頁面和資料。
  • 通常每臺計算機只能繫結一個 IP,所以請求訊息中的 URL 並沒有傳遞主機名(hostname)

HTTP 1.1

http1.1是目前最為主流的http協議版本,從1999年釋出至今,仍是主流的http協議版本。

  • 引入了持久連線( persistent connection),即TCP連線預設不關閉,可以被多個請求複用,不用宣告Connection: keep-alive。長連線的連線時長可以通過請求頭中的keep-alive來設定
  • 引入了管道機制( pipelining),即在同一個TCP連線裡,客戶端可以同時傳送多個 請求,進一步改進了HTTP協議的效率。
  • HTTP 1.1 中新增加了 E-tag,If-Unmodified-Since, If-Match, If-None-Match 等快取控制標頭來控制快取失效。
  • 支援斷點續傳,通過使用請求頭中的Range來實現。
  • 使用了虛擬網路,在一臺物理伺服器上可以存在多個虛擬主機(Multi-homed Web Servers),並且它們共享一個IP地址。
  • 新增方法:PUT、 PATCH、 OPTIONS、 DELETE。

http1.x版本問題

  • 在傳輸資料過程中,所有內容都是明文,客戶端和伺服器端都無法驗證對方的身份,無法保證資料的安全性。
  • HTTP/1.1 版本預設允許複用TCP連線,但是在同一個TCP連線裡,所有資料通訊是按次序進行的,伺服器通常在處理完一個迴應後,才會繼續去處理下一個,這樣子就會造成隊頭阻塞。
  • http/1.x 版本支援Keep-alive,用此方案來彌補建立多次連線產生的延遲,但是同樣會給伺服器帶來壓力,並且的話,對於單檔案被不斷請求的服務,Keep-alive會極大影響效能,因為它在檔案被請求之後還保持了不必要的連線很長時間。

HTTP 2.0

  • 二進位制分幀這是一次徹底的二進位制協議,頭資訊和資料體都是二進位制,並且統稱為"幀":頭資訊幀和資料幀。
  • 頭部壓縮HTTP 1.1版本會出現「User-Agent、Cookie、Accept、Server、Range」等欄位可能會佔用幾百甚至幾千位元組,而 Body 卻經常只有幾十位元組,所以導致頭部偏重。HTTP 2.0 使用HPACK演算法進行壓縮。
  • 多路複用複用TCP連線,在一個連線裡,客戶端和瀏覽器都可以同時傳送多個請求或迴應,且不用按順序一一對應,這樣子解決了隊頭阻塞的問題。
  • 伺服器推送允許伺服器未經請求,主動向客戶端傳送資源,即伺服器推送。
  • 請求優先順序可以設定資料幀的優先順序,讓服務端先處理重要資源,優化使用者體驗。

談一談你對HTTP/2理解

頭部壓縮

HTTP 1.1版本會出現「User-Agent、Cookie、Accept、Server、Range」等欄位可能會佔用幾百甚至幾千位元組,而 Body 卻經常只有幾十位元組,所以導致頭部偏重。

HTTP 2.0 使用HPACK演算法進行壓縮。

那我們看看HPACK演算法吧

從上面看,我們可以看到類似於索引表,每個索引表對應一個值,比如索引為2對應頭部中的method頭部資訊,這樣子的話,在傳輸的時候,不在是傳輸對應的頭部資訊了,而是傳遞索引,對於之前出現過的頭部資訊,只需要把「索引」(比如1,2,...)傳給對方即可,對方拿到索引查表就行了。

這種「傳索引」的方式,可以說讓請求頭欄位得到極大程度的精簡和複用。

其次是對於整數和字串進行「哈夫曼編碼」,哈夫曼編碼的原理就是先將所有出現的字元建立一張索引表,然後讓出現次數多的字元對應的索引儘可能短,傳輸的時候也是傳輸這樣的「索引序列」,可以達到非常高的壓縮率。

多路複用

HTTP 1.x 中,如果想併發多個請求,必須使用多個 TCP 連結,且瀏覽器為了控制資源,還會對單個域名有 6-8個的TCP連結請求限制。

HTTP2中:

  • 同域名下所有通訊都在單個連線上完成。
  • 單個連線可以承載任意數量的雙向資料流。
  • 資料流以訊息的形式傳送,而訊息又由一個或多個幀組成,多個幀之間可以亂序傳送,因為根據幀首部的流標識可以重新組裝,也就是Stream ID,流識別符號,有了它,接收方就能從亂序的二進位制幀中選擇ID相同的幀,按照順序組裝成請求/響應報文。

伺服器推送

瀏覽器傳送一個請求,伺服器主動向瀏覽器推送與這個請求相關的資源,這樣瀏覽器就不用發起後續請求。

相比較http/1.1的優勢

  • 推送資源可以由不同頁面共享
  • 伺服器可以按照優先順序推送資源
  • 客戶端可以快取推送的資源
  • 客戶端可以拒收推送過來的資源

二進位制分幀

之前是明文傳輸,不方便計算機解析,對於回車換行符來說到底是內容還是分隔符,都需要內部狀態機去識別,這樣子效率低,HTTP/2採用二進位制格式,全部傳輸01串,便於機器解碼。

這樣子一個報文格式就被拆分為一個個二進位制幀,用「Headers幀」存放頭部欄位,「Data幀」存放請求體資料。這樣子的話,就是一堆亂序的二進位制幀,它們不存在先後關係,因此不需要排隊等待,解決了HTTP隊頭阻塞問題。

在客戶端與伺服器之間,雙方都可以互相傳送二進位制幀,這樣子「雙向傳輸的序列」,稱為流,所以HTTP/2中以流來表示一個TCP連線上進行多個數據幀的通訊,這就是多路複用概念。

那亂序的二進位制幀,是如何組裝成對於的報文呢?

  • 所謂的亂序,值的是不同ID的Stream是亂序的,對於同一個Stream ID的幀是按順序傳輸的。
  • 接收方收到二進位制幀後,將相同的Stream ID組裝成完整的請求報文和響應報文。
  • 二進位制幀中有一些欄位,控制著優先順序和流量控制等功能,這樣子的話,就可以設定資料幀的優先順序,讓伺服器處理重要資源,優化使用者體驗。

介紹一下HTTP 常見狀態碼

RFC 規定 HTTP 的狀態碼為「三位數」,第一個數字定義了響應的類別,被分為五類:

  • 「1xx」: 代表請求已被接受,需要繼續處理。
  • 「2xx」: 表示成功狀態。
  • 「3xx」: 重定向狀態。
  • 「4xx」: 客戶端錯誤。
  • 「5xx」: 伺服器端錯誤。

1xx 資訊類

接受的請求正在處理,資訊類狀態碼。

2xx 成功

  • 200 OK 表示從客戶端發來的請求在伺服器端被正確請求。
  • 204 No content,表示請求成功,但沒有資源可返回。
  • 206 Partial Content,該狀態碼錶示客戶端進行了範圍請求,而伺服器成功執行了這部分的 GET 請求 響應報文中包含由「Content-Range」指定範圍的實體內容。

3xx 重定向

  • 301 moved permanently,永久性重定向,表示資源已被分配了新的 URL,這時應該按 Location 首部欄位提示的 URI 重新儲存。
  • 302 found,臨時性重定向,表示資源臨時被分配了新的 URL。
  • 303 see other,表示資源存在著另一個 URL,應使用 GET 方法獲取資源。
  • 304 not modified,當協商快取命中時會返回這個狀態碼。
  • 307 temporary redirect,臨時重定向,和302含義相同,不會改變method

當 301、302、303 響應狀態碼返回時,幾乎所有的瀏覽器都會把 POST 改成 GET,並刪除請求報文內的主體,之後請求會自動再次傳送 301、302 標準是禁止將 POST 方法改變成 GET 方法的,但實際使用時大家都會這麼做

4XX 客戶端錯誤

  • 400 bad request,請求報文存在語法錯誤。
  • 401 unauthorized,表示傳送的請求需要有通過 HTTP 認證的認證資訊。
  • 403 forbidden,表示對請求資源的訪問被伺服器拒絕。
  • 404 not found,表示在伺服器上沒有找到請求的資源。
  • 405 Method Not Allowed,伺服器禁止使用該方法,客戶端可以通過options方法來檢視伺服器允許的訪問方法,如下
Access-Control-Allow-Methods→GET,HEAD,PUT,PATCH,POST,DELETE
複製程式碼

5XX 伺服器錯誤

  • 500 internal sever error,表示伺服器端在執行請求時發生了錯誤。
  • 502 Bad Gateway,伺服器自身是正常的,訪問的時候出了問題,具體啥錯誤我們不知道。
  • 503 service unavailable,表明伺服器暫時處於超負載或正在停機維護,無法處理請求。

DNS如何工作的

DNS 協議提供的是一種主機名到 IP 地址的轉換服務,就是我們常說的域名系統。是應用層協議,通常該協議執行在UDP協議之上,使用的是53埠號。

「我們通過一張圖來看看它的查詢過程吧」

這張圖很生動的展示了DNS在本地DNS伺服器是如何查詢的,「一般向本地DNS伺服器傳送請求是遞迴查詢的」

本地 DNS 伺服器向其他域名伺服器請求的過程是迭代查詢的過程

DNS查詢

遞迴查詢和迭代查詢

  • 遞迴查詢指的是查詢請求發出後,域名伺服器代為向下一級域名伺服器發出請求,最後向用戶返回查詢的最終結果。使用遞迴 查詢,使用者只需要發出一次查詢請求。
  • 迭代查詢指的是查詢請求後,域名伺服器返回單次查詢的結果。下一級的查詢由使用者自己請求。使用迭代查詢,使用者需要發出 多次的查詢請求。

所以一般而言,「本地伺服器查詢是遞迴查詢」,而「本地 DNS 伺服器向其他域名伺服器請求的過程是迭代查詢的過程」。

DNS快取

快取也很好理解,在一個請求中,當某個DNS伺服器收到一個DNS回答後,它能夠回答中的資訊快取在本地儲存器中。「返回的資源記錄中的 TTL 代表了該條記錄的快取的時間。」

DNS實現負載平衡

它是如何實現負載均衡的呢?首先我們得清楚DNS 是可以用於在冗餘的伺服器上實現負載平衡。

**原因:**這是因為一般的大型網站使用多臺伺服器提供服務,因此一個域名可能會對應 多個伺服器地址。

舉個例子來說

  • 當用戶發起網站域名的 DNS 請求的時候,DNS 伺服器返回這個域名所對應的伺服器 IP 地址的集合
  • 在每個回答中,會迴圈這些 IP 地址的順序,使用者一般會選擇排在前面的地址傳送請求。
  • 以此將使用者的請求均衡的分配到各個不同的伺服器上,這樣來實現負載均衡。

總結

  • DNS域名系統,是應用層協議,執行UDP協議之上,使用埠43。
  • 查詢過程,本地查詢是遞迴查詢,依次通過瀏覽器快取—>>本地hosts檔案—>>本地DNS解析器—>>本地DNS伺服器—>>其他域名伺服器請求。 接下來的過程就是迭代過程。
  • 遞迴查詢一般而言,傳送一次請求就夠,迭代過程需要使用者傳送多次請求。

DNS 為什麼使用 UDP 協議作為傳輸層協議?

「DNS 使用 UDP 協議作為傳輸層協議的主要原因是為了避免使用 TCP 協議時造成的連線時延。」

  • 為了得到一個域名的 IP 地址,往往會向多個域名伺服器查詢,如果使用 TCP 協議,那麼每次請求都會存在連線時延,這樣使 DNS 服務變得很慢。
  • 大多數的地址查詢請求,都是瀏覽器請求頁面時發出的,這樣會造成網頁的等待時間過長。

介紹一下Connection:keep-alive

什麼是keep-alive

我們知道HTTP協議採用“請求-應答”模式,當使用普通模式,即非KeepAlive模式時,每個請求/應答客戶和伺服器都要新建一個連線,完成 之後立即斷開連線(HTTP協議為無連線的協議);

當使用Keep-Alive模式(又稱持久連線、連線重用)時,Keep-Alive功能使客戶端到服 務器端的連線持續有效,當出現對伺服器的後繼請求時,Keep-Alive功能避免了建立或者重新建立連線。

為什麼要使用keep-alive

keep-alive技術的建立目的,能在多次HTTP之前重用同一個TCP連線,從而減少建立/關閉多個 TCP 連線的開銷(包括響應時間、CPU 資源、減少擁堵等),參考如下示意圖(來源:維基百科):

客戶端如何開啟

在HTTP/1.0協議中,預設是關閉的,需要在http頭加入"Connection: Keep-Alive”,才能啟用Keep-Alive;

Connection:keep-alive
複製程式碼

http 1.1中預設啟用Keep-Alive,如果加入"Connection: close “,才關閉。

Connection:close
複製程式碼

目前大部分瀏覽器都是用http1.1協議,也就是說預設都會發起Keep-Alive的連線請求了,所以是否能完成一個完整的Keep- Alive連線就看伺服器設定情況。


介紹HTTP 快取策略

這個跟之前的瀏覽器快取原理一樣,我直接拿我之前梳理過的吧。

我在我之前的那一篇中已經詳細的說過了,點這裡傳送門聊一聊瀏覽器快取

我們來梳理一下吧

強快取

強快取兩個相關欄位,「Expires」,「Cache-Control」。

「強快取分為兩種情況,一種是傳送HTTP請求,一種不需要傳送。」

首先檢查強快取,這個階段**不需要傳送HTTP請求。**通過查詢不同的欄位來進行,不同的HTTP版本所以不同。

  • HTTP1.0版本,使用的是Expires,HTTP1.1使用的是Cache-Control

Expires

Expires即過期時間,時間是相對於伺服器的時間而言的,存在於服務端返回的響應頭中,在這個過期時間之前可以直接從快取裡面獲取資料,無需再次請求。比如下面這樣:

Expires:Mon,29Jun202011:10:23GMT
複製程式碼

表示該資源在2020年7月29日11:10:23過期,過期時就會重新向伺服器發起請求。

這個方式有一個問題:「伺服器的時間和瀏覽器的時間可能並不一致」,所以HTTP1.1提出新的欄位代替它。

Cache-Control

HTTP1.1版本中,使用的就是該欄位,這個欄位採用的時間是過期時長,對應的是max-age。

Cache-Control:max-age=6000
複製程式碼

上面代表該資源返回後6000秒,可以直接使用快取。

當然了,它還有其他很多關鍵的指令,梳理了幾個重要的

注意點:

  • 當Expires和Cache-Control同時存在時,優先考慮Cache-Control。
  • 當然了,當快取資源失效了,也就是沒有命中強快取,接下來就進入協商快取

協商快取

強快取失效後,瀏覽器在請求頭中攜帶響應的快取Tag來向伺服器傳送請求,伺服器根據對應的tag,來決定是否使用快取。

快取分為兩種,「Last-Modified」和「ETag」。兩者各有優勢,並不存在誰對誰有絕對的優勢,與上面所講的強快取兩個Tag所不同。

Last-Modified

這個欄位表示的是「最後修改時間」。在瀏覽器第一次給伺服器傳送請求後,伺服器會在響應頭中加上這個欄位。

瀏覽器接收到後,「如果再次請求」,會在請求頭中攜帶If-Modified-Since欄位,這個欄位的值也就是伺服器傳來的最後修改時間。

伺服器拿到請求頭中的If-Modified-Since的欄位後,其實會和這個伺服器中該資源的最後修改時間對比:

  • 如果請求頭中的這個值小於最後修改時間,說明是時候更新了。返回新的資源,跟常規的HTTP請求響應的流程一樣。
  • 否則返回304,告訴瀏覽器直接使用快取。

ETag

ETag是伺服器根據當前檔案的內容,對檔案生成唯一的標識,比如MD5演算法,只要裡面的內容有改動,這個值就會修改,伺服器通過把響應頭把該欄位給瀏覽器。

瀏覽器接受到ETag值,會在下次請求的時候,將這個值作為「If-None-Match」這個欄位的內容,發給伺服器。

伺服器接收到「If-None-Match」後,會跟伺服器上該資源的「ETag」進行比對

  • 如果兩者一樣的話,直接返回304,告訴瀏覽器直接使用快取
  • 如果不一樣的話,說明內容更新了,返回新的資源,跟常規的HTTP請求響應的流程一樣

兩者對比

  • 效能上,Last-Modified優於ETag,Last-Modified記錄的是時間點,而Etag需要根據檔案的MD5演算法生成對應的hash值。
  • 精度上,ETag優於Last-Modified。ETag按照內容給資源帶上標識,能準確感知資源變化,Last-Modified在某些場景並不能準確感知變化,比如編輯了資原始檔,但是檔案內容並沒有更改,這樣也會造成快取失效。Last-Modified 能夠感知的單位時間是秒,如果檔案在 1 秒內改變了多次,那麼這時候的 Last-Modified 並沒有體現出修改了。

最後,「如果兩種方式都支援的話,伺服器會優先考慮ETag」。

快取位置

接下來我們考慮使用快取的話,快取的位置在哪裡呢?

瀏覽器快取的位置的話,可以分為四種,優先順序從高到低排列分別

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

Service Worker

這個應用場景比如PWA,它借鑑了Web Worker思路,由於它脫離了瀏覽器的窗體,因此無法直接訪問DOM。它能完成的功能比如:離線快取、訊息推送和網路代理,其中離線快取就是「Service Worker Cache」。

Memory Cache

指的是記憶體快取,從效率上講它是最快的,從存活時間來講又是最短的,當渲染程序結束後,記憶體快取也就不存在了。

Disk Cache

儲存在磁碟中的快取,從存取效率上講是比記憶體快取慢的,優勢在於儲存容量和儲存時長。

Disk Cache VS Memory Cache

兩者對比,主要的策略

內容使用率高的話,檔案優先進入磁碟

比較大的JS,CSS檔案會直接放入磁碟,反之放入記憶體。

Push Cache

推送快取,這算是瀏覽器中最後一道防線吧,它是HTTP/2的內容。具體我也不是很清楚,有興趣的可以去了解。

總結

  • 首先檢查Cache-Control, 嚐鮮,看強快取是否可用
  • 如果可用的話,直接使用
  • 否則進入協商快取,傳送HTTP請求,伺服器通過請求頭中的If-Modified-Since或者If-None-Match欄位檢查資源是否更新
  • 資源更新,返回資源和200狀態碼。
  • 否則,返回304,直接告訴瀏覽器直接從快取中去資源。

說一說HTTP 的請求方法?

  • HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法
  • HTTP1.1新增了五種請求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT

http/1.1規定了以下請求方法(注意,都是大寫):

  • GET: 請求獲取Request-URI所標識的資源
  • POST: 在Request-URI所標識的資源後附加新的資料
  • HEAD: 請求獲取由Request-URI所標識的資源的響應訊息報頭
  • PUT: 請求伺服器儲存一個資源,並用Request-URI作為其標識(修改資料)
  • DELETE: 請求伺服器刪除對應所標識的資源
  • TRACE: 請求伺服器回送收到的請求資訊,主要用於測試或診斷
  • CONNECT: 建立連線隧道,用於代理伺服器
  • OPTIONS: 列出可對資源實行的請求方法,用來跨域請求

談一談GET 和 POST 的區別

本質上,只是語義上的區別,GET 用於獲取資源,POST 用於提交資源。

想裝逼請參考 https://zhuanlan.zhihu.com/p/22536382

具體差別

  • 從快取角度看,GET 請求後瀏覽器會主動快取,POST 預設情況下不能。
  • 從引數角度來看,GET請求一般放在URL中,因此不安全,POST請求放在請求體中,相對而言較為安全,但是在抓包的情況下都是一樣的。
  • 從編碼角度看,GET請求只能經行URL編碼,只能接受ASCII碼,而POST支援更多的編碼型別且不對資料型別限值。
  • GET請求冪等,POST請求不冪等,冪等指傳送 M 和 N 次請求(兩者不相同且都大於1),伺服器上資源的狀態一致。
  • GET請求會一次性發送請求報文,POST請求通常分為兩個TCP資料包,首先發 header 部分,如果伺服器響應 100(continue), 然後發 body 部分。

從應用場景角度來看,Get 多用於無副作用,冪等的場景,例如搜尋關鍵字。Post 多用於副作用,不冪等的場景,例如註冊。


options 方法有什麼用?

  • OPTIONS 請求與 HEAD 類似,一般也是用於客戶端檢視伺服器的效能。
  • 這個方法會請求伺服器返回該資源所支援的所有 HTTP 請求方法,該方法會用'*'來代替資源名稱,向伺服器傳送 OPTIONS 請求,可以測試伺服器功能是否正常。
  • JS 的 XMLHttpRequest物件進行 CORS 跨域資源共享時,對於複雜請求,就是使用 OPTIONS 方法傳送嗅探請求,以判斷是否有對指定資源的訪問許可權。

談一談你對URL理解

統一資源定位符的簡稱,Uniform Resource Locator,常常被稱為網址,是因特網上標準的資源地址。

組成

通用的格式:scheme://host[:port]/path/…/?query#anchor

名稱功能scheme訪問伺服器以獲取資源時要使用哪種協議,比如:http,https 和 FTP 等hostHTTP 伺服器的 IP 地址或者域名portHTTP 伺服器的預設埠是 80,HTTPS預設埠是443,這種情況下埠號可以省略,如果使用了別的埠,必須指明。不同的埠,你可以認為是不同的應用程式。path訪問資源的路徑query-string發給 http 伺服器的資料anchor錨點

舉個例子

https://www.baidu.com/s?tn=baidu&bar=&wd=TianTian
複製程式碼

這個URL中,https就是協議,www.baidu.com就是域名,預設埠是443,/s就是請求的path,tn=baidu&bar=&wd=TianTian這個就是query

URL 編碼

  • URL 只能使用 ASCII 字符集來通過因特網進行傳送。
  • 由於 URL 常常會包含 ASCII 集合之外的字元,URL 必須轉換為有效的 ASCII 格式。
  • URL 編碼使用 "%" 其後跟隨兩位的十六進位制數來替換非 ASCII 字元。
  • URL 不能包含空格。URL 編碼通常使用 + 來替換空格。

舉個例子

天天轉換為有效的ASCII格式就是%CC%EC%CC%EC


談一談隊頭阻塞問題

什麼是隊頭阻塞?

對於每一個HTTP請求而言,這些任務是會被放入一個任務佇列中序列執行的,一旦隊首任務請求太慢時,就會阻塞後面的請求處理,這就是HTTP隊頭阻塞問題。

有什麼解決辦法嗎

併發連線

我們知道對於一個域名而言,是允許分配多個長連線的,那麼可以理解成增加了任務佇列,也就是說不會導致一個任務阻塞了該任務佇列的其他任務,在RFC規範中規定客戶端最多併發2個連線,不過實際情況就是要比這個還要多,舉個例子,Chrome中是6個。

域名分片

顧名思義,我們可以在一個域名下分出多個二級域名出來,而它們最終指向的還是同一個伺服器,這樣子的話就可以併發處理的任務佇列更多,也更好的解決了隊頭阻塞的問題。

舉個例子,比如TianTian.com,可以分出很多二級域名,比如Day1.TianTian.com,Day2.TianTian.com,Day3.TianTian.com,這樣子就可以有效解決隊頭阻塞問題。


談一談HTTP資料傳輸

大概遇到的情況就分為「定長資料」與「不定長資料」的處理吧。

定長資料

對於定長的資料包而言,傳送端在傳送資料的過程中,需要設定Content-Length,來指明發送資料的長度。

當然瞭如果採用了Gzip壓縮的話,Content-Length設定的就是壓縮後的傳輸長度。

我們還需要知道的是

  • Content-Length如果存在並且有效的話,則必須和訊息內容的傳輸長度完全一致,也就是說,如果過短就會截斷,過長的話,就會導致超時。
  • 如果採用短連結的話,直接可以通過伺服器關閉連線來確定訊息的傳輸長度。
  • 那麼在HTTP/1.0之前的版本中,Content-Length欄位可有可無,因為一旦伺服器關閉連線,我們就可以獲取到傳輸資料的長度了。
  • 在HTTP/1.1版本中,如果是Keep-alive的話,chunked優先順序高於Content-Length,若是非Keep-alive,跟前面情況一樣,Content-Length可有可無。

那怎麼來設定Content-Length

舉個例子來看看

constserver=require('http').createServer();
server.on('request',(req,res)=>{
if(req.url==='/index'){
//設定資料型別
res.setHeader('Content-Type','text/plain');
res.setHeader('Content-Length',10);
res.write("你好,使用的是Content-Length設定傳輸資料形式");
}
})

server.listen(3000,()=>{
console.log("成功啟動--TinaTian");
})

複製程式碼

不定長資料

現在採用最多的就是HTTP/1.1版本,來完成傳輸資料,在儲存Keep-alive狀態下,當資料是不定長的時候,我們需要設定新的頭部欄位

Transfer-Encoding:chunked
複製程式碼

通過chunked機制,可以完成對不定長資料的處理,當然了,你需要知道的是

  • 如果頭部資訊中有Transfer-Encoding,優先採用Transfer-Encoding裡面的方法來找到對應的長度。
  • 如果設定了Transfer-Encoding,那麼Content-Length將被忽視。
  • 使用長連線的話,會持續的推送動態內容。

那我們來模擬一下吧

constserver=require('http').createServer();
server.on('request',(req,res)=>{
if(req.url==='/index'){
//設定資料型別
res.setHeader('Content-Type','text/html;charset=utf8');
res.setHeader('Content-Length',10);
res.setHeader('Transfer-Encoding','chunked');

res.write("你好,使用的是Transfer-Encoding設定傳輸資料形式");
setTimeout(()=>{
res.write("第一次傳輸資料給您<br/>");
},1000);
res.write("騷等一下");
setTimeout(()=>{
res.write("第一次傳輸資料給您");
res.end()
},3000);
}
})

server.listen(3000,()=>{
console.log("成功啟動--TinaTian");
})

複製程式碼

上面使用的是nodejs中http模組,有興趣的小夥伴可以去試一試,以上就是HTTP對「定長資料」和「不定長資料」傳輸過程中的處理手段。


介紹一下HTTPS和HTTP區別

HTTPS 要比 HTTPS 多了 secure 安全性這個概念,實際上, HTTPS 並不是一個新的應用層協議,它其實就是 HTTP + TLS/SSL 協議組合而成,而安全性的保證正是 SSL/TLS 所做的工作。

「SSL」

安全套接層(Secure Sockets Layer)

「TLS」

(傳輸層安全,Transport Layer Security)

現在主流的版本是 TLS/1.2, 之前的 TLS1.0、TLS1.1 都被認為是不安全的,在不久的將來會被完全淘汰。

「HTTPS 就是身披了一層 SSL 的 HTTP」。

HTTP與HTTPS區別

那麼區別有哪些呢

  • HTTP 是明文傳輸協議,HTTPS 協議是由 SSL+HTTP 協議構建的可進行加密傳輸、身份認證的網路協議,比 HTTP 協議安全。
  • HTTPS比HTTP更加安全,對搜尋引擎更友好,利於SEO,谷歌、百度優先索引HTTPS網頁。
  • HTTPS標準埠443,HTTP標準埠80。
  • HTTPS需要用到SSL證書,而HTTP不用。

我覺得記住以下兩點HTTPS主要作用就行

  1. 對資料進行加密,並建立一個資訊保安通道,來保證傳輸過程中的資料安全;
  2. 對網站伺服器進行真實身份認證。

介紹一個HTTPS工作原理

上一節來看,我們可以把HTTPS理解成「HTTPS = HTTP + SSL/TLS」

TLS/SSL 的功能實現主要依賴於三類基本演算法:雜湊函式、對稱加密和非對稱加密,其利用非對稱加密實現身份認證和金鑰協商,對稱加密演算法採用協商的金鑰對資料加密,基於雜湊函式驗證資訊的完整性。

對稱加密

加密和解密用同一個祕鑰的加密方式叫做對稱加密。Client客戶端和Server端共用一套金鑰,這樣子的加密過程似乎很讓人理解,但是隨之會產生一些問題。

「問題一:」WWW全球資訊網有許許多多的客戶端,不可能都用祕鑰A進行資訊加密,這樣子很不合理,所以解決辦法就是使用一個客戶端使用一個金鑰進行加密。

「問題二:「既然不同的客戶端使用不同的金鑰,那麼」對稱加密的金鑰如何傳輸?」那麼解決的辦法只能是「一端生成一個祕鑰,然後通過HTTP傳輸給另一端」,那麼這樣子又會產生新的問題。

「問題三:」這個傳輸金鑰的過程,又如何保證加密?「如果被中間人攔截,金鑰也會被獲取,」那麼你會說對金鑰再進行加密,那又怎麼儲存對金鑰加密的過程,是加密的過程?

到這裡,我們似乎想明白了,使用對稱加密的方式,行不通,所以我們需要採用非對稱加密

非對稱加密

通過上面的分析,對稱加密的方式行不通,那麼我們來梳理一下非對稱加密。採用的演算法是RSA,所以在一些文章中也會看見「傳統RSA握手」,基於現在TLS主流版本是1.2,所以接下來梳理的是「TLS/1.2握手過程」。

非對稱加密中,我們需要明確的點是

  • 有一對祕鑰,「公鑰」和「私鑰」。
  • 公鑰加密的內容,只有私鑰可以解開,私鑰加密的內容,所有的公鑰都可以解開,這裡說的「公鑰都可以解開,指的是一對祕鑰」。
  • 公鑰可以傳送給所有的客戶端,私鑰只儲存在伺服器端。

主要工作流程

梳理起來,可以把「TLS 1.2 握手過程」分為主要的五步

圖片內容來自浪裡行舟

步驟(1)

Client發起一個HTTPS請求,連線443埠。這個過程可以理解成是「請求公鑰的過程」。

步驟(2)

Server端收到請求後,通過第三方機構私鑰加密,會把數字證書(也可以認為是公鑰證書)傳送給Client。

步驟(3)

  • 瀏覽器安裝後會自動帶一些權威第三方機構公鑰,使用匹配的公鑰對數字簽名進行解密。
  • 根據簽名生成的規則對網站資訊進行本地簽名生成,然後兩者比對。
  • 通過比對兩者簽名,匹配則說明認證通過,不匹配則獲取證書失敗。

步驟(4)

在安全拿到「伺服器公鑰」後,客戶端Client隨機生成一個「對稱金鑰」,使用「伺服器公鑰」(證書的公鑰)加密這個「對稱金鑰」,傳送給Server(伺服器)。

步驟(5)

Server(伺服器)通過自己的私鑰,對資訊解密,至此得到了「對稱金鑰」,此時兩者都擁有了相同的「對稱金鑰」。

接下來,就可以通過該對稱金鑰對傳輸的資訊加密/解密啦,從上面圖舉個例子

  • Client使用者使用該「對稱金鑰」加密'明文內容B',傳送給Server(伺服器)
  • Server使用該「對稱金鑰」進行解密訊息,得到明文內容B。

接下來考慮一個問題,「如果公鑰被中間人拿到纂改怎麼辦呢?」

以下圖片來自leocoder

中間人獲取公鑰

「客戶端可能拿到的公鑰是假的,解決辦法是什麼呢?」

第三方認證

客戶端無法識別傳回公鑰是中間人的,還是伺服器的,這是問題的根本,我們是不是可以通過某種規範可以讓客戶端和伺服器都遵循某種約定呢?那就是通過「第三方認證的方式」

在HTTPS中,通過「證書」+「數字簽名」來解決這個問題。

這裡唯一不同的是,假設對網站資訊加密的演算法是MD5,通過MD5加密後,「然後通過第三方機構的私鑰再次對其加密,生成數字簽名」。

這樣子的話,數字證書包含有兩個特別重要的資訊「某網站公鑰+數字簽名」

我們再次假設中間人擷取到伺服器的公鑰後,去替換成自己的公鑰,因為有數字簽名的存在,這樣子客戶端驗證發現數字簽名不匹配,這樣子就防止中間人替換公鑰的問題。

那麼客戶端是如何去對比兩者數字簽名的呢?

  • 瀏覽器會去安裝一些比較權威的第三方認證機構的公鑰,比如VeriSign、Symantec以及GlobalSign等等。
  • 驗證數字簽名的時候,會直接從本地拿到相應的第三方的公鑰,對私鑰加密後的數字簽名進行解密得到真正的簽名。
  • 然後客戶端利用簽名生成規則進行簽名生成,看兩個簽名是否匹配,如果匹配認證通過,不匹配則獲取證書失敗。

數字簽名作用

數字簽名:將網站的資訊,通過特定的演算法加密,比如MD5,加密之後,再通過伺服器的私鑰進行加密,形成「加密後的數字簽名」。

第三方認證機構是一個公開的平臺,中間人可以去獲取。

如果沒有數字簽名的話,這樣子可以就會有下面情況

從上面我們知道,如果「只是對網站資訊進行第三方機構私鑰加密」的話,還是會受到欺騙。

因為沒有認證,所以中間人也向第三方認證機構進行申請,然後攔截後把所有的資訊都替換成自己的,客戶端仍然可以解密,並且無法判斷這是伺服器的還是中間人的,最後造成資料洩露。

「總結」

  • HTTPS就是使用SSL/TLS協議進行加密傳輸
  • 大致流程:客戶端拿到伺服器的公鑰(是正確的),然後客戶端隨機生成一個「對稱加密的祕鑰」,使用「該公鑰」加密,傳輸給服務端,服務端再通過解密拿到該「對稱祕鑰」,後續的所有資訊都通過該「對稱祕鑰」進行加密解密,完成整個HTTPS的流程。
  • 「第三方認證」,最重要的是「數字簽名」,避免了獲取的公鑰是中間人的。

SSL 連線斷開後如何恢復?

一共有兩種方法來恢復斷開的 SSL 連線,一種是使用 session ID,一種是 session ticket。

通過session ID

使用 session ID 的方式,每一次的會話都有一個編號,當對話中斷後,下一次重新連線時,只要客戶端給出這個編號,伺服器如果有這個編號的記錄,那麼雙方就可以繼續使用以前的祕鑰,而不用重新生成一把。目前所有的瀏覽器都支援這一種方法。但是這種方法有一個缺點是,session ID 只能夠存在一臺伺服器上,如果我們的請求通過負載平衡被轉移到了其他的伺服器上,那麼就無法恢復對話。

通過session ticket

另一種方式是 session ticket 的方式,session ticket 是伺服器在上一次對話中傳送給客戶的,這個 ticket 是加密的,只有伺服器能夠解密,裡面包含了本次會話的資訊,比如對話祕鑰和加密方法等。這樣不管我們的請求是否轉移到其他的伺服器上,當伺服器將 ticket 解密以後,就能夠獲取上次對話的資訊,就不用重新生成對話祕鑰了。

短輪詢、長輪詢和 WebSocket 間的區別?

短輪詢

短輪詢的基本思路:

  • 瀏覽器每隔一段時間向瀏覽器傳送 http 請求,伺服器端在收到請求後,不論是否有資料更新,都直接進行 響應。
  • 這種方式實現的即時通訊,本質上還是瀏覽器傳送請求,伺服器接受請求的一個過程,通過讓客戶端不斷的進行請求,使得客戶端能夠模擬實時地收到伺服器端的資料的變化。

優缺點

  • 優點是比較簡單,易於理解。
  • 缺點是這種方式由於需要不斷的建立 http 連線,嚴重浪費了伺服器端和客戶端的資源。當用戶增加時,伺服器端的壓力就會變大,這是很不合理的。

長輪詢

長輪詢的基本思路:

  • 首先由客戶端向伺服器發起請求,當伺服器收到客戶端發來的請求後,伺服器端不會直接進行響應,而是先將 這個請求掛起,然後判斷伺服器端資料是否有更新。
  • 如果有更新,則進行響應,如果一直沒有資料,則到達一定的時間限制才返回。客戶端 JavaScript 響應處理函式會在處理完伺服器返回的資訊後,再次發出請求,重新建立連線。

優缺點

  • 長輪詢和短輪詢比起來,它的優點是「明顯減少了很多不必要的 http 請求次數」,相比之下節約了資源。
  • 長輪詢的缺點在於,連線掛起也會導致資源的浪費。

WebSocket

  • WebSocket 是 Html5 定義的一個新協議,與傳統的 http 協議不同,該協議允許由伺服器主動的向客戶端推送資訊。
  • 使用 WebSocket 協議的缺點是在伺服器端的配置比較複雜。WebSocket 是一個全雙工的協議,也就是通訊雙方是平等的,可以相互發送訊息。

說一說正向代理和反向代理

正向代理

我們常說的代理也就是指正向代理,正向代理的過程,它隱藏了真實的請求客戶端,服務端不知道真實的客戶端是誰,客戶端請求的服務都被代理伺服器代替來請求。

反向代理

這種代理模式下,它隱藏了真實的服務端,當我們向一個網站發起請求的時候,背後可能有成千上萬臺伺服器為我們服務,具體是哪一臺,我們不清楚,我們只需要知道反向代理伺服器是誰就行,而且反向代理伺服器會幫我們把請求轉發到真實的伺服器那裡去,一般而言反向代理伺服器一般用來實現負載平衡。

負載平衡的兩種實現方式?

  • 一種是使用反向代理的方式,使用者的請求都發送到反向代理服務上,然後由反向代理伺服器來轉發請求到真實的伺服器上,以此來實現叢集的負載平衡。
  • 另一種是 DNS 的方式,DNS 可以用於在冗餘的伺服器上實現負載平衡。因為現在一般的大型網站使用多臺伺服器提供服務,因此一個域名可能會對應多個伺服器地址。當用戶向網站域名請求的時候,DNS 伺服器返回這個域名所對應的伺服器 IP 地址的集合,但在每個回答中,會迴圈這些 IP 地址的順序,使用者一般會選擇排在前面的地址傳送請求。以此將使用者的請求均衡的分配到各個不同的伺服器上,這樣來實現負載均衡。這種方式有一個缺點就是,由於 DNS 伺服器中存在快取,所以有可能一個伺服器出現故障後,域名解析仍然返回的是那個 IP 地址,就會造成訪問的問題。