Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲成人免费在线,亚洲精品精品,91在线免费看

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          沃爾瑪基于前后端的消息通知框架介紹和源碼

          服務(wù)是一種流行的設(shè)計模式,其中一個大型應(yīng)用程序被分解為多個獨立且松散耦合的服務(wù),這些服務(wù)通過預(yù)定義的接口相互通信;Walmart 的ML平臺使用相同的原理構(gòu)建: 部署在 Kubernetes 集群中的獨立服務(wù)通過 REST API 進行通信。

          作為平臺功能,為事件提供以用戶為目標(biāo)的通知是一項優(yōu)先要求。為此,我們開發(fā)了一個模型框架,可供任何對通知服務(wù)感興趣的基于微服務(wù)的應(yīng)用程序使用。

          在較高級別上,系統(tǒng)應(yīng)該能夠根據(jù)以下規(guī)則生成和處理通知。

          1. 每個服務(wù)都可以獨立生成針對一個用戶或一組用戶的通知。
          2. 針對用戶的所有通知都將存儲在通知存儲中
          3. 對于在線用戶,通知消息應(yīng)立即在 UI 中彈出。對于離線用戶,一旦他們登錄,通知應(yīng)該在通知托盤中可用。
          4. 用戶可以將通知標(biāo)記為已讀或刪除舊通知。
          5. 如果需要,系統(tǒng)還可以選擇清除舊通知。

          除了所需的規(guī)則外,我們還向系統(tǒng)添加了一些所需的功能:

          1. 系統(tǒng)不應(yīng)給微服務(wù)帶來重大負擔(dān)。
          2. 系統(tǒng)應(yīng)該是快速且相當(dāng)穩(wěn)定的。
          3. 雖然丟失通知是不可取的,但可以稍微延遲發(fā)送它們。

          我們將整個系統(tǒng)設(shè)計為一個基于 Java 的庫,可以將其導(dǎo)入任何有興趣發(fā)送通知的微服務(wù)中。對于通知存儲,Redis 是首選,服務(wù)器發(fā)送事件 (SSE) 用于將通知發(fā)送到 UI 客戶端(用戶的瀏覽器)。我們將在后續(xù)部分中分別介紹每個系統(tǒng),然后將它們放在一起,看看它們是如何加起來完成此功能的。

          后端實現(xiàn)

          1. 通知模型

          通知結(jié)構(gòu)的一個非常簡單的設(shè)計需要兩個字段——目標(biāo)用戶和消息。這是我們最初采用的結(jié)構(gòu),隨著功能開始成熟,添加了更多字段以增強界面并在數(shù)據(jù)結(jié)構(gòu)中捆綁更多信息。最后,我們正式確定了這種通知結(jié)構(gòu)。

          {
              "id": 1234,
              "message": "Lorem ipsum",
              "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
              "status": "NEW",
              "severity": "INFO",
              "source": "project",
              "displayType": "INBOX",
              "createdAt": 1621327682,
              "expiryAt": 1621932482
          }
          

          2. 存儲通知

          在 Notification Store 中存儲通知有兩個要點需要考慮:

          • 給定通知,應(yīng)該很容易將其推送到存儲
          • 給定用戶,應(yīng)該很容易從存儲獲取他們的通知。

          作為鍵值存儲的 Redis 以毫秒級的延遲完成了這兩項任務(wù)。此外,Redis 已被證明具有故障恢復(fù)能力和高度可擴展性。因此,它被選為通知的后備存儲。

          此外,Redis 具有對更高 ADT 的內(nèi)置支持,例如列表和映射。我們利用映射(即 Redis 中的哈希)來存儲通知。對于每個用戶,存儲通知 JSON 的相應(yīng)通知 id 哈希值。每個唯一的用戶 ID 都充當(dāng) Redis 中的一個鍵。這種結(jié)構(gòu)確保支持系統(tǒng)中的 232 個用戶,每個用戶都有 232 個潛在的通知。

          Redis 可以以線程安全的方式支持高并發(fā)工作負載。它還可以通過 Redis Sentinels 提供生產(chǎn)級支持以實現(xiàn)高可用性和 AOF 文件備份以實現(xiàn)持久性。有關(guān)于如何設(shè)置它們以運行生產(chǎn)級 Redis 集群的優(yōu)秀文檔(請參閱Redis Sentinel和Redis Persistence)。

          3.推送通知到存儲

          通知庫公開了NotifierClient具有notifyUsers和notifyGroup方法的接口。要觸發(fā)通知,微服務(wù)將notifyUsers使用Notification對象和要向其發(fā)送通知的用戶 ID 列表調(diào)用該方法。該庫還允許創(chuàng)建可用于將相似用戶(例如,特定項目的所有用戶、使用 GPU 的所有用戶等)聚集在一起的組,并且微服務(wù)可以選擇使用notifyGroup方法向整個組發(fā)送通知。

          Jedis是 Java 中最著名的與 Redis 通信的庫,我們在庫中使用它來讀寫通知。Jedis 支持 Sentinel 支持和連接池等高級功能,這也使其成為生產(chǎn)服務(wù)器的理想選擇。

          為了防止讀取和寫入偶爾中斷,對 Redis 的調(diào)用通過Resilience4J 進行包裝,以確保在出現(xiàn)臨時故障時進行正確的重試和錯誤處理。

          前端實現(xiàn)

          為了啟用前端,我們使用 Express 服務(wù)器作為用戶瀏覽器和后端微服務(wù)之間的中間件來執(zhí)行身份驗證和會話管理。我們搭載在這個服務(wù)器上從 Redis 讀取通知并將它們推送給用戶。

          1. 將通知推送到瀏覽器

          服務(wù)器發(fā)送事件 (SSE) 是一種建立在 HTTP 之上的技術(shù)。對于登錄到系統(tǒng)的每個用戶,我們在用戶會話期間建立一個持久的 HTTP 連接。SSE 的協(xié)議規(guī)范規(guī)定將 JSON 數(shù)據(jù)轉(zhuǎn)換為字符串,并且每個事件以兩個換行符結(jié)尾。

          建立連接后,我們利用 Node.js 事件模型在 Redis 提供新通知時將數(shù)據(jù)推送到客戶端。我們發(fā)出一個通知事件,該事件附加到 HTTP SSE 處理程序范圍內(nèi)的事件偵聽器。我們使用登錄用戶的唯一 id 將來自 Redis 的消息與用戶的連接進行匹配。

          這個GitHub 代碼片段描述了它是如何完成的。

          2.在瀏覽器中接收通知

          在客戶端,SSE 提供了一個 EventSource API,允許我們連接到服務(wù)器并從它接收更新。SSE 有一個限制,它可以同時支持六個并發(fā)連接。由于我們在每個瀏覽器選項卡中打開一個新連接,因此它限制了我們的用戶一次只能打開六個選項卡。為了規(guī)避這個限制,我們使用SharedWorkers。這使我們能夠在 SharedWorker 中創(chuàng)建一個持久連接,并通過不同的瀏覽器選項卡和 iframe 訪問它。

          共享工作者SharedWorker的一個缺點是它在 Safari 和 IE 上不受支持,但由于我們的大多數(shù)用戶群都在 Chrome 和 Firefox 上,因此這被視為可接受的解決方案。如果用戶使用 IE 或 Safari,我們將退回到僅允許 6 個選項卡的 SSE 模型。

          當(dāng)用戶第一次登錄時,會實例化一個新的共享工作者實例,然后將其附加到窗口實例。然后可以在所有瀏覽器上下文中訪問它。然后,網(wǎng)頁可以使用 MessagePort 對象與共享 worker 通信,并附加一個事件處理程序,每次共享 worker 推送消息時都會調(diào)用該處理程序。

          github中的以下代碼段具有在瀏覽器中接收通知的代碼。

          3. 向用戶顯示通知

          每次打開新選項卡或瀏覽器窗口時,共享工作者都會為每個新選項卡分配一個端口號。這些端口號為每個用戶保存在一個數(shù)組中。每當(dāng)生成新通知時,都會將其推送到所有端口,以使其在選項卡之間保持一致。關(guān)閉選項卡時beforeUnload會觸發(fā)一個事件,在該事件中我們從陣列中刪除相應(yīng)的端口。

          為了僅當(dāng)用戶在選項卡上處于活動狀態(tài)時才顯示通知,我們處理visibilitychange由Page Visibility API公開的事件。處理程序?qū)㈨撁鏄?biāo)記為非隱藏,然后使用來自后端的通知刷新 redux 存儲。這會觸發(fā) UI 的渲染,并且通知顯示在小吃欄中。

          橋接后端和前端

          系統(tǒng)的兩個部分協(xié)同工作以提供整個通知系統(tǒng):

          1. 一組后端服務(wù)——生成通知并將它們持久化到 Redis
          2. UI 負責(zé)向用戶顯示通知,無論是在用戶在線時實時顯示,還是在用戶上線時作為錯過的通知列表。
          • 后端到前端——通過 Redis PubSub

          對于實時場景,新的通知一生成就需要通知 UI。我們使用 Redis PubSub 創(chuàng)建從后端服務(wù)到 UI 服務(wù)器的反饋通道,然后如所述通過 HTML5 SSE 與 UI 客戶端(或用戶的瀏覽器)進行通信。

          當(dāng)生成通知并將通知寫入用戶的密鑰時,庫還會在特定頻道上生成一條 PubSub 消息,其中相應(yīng)的用戶 ID 已被修改。UI 服務(wù)器訂閱給定的 PubSub 頻道,并在接收到用戶 ID 時在其內(nèi)存中構(gòu)建通知映射。如果用戶在線,UI 服務(wù)器會在該用戶的 SSE 套接字上發(fā)送整個通知 JSON 映射,以在他的瀏覽器中呈現(xiàn)它。

          下面給出了一個粗略的序列圖,用于演示通知實例從在后端服務(wù)中生成到向用戶顯示的流程。

          • 前端到后端——通過 REST

          一旦用戶響應(yīng)通知(閱讀、點擊或刪除它),該信息必須流經(jīng)后端存儲。我們將其實現(xiàn)為 REST 端點。

          該庫本身公開了一個 Java API,它可以采用唯一的通知 id、用戶 id 和要更新的狀態(tài),并且它將用更新的狀態(tài)修補 Redis 中的通知。然后可以使用服務(wù)將這個 API 與 REST 或任何其他類似(例如 gRPC)端點包裝在一起。

          為了清除 Redis 中過期和刪除的通知,庫內(nèi)部使用了Quartz調(diào)度程序。為了確保一次只運行一個清潔器實例,使用Redlock 算法來創(chuàng)建分布式鎖定機制。

          整個框架可在https://github.com/daichi-m/notification4J 上作為庫使用。

          天項目經(jīng)理交給我一個開發(fā)任務(wù)。如果有人在前臺下了訂單就給后臺倉庫管理一個發(fā)貨通知。也就是服務(wù)端觸發(fā)一個事件,推送消息到客戶端。如果我用websocket來做還要搞個websocket服務(wù)器,而且還 有不少配置。websocket是全雙工通信,單向通信簡直是殺雞用牛刀。用輪詢吧,浪費服務(wù)器資源不說,還不一定實時,訂單處理慢了豈不是怠慢了客戶。有沒有別的選擇呢?當(dāng)然有!

          1. SSE推送技術(shù)

          SSE全稱Server-sent Events,是HTML 5 規(guī)范的一個組成部分,具體去MDN網(wǎng)站查看相關(guān)文檔。該規(guī)范十分簡單,主要由兩個部分組成:第一個部分是服務(wù)器端與瀏覽器端之間的通訊協(xié)議,第二部分是在瀏覽器端可供 JavaScript 使用的 EventSource 對象。通訊協(xié)議是基于純文本的簡單協(xié)議。服務(wù)器響應(yīng)的內(nèi)容類型是“text/event-stream”。響應(yīng)文本的內(nèi)容可以看成是一個事件流,由不同的事件所組成。每個事件由類型和數(shù)據(jù)兩部分組成,同時每個事件可以有一個可選的標(biāo)識符。不同事件的內(nèi)容之間通過僅包含回車符和換行符的空行(“\r\n”)來分隔。每個事件的數(shù)據(jù)可能由多行組成。


          如上圖所示,每個事件之間通過空行來分隔。每一行都是由鍵值對組成。如果鍵為空則表示該行為注釋,會在處理時被忽略。例如第10行。

          第1行表示一個只包含數(shù)據(jù)的事件。會按照默認事件走(message事件)。第3-4行代表一個附帶eventID的事件。第6-8行代表一個自定義事件。第10-14行代表一個多行數(shù)據(jù)事件,多行數(shù)據(jù)由換行符鏈接

          key定義有以下幾種:

          • data,表示該行包含的是數(shù)據(jù)。以 data 開頭的行可以出現(xiàn)多次。所有這些行都是該事件的數(shù)據(jù)。
          • event,表示該行用來聲明事件的類型。瀏覽器在收到數(shù)據(jù)時,會產(chǎn)生對應(yīng)類型的事件。默認提供三個標(biāo)準(zhǔn)事件(當(dāng)然你可以自定義):


          • id,表示該行用來聲明事件的標(biāo)識符。服務(wù)器端返回的數(shù)據(jù)中包含了事件的標(biāo)識符,瀏覽器會記錄最近一次接收到的事件的標(biāo)識符。如果與服務(wù)器端的連接中斷,當(dāng)瀏覽器端再次進行連接時,會通過 HTTP 頭“Last-Event-ID”來聲明最后一次接收到的事件的標(biāo)識符。服務(wù)器端可以通過瀏覽器端發(fā)送的事件標(biāo)識符來確定從哪個事件開始來繼續(xù)連接。
          • retry,表示該行用來聲明瀏覽器在連接斷開之后進行再次連接之前的等待時間。

          SSE只適用于高級瀏覽器,但是注意IE不直接支持。IE上的XMLHttpRequest對象不支持獲取部分的響應(yīng)內(nèi)容,所以不支持。每次總有IE,怪不得快被淘汰了。

          2. SSE VS Websocket

          • SSE 只能Server到Client單項,而Websocket是雙向通信。
          • SSE 比 Websocket 輕量。當(dāng)然功能要簡單的多。開發(fā)便利,不牽涉協(xié)議升級問題。
          • SSE 天然支持斷線重連

          2. SSE VS Websocket

          • SSE 只能Server到Client單項,而Websocket是雙向通信。
          • SSE 比 Websocket 輕量。當(dāng)然功能要簡單的多。開發(fā)便利,不牽涉協(xié)議升級問題。
          • SSE 天然支持斷線重連

          3. Spring Mvc中的SSE

          Spring Mvc對SSE進行了支持。如果你要聲明一個SSE連接。只需要在你的控制器聲明一個如下接口:

          必須必須返回SseEmitter對象,SseEmitter對象是Session級別的,如果你要點對點針對每個session要獨立存儲。如果你是廣播可以共用一個SseEmitter對象。按照SSE規(guī)范也必須聲明produces為"text/event-stream"。當(dāng)你調(diào)用該接口的時候?qū)⒔⑵餝SE連接。

          你可以在另一個線程中調(diào)用SseEmitter的send方法向客戶端發(fā)送事件。你也可以在發(fā)送事件后調(diào)用complete方法來關(guān)閉SSE連接。

          4. 瀏覽器端的EventSource

          由于SSE 是HTML5規(guī)范。所以對于APP端必須有HTML才能支持。并且IE如果要支持需要使用一些兼容開發(fā)包,比如polyfill庫。客戶端因為只接受事件所以開發(fā)比較簡單:

          • 聲明客戶端連接, 初始化EventSource對象。
          • 編寫監(jiān)聽器來監(jiān)聽事件。

          總結(jié)

          今天介紹了SSE 服務(wù)端推送。和長輪訓(xùn)、comet、websocket相比而言比較輕量級。在一些需要服務(wù)器實時推送規(guī)模不大的業(yè)務(wù)場景實現(xiàn)更簡單點。相信看了本文后你會很快入門。在實際開發(fā)中要根據(jù)業(yè)務(wù)對這幾種推送進行技術(shù)選型。沒有最好的只有最適合的。SSE對大多數(shù)開發(fā)者來說不夠熟悉。

          文章來源:https://dwz.cn/vQHsUK6H

          作者:碼農(nóng)小胖哥

          接上文HTTP請求的詳細過程

          http協(xié)議版本歷史

          http造就了萬維網(wǎng),http成就了互聯(lián)網(wǎng)第三次信息技術(shù)革命并且影響著即將到來的第四次人工智能技術(shù)革命。

          1989年第一個http協(xié)議,http0.9發(fā)布,發(fā)明了萬維網(wǎng),創(chuàng)建了世界第一個網(wǎng)頁瀏覽器;

          http1.0版本是在1996年正式發(fā)版的 ,這個時候才豐富了交互響應(yīng)的文件類型,96年之前網(wǎng)頁是不能播放音樂的;

          97年發(fā)布http1.1版本,2015年才發(fā)布http2.0版本,源自谷歌的SPDY協(xié)議,目前現(xiàn)代瀏覽器完全支持http2.0,http2可以更好的表達設(shè)備的在線狀態(tài);

          2022年6月,發(fā)布了http3 ,使用QUIC協(xié)議替代了TCP協(xié)議,作為位于應(yīng)用層的通用傳輸協(xié)議,這是一種基于UDP構(gòu)建的多路復(fù)用傳輸協(xié)議;

          http3.0版本解決了TCP的一些問題,提高效率并保留了TCP的所有優(yōu)點。

          http2和http3這2個協(xié)議版本都是由谷歌主導(dǎo)的,并且第一個在chrome瀏覽器率先實現(xiàn)的;

          http0.9到1.0版本見證了web1.0時代的開始,http1.1-2.0版本見證了web1.0并完成了它的使命和見證了web2.0時代發(fā)展到了今天。

          這是開放式系統(tǒng)互聯(lián)的OSI七層模型圖,http協(xié)議位于應(yīng)用程序?qū)樱枰?jīng)過傳輸層才能進行通信。

          理論上傳輸層用哪個協(xié)議并不是那么重要,但一定要知道傳輸層的TCP和UDP兩個協(xié)議到底是什么,著重介紹傳輸層的原因是因為它對http協(xié)議至關(guān)重要,后面的IP層和其他底層對TCP和UDP都很友好,而且對http各個版本的發(fā)展并沒有影響。

          TCP協(xié)議即傳輸控制協(xié)議,首先通過三次握手建立連接,然后傳輸數(shù)據(jù),數(shù)據(jù)傳輸完畢,進入四次揮手關(guān)閉連接,這中間出現(xiàn)了數(shù)據(jù)丟失,則會重試保證數(shù)據(jù)傳輸可靠性。

          可以看出TCP是一個面向連接的可靠的傳輸協(xié)議,而UDP協(xié)議是用戶數(shù)據(jù)協(xié)議,是面向數(shù)據(jù)報文的,發(fā)出去就發(fā)出去了,發(fā)不出去就發(fā)不出去,發(fā)丟了消息也不會去重試,UDP不需要握手建立連接,發(fā)送消息的數(shù)據(jù)又小,所以速度更快。

          為了保證傳輸?shù)目煽啃裕琱ttp0.9到2.0版本都是采用tcp協(xié)議傳輸,到了3.0版本就拋棄了tcp改用udp傳輸,利用QUIC協(xié)議在應(yīng)用層保證數(shù)據(jù)傳輸?shù)目煽啃裕?/span>

          http0.9是萬維網(wǎng)第一個http協(xié)議,經(jīng)過三次握手建立連接,客戶端發(fā)起get請求,服務(wù)器僅能響應(yīng)http文本類型的數(shù)據(jù),但不能在客戶端顯示圖片和播放音樂,客戶端收到數(shù)據(jù)之后,四次揮手關(guān)閉連接,所以也叫單線協(xié)議,同時http0.9版本沒有http標(biāo)頭,沒有狀態(tài)碼、錯誤碼,

          如圖所示僅僅使用一行請求信息,所以也叫它單行版本,就是因為0.9版本太簡陋,服務(wù)器僅能響應(yīng)html文件,比如制作個人網(wǎng)頁,

          于是就有了http1.0,請求加入了http協(xié)議標(biāo)頭,響應(yīng)加了狀態(tài)碼和傳輸?shù)膬?nèi)容類型,

          也就是經(jīng)常在服務(wù)器配置的MIME媒體類型,即多用途互聯(lián)網(wǎng)郵件類型。

          不為http服務(wù)器配置MIME類型,服務(wù)器就沒辦法解釋這個內(nèi)容類型是個啥,客戶端也不認識就會出現(xiàn)問題,服務(wù)器配置好以后,就可以傳輸更多類型的文件內(nèi)容數(shù)據(jù)了。

          和0.9版本一樣,1.0版本還是單線協(xié)議,一個連接只能處理一個請求一個響應(yīng),每次建立物理連接的成本很高,

          并且服務(wù)器遭受不住太多客戶端訪問,否則服務(wù)器扛不住就會直接給你報503錯誤,為了減輕服務(wù)器壓力,出現(xiàn)了升級的http1.1版本,該版本中引入了一個重要的概念,就是持久連接的概念,

          即增加了keep-alive值的標(biāo)頭,就意味著建立連接后,指定時間內(nèi)不會關(guān)閉這個連接,可以在保持的這個連接上發(fā)送多個請求,并且不會出現(xiàn)一個往返就關(guān)閉連接的情況,

          節(jié)省了每次交互都會握手和揮手的時間,除此之外1.1版本還引入了流水線概念,在服務(wù)器響應(yīng)前,客戶端可以一次發(fā)送多個請求,服務(wù)器收到多個請求后,按順序?qū)?zhǔn)備好的數(shù)據(jù)再一個一個的回復(fù)給客戶端,對比一下左邊一次一個請求一個響應(yīng)和右邊一次多個請求逐個響應(yīng)的過程,可以看到明顯的節(jié)約了請求發(fā)送的時間,提高了傳輸?shù)男省?/span>

          按順序響應(yīng)有沒有問題?

          • 線頭阻塞問題

          首先就會造成一個重要的問題,head of line blocking(線頭阻塞即流水線頭阻塞),

          假如客戶端處理了三個請求,第一個請求處理的時間太長,第二個第三個請求是不是得必須老實的排隊,等待第一個請求處理完成呢?

          這就造成了流水線的塞車現(xiàn)象,在這個版本中解決塞車的辦法就是瀏覽器通常會保持6個左右的連接以緩解一個連接塞車,客戶端可以通過其他連接繼續(xù)發(fā)送請求。

          • http標(biāo)頭冗余問題

          在連續(xù)發(fā)送請求的情況下,每次請求總是會帶上相同基本不會變化的http標(biāo)頭,并且http1.1發(fā)送的數(shù)據(jù)還是純文本的,傳輸數(shù)據(jù)不但大還啰嗦。

          http2協(xié)議

          以上2個問題就是http2要解決的,http2協(xié)議是http1.1版本的擴展而不是替換,因為它只是更好的解決了1.1版本的問題和功能補充,

          b站網(wǎng)絡(luò)請求就是使用的http2協(xié)議,

          http2在應(yīng)用層改變了傳輸方式,之前版本應(yīng)用程序?qū)觽鬟f的http標(biāo)頭和數(shù)據(jù)都是明文的,

          http2在應(yīng)用層增加了一個二進制幀數(shù)據(jù)處理層,

          將http標(biāo)頭和數(shù)據(jù)明文消息拆分成二進制數(shù)據(jù)幀進行傳輸,

          每個數(shù)據(jù)幀包含自己攜帶的數(shù)據(jù)并用stream id做標(biāo)識,這也解釋了什么叫數(shù)據(jù)流,就是將數(shù)據(jù)切開形成數(shù)據(jù)幀,然后將它們單獨或者打包成獨立流傳輸?shù)侥康牡睾螅?/span>

          再按照stream id將它們組裝起來還原成原始數(shù)據(jù)。

          好處就是OSI模型應(yīng)用程序下的底層協(xié)議,更喜歡二進制數(shù)據(jù)幀,因為它們的大部分工作被應(yīng)用程序?qū)幼隽耍鼈兊幕钌倭耍憫?yīng)的網(wǎng)絡(luò)傳輸速度也就提高了。

          另一個好處就是再大的文件也給你剁的細碎,規(guī)避了大文件傳輸大小的限制,比如IIS、apache等http服務(wù)器都有對單個請求體又大小的限制。

          客戶端以流的方式請求,服務(wù)器以流的形式響應(yīng),就形成了雙向數(shù)據(jù)流,數(shù)據(jù)幀都用stream id做標(biāo)識,然后stream id將多個數(shù)據(jù)幀識別為一個消息,

          保證了數(shù)據(jù)的完整性。

          http2協(xié)議是客戶端與服務(wù)器建立一個連接,再加上雙向數(shù)據(jù)流,客戶端和服務(wù)器的交互就可以重用它們建立的單個連接傳遞數(shù)據(jù),就形成了http2的多路復(fù)用。

          既然數(shù)據(jù)幀標(biāo)明了stream id,那么傳輸數(shù)據(jù)流的順序可以是亂序的,當(dāng)然傳輸數(shù)據(jù)也可以設(shè)置優(yōu)先權(quán)重的,

          無論是客戶端還是服器接收到的流數(shù)據(jù)先到的在緩沖區(qū)先組裝,不用等待前一個請求數(shù)據(jù)處理完成再處理下一個,

          就這解決了http1.1應(yīng)用程序?qū)拥膆ttp線頭阻塞的問題,http2是否真正解決了線頭阻塞的問題?

          之所以前面著重強調(diào)的是應(yīng)用程序?qū)拥膆ttp線頭阻塞,原因是http2僅在二進制幀處理層拿到完成的tcp數(shù)據(jù)后解決了線頭阻塞問題,http2同樣基于傳輸層的tcp協(xié)議傳輸?shù)模?/span>

          TCP標(biāo)頭格式

          tcp協(xié)議為了保證消息可靠,順序傳遞消息,就有可能造成塞車的情況,所以傳輸層的tcp線頭阻塞問題并沒有得到解決,為了說明這個問題,來模擬下傳輸層tcp傳遞數(shù)據(jù)包的視角。

          當(dāng)打開b站,我們和瀏覽器都明白我們向b站服務(wù)器請求獲取js、css等文件資源,

          http2通過前面講的二進制幀數(shù)據(jù)處理層,將數(shù)據(jù)剁碎,加入stream id,用數(shù)據(jù)流的方式傳遞數(shù)據(jù),其他的,http2并不關(guān)心,更不用說到達傳輸層的tcp協(xié)議了,甚至都不知道這是傳輸?shù)氖莌ttp請求、響應(yīng)的信息,tcp只知道數(shù)據(jù)包的最大大小通常是1450字節(jié)。

          http2跟蹤傳輸?shù)闹付ㄗ止?jié)范圍的數(shù)據(jù)部分,以便可以按正確順序來還原原始數(shù)據(jù),

          假如tcp數(shù)據(jù)包2在傳輸過程中丟失了,

          那么tcp數(shù)據(jù)包1和3會提前達到緩沖區(qū),tcp就會發(fā)現(xiàn)數(shù)據(jù)包1和3之間缺少了數(shù)據(jù)包2的字節(jié)范圍數(shù)據(jù),哪怕http2知道tcp數(shù)據(jù)包1中有stream id為1的數(shù)據(jù)幀,加上tcp數(shù)據(jù)包3中stream為1的數(shù)據(jù)幀,可以組裝成一個完整的消息,也要耐心等待tcp至少重返服務(wù)器一次,來重傳丟包的數(shù)據(jù)2的數(shù)據(jù)副本到達才能繼續(xù)解包其他tcp數(shù)據(jù)交給應(yīng)用程序?qū)樱褪莵G失的tcp數(shù)據(jù)包2阻塞了tcp數(shù)據(jù)包3導(dǎo)致了tcp的線頭阻塞,所以結(jié)論就是http2并沒有完全解決線頭阻塞的問題。

          接下來講個http2重要的擴展功能:

          http2服務(wù)器推送功能

          首先正是因為http2具有雙向流、多路復(fù)用的特點 ,才更有利于http2提供的服務(wù)器推送功能,比如直接打開b站 請求的是首頁,

          b站服務(wù)器可以將未經(jīng)過請求的、首頁所需的js、css等文件資源準(zhǔn)備好之后直接推送給我們;

          同時對于前端開發(fā)者,http2更加有益于html5套件的SSE功能(Server-Send Events即服務(wù)器發(fā)送事件),

          服務(wù)器推送功能進一步減少了網(wǎng)絡(luò)請求的數(shù)量,提高了交互的效率。

          http2標(biāo)頭壓縮功能

          打開瀏覽器開發(fā)者工具,直接進入百度首頁,

          百度依然使用的是http1.1版本,

          百度首頁的請求標(biāo)頭是689字節(jié),響應(yīng)標(biāo)頭是598字節(jié),點擊標(biāo)頭標(biāo)簽可以看到請求和響應(yīng)的所有標(biāo)頭信息,嘗試刷新下頁面,看到請求發(fā)送標(biāo)頭大小沒有任何變化,說明http1.1來回總是在說車轱轆話。

          b站使用的是http2協(xié)議,

          b站首次請求的標(biāo)頭大小是387字節(jié),響應(yīng)的標(biāo)頭大小是417字節(jié),進入標(biāo)頭標(biāo)簽可以詳細看到http2請求和響應(yīng)標(biāo)頭信息,

          再次刷新頁面,發(fā)現(xiàn)請求標(biāo)頭縮小至116kb,響應(yīng)字節(jié)也小了,

          再次刷新頁面,發(fā)現(xiàn)b站請求和響應(yīng)標(biāo)頭更小了,而達到這種節(jié)約網(wǎng)絡(luò)請求的標(biāo)頭大小的效果就要歸功于http2是使用HPACK來編解碼http標(biāo)頭的。

          HPACK的原理就是建立連接的客戶端和服務(wù)器都維護著相同的61個條目的只讀的靜態(tài)表和可動態(tài)添加條目的空白的動態(tài)表,

          當(dāng)請求發(fā)送的時候先查找表中的名值,名值完全匹配直接提取該標(biāo)頭在表中對應(yīng)的整數(shù)索引用于表示該名值對。

          如果只匹配名稱,則使用索引表示名稱,值就用霍夫曼huffman算法進行編碼后表示,

          表中沒有的,則按照順序添加到動態(tài)表中,再使用霍夫曼算法編碼名值對字符后傳遞給服務(wù)器;服務(wù)器端則反之, 使用表中的索引解碼或者按順序使用霍夫曼算法進行解碼后添加到服務(wù)器動態(tài)表中,這樣保證雙方的表中保存的數(shù)據(jù)和整數(shù)索引是一致的,在之后的所有請求的標(biāo)頭會越來越小,減少了網(wǎng)絡(luò)數(shù)據(jù)流量來提高交互的效率。

          這是http2標(biāo)準(zhǔn)中已經(jīng)定義的61個標(biāo)頭的靜態(tài)表,

          類似:path這樣的標(biāo)頭名稱,被稱為偽標(biāo)頭,出現(xiàn)偽標(biāo)頭的原因是為了兼容之前的版本,

          http2的標(biāo)頭都是名值對,之前版本的標(biāo)頭都存在標(biāo)頭行,http2將http1的標(biāo)頭行拆分成名值后的名稱前加上了冒號,方便與http2的標(biāo)頭名稱進行區(qū)分。

          什么是霍夫曼算法

          最優(yōu)前綴碼算法,利用二叉樹將出現(xiàn)頻率大的符號采用較短的有效編碼,頻率小的有效編碼更長,解決等長編碼解壓時出現(xiàn)歧義的問題,

          字節(jié)下的數(shù)字代表這個字母出現(xiàn)的頻率,

          霍夫曼算法會先從出現(xiàn)頻率低的兩個字母,按照左樹代表0放出現(xiàn)頻率小的字母

          右樹代表1放出現(xiàn)頻率大的字母的原則合并組成一個新節(jié)點。

          以此類推最終組成一個二叉樹,

          以上6個字母的最終編碼如圖所示,a=0直到f=1100,你是否認為http2每次都對未出現(xiàn)在表中的標(biāo)頭字符都這樣算一遍?

          http2標(biāo)準(zhǔn)按照標(biāo)頭字符的使用頻率,專門準(zhǔn)備了一張霍夫曼編碼表,剛好256個條目,

          對應(yīng)ASCII編碼表示的256個可能的字符,那么對應(yīng)霍夫曼編碼表,編碼就不需要二叉樹運算了,提高了編碼的效率,例如在霍夫曼編碼表中,位于索引47位置的符號/,由6位二進制編碼011000組成,16進制編碼值就是0x18。

          http2留給我們的三個思考

          • 了解HPACK原理之后 是不是可以更好的優(yōu)化網(wǎng)絡(luò)請求?

          因為客戶端對同一個網(wǎng)站交互越多,

          動態(tài)表中積累的http標(biāo)頭條目就越多,標(biāo)頭壓縮的效果就越好,所以這里有一個http2的優(yōu)化技巧,交互最好是同域名或者同域名下不同二級域名指向的服務(wù)器ip是一樣的,

          而且使用的是同一個加密證書,這樣會確保使用相同的靜動態(tài)標(biāo)頭表,這也解釋了為啥泛域名的SSL加密證書每年費用那么貴的原因。

          HPACK是按順序編解碼標(biāo)頭的是不是也會造成線頭阻塞問題?

          是的,因為http2數(shù)據(jù)流可以是無序的,

          請求數(shù)據(jù)流達到服務(wù)器后,請求攜帶編碼后的標(biāo)頭中,

          發(fā)現(xiàn)有個請求標(biāo)頭索引比服務(wù)器解碼動態(tài)標(biāo)頭表中的最大索引值還大,就會造成無法解碼,而導(dǎo)致使用該請求標(biāo)頭相關(guān)的數(shù)據(jù)流都會被阻塞,直到攜帶該索引表示的正確霍夫曼編碼的數(shù)據(jù)流達到后,

          才能解碼之前的請求數(shù)據(jù)流,如果這條數(shù)據(jù)流丟包了,就會造成塞車時間更長,目前這個塞車問題只能留給http3解決了。

          第三個思考是使用多路復(fù)用,它真的就比之前版本的按順序傳輸更快嗎

          真正解決了交互事務(wù)中的線頭阻塞問題了嗎? 答案是不一定

          不是說好了速度更快且沒有http線頭阻塞問題?

          首先表象的看http2和現(xiàn)代瀏覽器強制了必須使用https加密超文本協(xié)議傳輸,那https是如何工作的?

          相比于http1.1之前不強制使用https加密,http2在加密交換密鑰的過程中,最少增加了2個往返時間即2RRT(Round-Trip Time),反而減慢了交互速度。

          另一個是多路復(fù)用中數(shù)據(jù)被切碎成數(shù)據(jù)幀,并用stream id標(biāo)識打包成數(shù)據(jù)流傳輸,這樣就可以亂序傳輸了。

          假如我們一次請求一個js或css文件,根據(jù)文件大小假如被切分成了5份和3分,來對比下順序傳輸和亂序傳輸

          ,順序傳輸?shù)膉s文件的接收速度比亂序的js傳輸速度更快,

          總的到達時間基本一致的,所以在http2中的多路復(fù)用,就需要根據(jù)實際情況考慮使用優(yōu)先級控制優(yōu)化了。

          再比如亂序中某個數(shù)據(jù)的丟包,在緩存中排序組裝消息的時候發(fā)現(xiàn)少了一幀,

          就需要至少往返一次服務(wù)器,拿丟失的數(shù)據(jù)幀副本,雖然沒阻塞其他的消息,但確實阻塞了自己,所以這個問題 不單單存在于http2,http3的多路復(fù)用也存在這個問題,雖然這種數(shù)據(jù)流的線頭阻塞是概率問題,但確實存在。

          http3

          它是基于QUIC協(xié)議的升級版本,

          已經(jīng)知道了http2基于傳輸層的tcp協(xié)議傳輸,僅僅解決了在應(yīng)用程序?qū)拥膆ttp線頭阻塞的問題,

          但又由于傳輸層的協(xié)議基本由系統(tǒng)實現(xiàn),應(yīng)用程序?qū)訜o法干涉,所以http3就想,既然動不了你,干嘛不直接拋棄你,http3就直接使用了傳輸效率更高的UDP,雖然UDP不保證消息傳輸?shù)目煽啃裕趆ttp2已經(jīng)解決了應(yīng)用程序?qū)觝ttp的線頭阻塞問題的基礎(chǔ)之上,

          http3在應(yīng)用程序?qū)舆m配UDP并保證數(shù)據(jù)傳輸?shù)目煽啃裕屯瞥隽嘶赨DP的QUIC協(xié)議,可以說它是tcp2.0,只不過是在應(yīng)用程序?qū)訉崿F(xiàn)的,QUIC協(xié)議實現(xiàn)了tcp的可靠性、擁塞控制、流量控制、排序等功能和保留了http2基于流的多路復(fù)用及http2優(yōu)化的其他功能。

          http3將基于UDP的流作為一等公民和http2的流處理有2個重要區(qū)別

          一個區(qū)別是http3使用動態(tài)標(biāo)頭編碼QPACK和http2使用HPACK,QPACK和HPACK在編解碼方式都是一樣的,重要區(qū)別在與QPACK增加了靜態(tài)表中的標(biāo)頭條目至98個。

          另一個就是解決HPACK按順序解碼容易造成線頭阻塞的問題,

          QPACK的解決辦法就是使用指令獨立流,

          提前講好需要增加的標(biāo)頭項,使用添加指令發(fā)送給解碼方,解碼方收到后回復(fù)確認標(biāo)頭指令,從而雙方利用標(biāo)頭動態(tài)表中標(biāo)頭項的狀態(tài),來確認標(biāo)頭是否完成同步,標(biāo)頭項狀態(tài)未被確認的,請求的數(shù)據(jù)流就不會使用該項標(biāo)頭的索引編碼標(biāo)頭以解決動態(tài)標(biāo)頭解碼的塞車問題。

          另一個區(qū)別就是因為QUIC協(xié)議實現(xiàn)了tcp協(xié)議的所有功能,那QUIC協(xié)議如何規(guī)避tcp線頭阻塞的缺點?

          http2存在tcp的線頭阻塞,是因為tcp只知道跟蹤攜帶的字節(jié)范圍數(shù)據(jù),并不知道我們前面講的tcp線頭阻塞例子中數(shù)據(jù)包1中的stream id為1的數(shù)據(jù)幀和數(shù)據(jù)包3中的stream id為1的數(shù)據(jù)幀可以組裝成一個消息,因為tcp根本不知道傳輸?shù)木唧w細節(jié)。

          在http3中QUIC協(xié)議優(yōu)化了幀的組成部分,脫掉了tcp外衣,直接在數(shù)據(jù)幀中加入了Offset字節(jié)范圍幀頭,

          這樣就能根據(jù)stream id和Offset幀頭,判斷哪些數(shù)據(jù)包中的幀數(shù)是同一個消息,因此例子中數(shù)據(jù)包1中的stream id為1的數(shù)據(jù)幀,

          就可以和數(shù)據(jù)包3中的steram id為1的數(shù)據(jù)幀組成一個消息,因為它們的字節(jié)范圍可以匹配的上,這樣就解決了即使丟了數(shù)據(jù)包2,也不影響其他消息的情況,從而消除了tcp線頭阻塞的缺點。

          http3的三個重要優(yōu)點

          基于UDP有個明顯的好處就是數(shù)據(jù)到達傳輸層是需要加入傳輸層協(xié)議的頭部。

          http2和之前的版本使用的是tcp協(xié)議,

          需要20-60個字節(jié)的頭部,

          而http3采用傳輸層的UDP協(xié)議,頭部僅僅8個字節(jié),這樣就進一步優(yōu)化了交互數(shù)據(jù)的大小,提高了傳輸速度,QUIC協(xié)議基于UDP有個更大的好處就是0往返時間建立連接即0 RTT,

          tcp建立連接是需要經(jīng)歷3次握手和四次揮手的,QUIC協(xié)議是基于UDP的,就不需要三次握手和四次揮手,名義上實現(xiàn)了0 RTT,但是http3也需要加密傳輸,直接將加密協(xié)商信息和請求數(shù)據(jù)一起發(fā)送給服務(wù)器,只需要1個RTT就可以完成建立連接,相比于http2的加密協(xié)商至少需要2個RTT來說,還少了一個RTT。

          第三個優(yōu)點:tcp是基于連接的,而QUIC協(xié)議基于UDP的數(shù)據(jù)傳輸,是基于報文的,那http3怎么識別當(dāng)前連接并實現(xiàn)多路復(fù)用的?

          使用tcp建立的連接會基于源IP、源端口、目的IP和目的端口四元組信息,來確認是否已經(jīng)建立過該連接,假如說從wifi切換到5G至少會導(dǎo)致四元組信息中的源IP地址信息會發(fā)生改變,tcp就必須重新建立連接,

          而QUIC協(xié)議使用的是隨機數(shù)作為connection id即連接id,來識別客戶端和同域名服務(wù)器建立連接,從而不受網(wǎng)絡(luò)變化的影響,通過connection id確定了連接,當(dāng)然就可以實現(xiàn)和http2一樣的雙向流多路復(fù)用的功能,基于這個優(yōu)點,http3協(xié)議就非常適合移動互聯(lián)網(wǎng)了。


          主站蜘蛛池模板: 精品不卡一区二区| 亚洲国产综合无码一区二区二三区 | 国产微拍精品一区二区| 亚洲高清偷拍一区二区三区| 色一乱一伦一图一区二区精品| 国产aⅴ一区二区三区| 交换国产精品视频一区| 国产精品福利一区二区| 亚洲一区精品视频在线| 亚洲一区二区三区高清| 日韩电影一区二区三区| 国产一区二区三区高清在线观看| 精品一区二区三区在线观看l | 精品无码一区二区三区亚洲桃色| 精品乱子伦一区二区三区| 无码精品人妻一区| 视频精品一区二区三区| 国产成人av一区二区三区在线 | 一区免费在线观看| 国产aⅴ精品一区二区三区久久| 人妻夜夜爽天天爽一区| 精品国产一区二区三区AV | 亚洲一区二区三区在线播放| 精品一区二区三区电影| 国产嫖妓一区二区三区无码| 精品乱人伦一区二区| 精品一区二区三区影院在线午夜| 国产精品亚洲专一区二区三区| 日韩久久精品一区二区三区| 韩国资源视频一区二区三区| 国产精品美女一区二区三区| 精品国产一区二区三区av片| 成人精品一区二区电影| 国产精品综合一区二区三区| 免费在线视频一区| 国产免费播放一区二区| 国产a∨精品一区二区三区不卡 | 福利视频一区二区牛牛| 日本一区二区三区高清| 99久久精品国产一区二区成人| 伊人无码精品久久一区二区|