HTTP傳輸大文件的方法、連接管理以及重定向

目錄

1. HTTP傳輸大文件的方法

1.1. 數據壓縮

1.2. 分塊傳輸

1.3. 范圍請求

1.4. 多段數據

2. HTTP的連接管理

2.1. 短連接

2.2. 長連接

2.3. 隊頭阻塞

3. HTTP的重定向和跳轉

3.1. 重定向的過程

3.2. 重定向狀態碼

3.3. 重定向的應用場景

3.4. 重定向的相關問題

1. HTTP傳輸大文件的方法

如何在有限的帶寬下高效快捷地傳輸這些大文件:

  1. 壓縮 HTML 等文本文件是傳輸大文件最基本的方法;
  2. 分塊傳輸可以流式收發數據,節約內存和帶寬,使用響應頭字段“Transfer-Encoding: chunked”來表示,分塊的格式是 16 進制長度頭 + 數據塊;
  3. 范圍請求可以只獲取部分數據,即“分塊請求”,實現視頻拖拽或者斷點續傳,使用請求頭字段“Range”和響應頭字段“Content-Range”,響應狀態碼必須是 206;
  4. 也可以一次請求多個范圍,這時候響應報文的數據類型是“multipart/byteranges”,body 里的多個部分會用 boundary 字符串分隔。

1.1. 數據壓縮

通常瀏覽器在發送請求時都會帶著“Accept-Encoding”頭字段,里面是瀏覽器支持的壓縮格式列表,例如 gzip、deflate、br 等,這樣服務器就可以從中選擇一種壓縮算法,放進“Content-Encoding”響應頭里,再把原數據壓縮后發給瀏覽器。

不過這個解決方法也有個缺點,gzip 等壓縮算法通常只對文本文件有較好的壓縮率,而圖片、音頻視頻等多媒體數據本身就已經是高度壓縮的,再用 gzip 處理也不會變小(甚至還有可能會增大一點),所以它就失效了。

不過數據壓縮在處理文本的時候效果還是很好的,所以各大網站的服務器都會使用這個手段作為“保底”。例如,在 Nginx 里就會使用“gzip on”指令,啟用對“text/html”的壓縮。

1.2. 分塊傳輸

把大文件“拆開”,分解成多個小塊,把這些小塊分批發給瀏覽器,瀏覽器收到后再組裝復原。

這種“化整為零”的思路在 HTTP 協議里就是“chunked”分塊傳輸編碼,在響應報文里用頭字段“Transfer-Encoding: chunked”來表示,意思是報文里的 body 部分不是一次性發過來的,而是分成了許多的塊(chunk)逐個發送。

分塊傳輸也可以用于“流式數據”,例如由數據庫動態生成的表單頁面,這種情況下 body 數據的長度是未知的,無法在頭字段“Content-Length”里給出確切的長度,所以也只能用 chunked 方式分塊發送。

“Transfer-Encoding: chunked”和“Content-Length”這兩個字段是互斥的,也就是說響應報文里這兩個字段不能同時出現,一個響應報文的傳輸要么是長度已知,要么是長度未知。

分塊傳輸的編碼規則:

  1. 每個分塊包含兩個部分,長度頭和數據塊;
  2. 長度頭是以 CRLF(回車換行,即\r\n)結尾的一行明文,用 16 進制數字表示長度;
  3. 數據塊緊跟在長度頭后,最后也用 CRLF 結尾,但數據不包含 CRLF;
  4. 最后用一個長度為 0 的塊表示結束,即“0\r\n\r\n”。

分塊傳輸示例:

不過瀏覽器在收到分塊傳輸的數據后會自動按照規則去掉分塊編碼,重新組裝出內容,所以想要看到服務器發出的原始報文形態就得用 Telnet 手工發送請求(或者用 Wireshark 抓包):

16進制(0123456789abcdef)

1.3. 范圍請求

如果想獲取一個大文件其中的片段數據,需要使用“范圍請求”(range requests)。

范圍請求允許客戶端在請求頭里使用專用字段來表示只獲取文件的一部分,相當于是客戶端的“化整為零”。

范圍請求不是 Web 服務器必備的功能,可以實現也可以不實現,所以服務器必須在響應頭里使用字段“Accept-Ranges: bytes”明確告知客戶端:“我是支持范圍請求的”。

如果不支持的話服務器可以發送“Accept-Ranges: none”,或者干脆不發送“Accept-Ranges”字段,這樣客戶端就認為服務器沒有實現范圍請求功能,只能收發整塊文件。

請求頭Range是 HTTP 范圍請求的專用字段,格式是“bytes=x-y”,其中的 x 和 y 是以字節為單位的數據范圍。

范圍請求示例:

1.4. 多段數據

范圍請求還支持在 Range 頭里使用多個“x-y”,一次性獲取多個片段數據。

這種情況需要使用一種特殊的 MIME 類型:“multipart/byteranges”,表示報文的 body 是由多段字節序列組成的,并且還要用一個參數“boundary=xxx”給出段之間的分隔標記。

多段數據的格式與分塊傳輸也比較類似,但它需要用分隔標記 boundary 來區分不同的片段。

范圍請求多段數據示例:

2. HTTP的連接管理

  1. 早期的 HTTP 協議使用短連接,收到響應后就立即關閉連接,效率很低;
  2. HTTP/1.1 默認啟用長連接,在一個連接上收發多個請求響應,提高了傳輸效率;
  3. 服務器會發送“Connection: keep-alive”字段表示啟用了長連接;
  4. 報文頭里如果有“Connection: close”就意味著長連接即將關閉;
  5. 過多的長連接會占用服務器資源,所以服務器會用一些策略有選擇地關閉長連接;
  6. “隊頭阻塞”問題會導致性能下降,可以用“并發連接”和“域名分片”技術緩解。

2.1. 短連接

HTTP 協議最初(0.9/1.0)是個非常簡單的協議,通信過程也采用了簡單的“請求 - 應答”方式。

它底層的數據傳輸基于 TCP/IP,每次發送請求前需要先與服務器建立連接,收到響應報文后會立即關閉連接。因為客戶端與服務器的整個連接過程很短暫,不會與服務器保持長時間的連接狀態,所以就被稱為“短連接”(short-lived connections)。早期的 HTTP 協議也被稱為是“無連接”的協議。

在短連接中, TCP 協議頻繁的建立連接和關閉連接,時間浪費嚴重,效率很低。

2.2. 長連接

長連接”,也叫“持久連接”(persistent connections)、“連接保活”(keep alive)、“連接復用”(connection reuse),可以有效解決短連接的缺點。

在短連接里發送了三次 HTTP“請求 - 應答”,每次都會浪費 60% 的 RTT 時間。而在長連接的情況下,同樣發送三次請求,因為只在第一次時建立連接,在最后一次時關閉連接,所以浪費率就是“3÷9≈33%”,降低了差不多一半的時間損耗。

HTTP/1.1 中的連接都會默認啟用長連接。不需要用什么特殊的頭字段指定,只要向服務器發送了第一次請求,后續的請求都會重復利用第一次打開的 TCP 連接,也就是長連接,在這個連接上收發數據。

不過不管客戶端是否顯式要求長連接,如果服務器支持長連接,它總會在響應報文里放一個“Connection: keep-alive”字段,告訴客戶端:“我是支持長連接的,接下來就用這個 TCP 一直收發數據吧”。

長連接的缺點:

TCP 連接長時間不關閉,服務器必須在內存里保存它的狀態,這就占用了服務器的資源。如果有大量的空閑長連接只連不發,就會很快耗盡服務器的資源,導致服務器無法為真正有需要的用戶提供服務。

長連接缺點的解決方法:

  1. 在客戶端,可以在請求頭里加上“Connection: close”字段,告訴服務器:“這次通信后就關閉連接”。服務器看到這個字段,就知道客戶端要主動關閉連接,于是在響應報文里也加上這個字段,發送之后就調用 Socket API 關閉 TCP 連接。
  2. 服務器端通常不會主動關閉連接,但也可以使用一些策略。以 Nginx 為例,它有兩種方式:
  • 使用“keepalive_timeout”指令,設置長連接的超時時間,如果在一段時間內連接上沒有任何數據收發就主動斷開連接,避免空閑連接占用系統資源。
  • 使用“keepalive_requests”指令,設置長連接上可發送的最大請求次數。比如設置成 1000,那么當 Nginx 在這個連接上處理了 1000 個請求后,也會主動斷開連接。

2.3. 隊頭阻塞

“隊頭阻塞”與短連接和長連接無關,而是由 HTTP 基本的“請求 - 應答”模型所導致的。

因為 HTTP 規定報文必須是“一發一收”,這就形成了一個先進先出的“串行”隊列。隊列里的請求沒有輕重緩急的優先級,只有入隊的先后順序,排在最前面的請求被最優先處理。

如果隊首的請求因為處理的太慢耽誤了時間,那么隊列里后面的所有請求也不得不跟著一起等待,結果就是其他的請求承擔了不應有的時間成本。

性能優化:

1. 并發連接:對一個域名發起多個長連接,用數量來解決質量的問題。

但這種方式也存在缺陷。如果每個客戶端都想自己快,建立很多個連接,用戶數×并發數就會是個天文數字。服務器的資源根本就扛不住,或者被服務器認為是惡意攻擊,反而會造成“拒絕服務”。

2. 域名分片:多開幾個域名,比如 shard1.chrono.com、shard2.chrono.com,而這些域名都指向同一臺服務器 www.chrono.com,這樣實際長連接的數量就又上去了。

3. HTTP的重定向和跳轉

  1. 重定向是服務器發起的跳轉,要求客戶端改用新的 URI 重新發送請求,通常會自動進行,用戶是無感知的;
  2. 301/302 是最常用的重定向狀態碼,分別是“永久重定向”和“臨時重定向”;
  3. 響應頭字段 Location 指示了要跳轉的 URI,可以用絕對或相對的形式;
  4. 重定向可以把一個 URI 指向另一個 URI,也可以把多個 URI 指向同一個 URI,用途很多;
  5. 使用重定向時需要當心性能損耗,還要避免出現循環跳轉。

“超文本”里含有“超鏈接”,可以從一個“超文本”跳躍到另一個“超文本”,用戶可以在查看時隨意點擊鏈接、轉換頁面。點擊頁面“鏈接”時,瀏覽器首先要解析鏈接文字里的 URI,再用這個 URI 發起一個新的 HTTP 請求,獲取響應報文后就會切換顯示內容,渲染出新 URI 指向的頁面。

這樣的跳轉動作是由瀏覽器的使用者主動發起的,可以稱為“主動跳轉”,但還有一類跳轉是由服務器發起的,瀏覽器使用者無法控制,相對地就可以稱為“被動跳轉”,這在 HTTP 協議里有個專門的名詞,叫做“重定向”(Redirection)。

3.1. 重定向的過程

在實驗環境下用Chrome瀏覽器打開“http://www.chrono.com/18-1”。

Location”字段屬于響應字段,必須出現在響應報文里。但只有配合 301/302 狀態碼才有意義,它標記了服務器要求重定向的 URI,這里就是要求瀏覽器跳轉到“index.html”。

瀏覽器收到 301/302 報文,會檢查響應頭里有沒有“Location”。如果有,就從字段值里提取出 URI,發出新的 HTTP 請求,相當于自動替我們點擊了這個鏈接。

在“Location”里的 URI 既可以使用絕對 URI,也可以使用相對 URI。所謂“絕對 URI”,就是完整形式的 URI,包括 scheme、host:port、path 等。所謂“相對 URI”,就是省略了 scheme 和 host:port,只有 path 和 query 部分,是不完整的,但可以從請求上下文里計算得到。

實驗環境的 URI“/18-1”還支持使用 query 參數“dst=xxx”,指明重定向的 URI。

在重定向時如果只是在站內跳轉,你可以放心地使用相對 URI。但如果要跳轉到站外,就必須用絕對 URI。

例如,如果想跳轉到 Nginx 官網,就必須在“nginx.org”前把“http://”都寫出來,否則瀏覽器會按照相對 URI 去理解,得到的就會是一個不存在的 URI“http://www.chrono.com/nginx.org”

http://www.chrono.com/18-1?dst=nginx.org # 錯誤

http://www.chrono.com/18-1?dst=http://nginx.org # 正確

3.2. 重定向狀態碼

301俗稱“永久重定向”(Moved Permanently),意思是原 URI 已經“永久”性地不存在了,今后的所有請求都必須改用新的 URI。

瀏覽器看到 301,就知道原來的 URI“過時”了,就會做適當的優化。比如歷史記錄、更新書簽,下次可能就會直接用新的 URI 訪問,省去了再次跳轉的成本。搜索引擎的爬蟲看到 301,也會更新索引庫,不再使用老的 URI。

302俗稱“臨時重定向”(“Moved Temporarily”),意思是原 URI 處于“臨時維護”狀態,新的 URI 是起“頂包”作用的“臨時工”。

瀏覽器或者爬蟲看到 302,會認為原來的 URI 仍然有效,但暫時不可用,所以只會執行簡單的跳轉頁面,不記錄新的 URI,也不會有其他的多余動作,下次訪問還是用原 URI。

301/302 是最常用的重定向狀態碼,在 3××里還有:

  1. 303 See Other:類似 302,但要求重定向后的請求改為 GET 方法,訪問一個結果頁面,避免 POST/PUT 重復操作;
  2. 307 Temporary Redirect:類似 302,但重定向后請求里的方法和實體不允許變動,含義比 302 更明確;
  3. 308 Permanent Redirect:類似 307,不允許重定向后的請求變動,但它是 301“永久重定向”的含義。

不過這三個狀態碼的接受程度較低,有的瀏覽器和服務器可能不支持,開發時應當慎重,測試確認瀏覽器的實際效果后才能使用。

3.3. 重定向的應用場景

什么時候需要重定向:

1. 資源不可用”,需要用另一個新的 URI 來代替。

例如域名變更、服務器變更、網站改版、系統維護,這些都會導致原 URI 指向的資源無法訪問,為了避免出現 404,就需要用重定向跳轉到新的 URI,繼續為網民提供服務。

2. “避免重復”,讓多個網址都跳轉到一個 URI,增加訪問入口。

例如,有的網站都會申請多個名稱類似的域名,然后把它們再重定向到主站上。

重定向狀態碼如何選擇:

1. 301 的含義是“永久”的。

如果域名、服務器、網站架構發生了大幅度的改變,比如啟用了新域名、服務器切換到了新機房、網站目錄層次重構,這些都算是“永久性”的改變。原來的 URI 已經不能用了,必須用 301“永久重定向”,通知瀏覽器和搜索引擎更新到新地址,這也是搜索引擎優化(SEO)要考慮的因素之一。

2. 302 的含義是“臨時”的。

原來的 URI 在將來的某個時間點還會恢復正常,常見的應用場景就是系統維護,把網站重定向到一個通知頁面,告訴用戶過一會兒再來訪問。另一種用法就是“服務降級”,比如在雙十一促銷的時候,把訂單查詢、領積分等不重要的功能入口暫時關閉,保證核心服務能夠正常運行。

3.4. 重定向的相關問題

1. 性能損耗

重定向的機制決定了一個跳轉會有兩次請求 - 應答,比正常的訪問多了一次。

雖然 301/302 報文很小,但大量的跳轉對服務器的影響也是不可忽視的。站內重定向還好說,可以長連接復用,站外重定向就要開兩個連接,如果網絡連接質量差,那成本可就高多了,會嚴重影響用戶的體驗。

所以重定向應當適度使用,決不能濫用。

2. 循環跳轉

如果重定向的策略設置欠考慮,可能會出現“A=>B=>C=>A”的無限循環,不停地在這個鏈路里轉圈圈。

所以 HTTP 協議特別規定,瀏覽器必須具有檢測“循環跳轉”的能力,在發現這種情況時應當停止發送請求并給出錯誤提示。

實驗環境的 URI“/18-2”就模擬了這樣的一個“循環跳轉”,它跳轉到“/18-1”,并用參數“dst=/18-2”再跳回自己,實現了兩個 URI 的無限循環。結果如下:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/904796.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/904796.shtml
英文地址,請注明出處:http://en.pswp.cn/news/904796.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PostgreSQL 18 Beta 1發布,有哪些功能亮點?

PostgreSQL 全球開發組于 2025 年 5 月 8 日發布了第一個 PostgreSQL 18 Beta 版本,現已開放下載。雖然細節可能會有所改變,但是該版本包含了 PostgreSQL 18 最終正式版中所有新功能的預覽。 以下是 PostgreSQL 18 引入的部分關鍵功能亮點。 性能優化 …

vue dev-tools插件

背景 在項目上用到vue技術,在bilibili上學習vue,期間老師推薦使用vue dev-tools調試神器,所以過來安轉和使用了,用了感覺不錯,希望給大家帶來效率的提升。 定義 Vue DevTools 是一款旨在增強 Vue 開發者體驗的工具&am…

單片機-FLASH軟件模擬EEPROM,提升flash保存重要參數的使用壽命

目錄 1. FLASH和EEPROM讀寫數據的對比 ??2. FLASH模擬EEPROM的原理 ??3. FLASH模擬EEPROM的優點 ??4. 實戰項目工程代碼 1. FLASH和EEPROM讀寫數據的對比 1.1 擦除操作 EEPROM通常支持按單字節擦除和寫入,這一特性使其非常適合需要頻繁更新小量數據的應…

探索Stream流:高效數據處理的秘密武器

不可變集合 stream流 Stream流的使用步驟: 先得到一條Stream流(流水線),并把數據放上去 使用中間方法對流水線上的數據進行操作 使用終結方法對流水線上的數據進行操作 Stream流的中間方法 注意1:中間方法&#xff0…

vue3筆記(自存)

1. Vue3簡介 2020年9月18日,Vue.js發布版3.0版本,代號:One Piece(n 經歷了:4800次提交、40個RFC、600次PR、300貢獻者 官方發版地址:Release v3.0.0 One Piece vuejs/core 截止2023年10月,最…

實驗4 mySQL查詢和視圖

一、實驗目的 掌握SELECT語句的基本語法多表連接查詢GROUP BY的使用方法。ORDER BY的使用方法。 二、實驗步驟、內容、結果 實驗內容: 實驗4.1數據庫的查詢 目的與要求 (1)掌握SELECT語句的基本語法。 (2)掌握子查詢的表示。 (3)掌握連接查詢的表示。 (4)掌…

【bug】fused_bias_act_kernel.cu卡住沒反應

簡述 在推理人臉修復face restoration算法 GPEN的時候,發現有時候fused_bias_act_kernel.cu卡住沒反應。 解決 清理下緩存,讓程序自己再編譯下

.net/C#進程間通信技術方案總結

C# 應用進程間通信(IPC)技術方案 進程間通信(Inter-Process Communication, IPC)是不同進程之間交換數據和消息的機制。以下是C#中常用的IPC技術方案: 1. 命名管道(Named Pipes) 適用于本地機器上的進程通信,支持雙向通信。 ??服務端示例??&…

阿里云服務器數據庫故障排查指南?

阿里云服務器數據庫故障排查指南? 以下是針對阿里云服務器(如ECS自建數據庫或阿里云RDS等托管數據庫)的故障排查指南,涵蓋常見問題的定位與解決方案: 一、數據庫連接失敗 檢查網絡連通性 ECS自建數據庫 確認安全組規則放行數據庫…

深度學習 ———— 遷移學習

遷移學習原理 什么是遷移學習? 遷移學習利用在大規模數據集(如ImageNet)上預訓練的模型,改裝小數據集(如CIFAR-10)。優勢: 減少訓練時間:預訓練模型已學習通用特征(如邊…

單調棧模版型題目(3)

單調棧型題目貢獻法 基本模版 這是數組a中的 首先我們要明白什么叫做貢獻,在一個數組b{1,3,5}中,連續包含1的連續子數組為{1},{1,3},{1,3,5},一共有三個,這三個數一共能組成6個連續子數組,而其…

日常知識點之隨手問題整理(思考單播,組播,廣播哪個更省帶寬)

新入職的公司在某些場景下無腦使用組播技術,自己突然就意識到一個問題:單播,組播,廣播,哪個更省帶寬? 有所收獲,做點筆記,僅僅是個人理解~ 1:簡單理解 單播&#xff1…

R1-Omni

一、Omni概述 Omni 文本視頻音頻,全模態。 R1Omni 強化學習全模態。 二、Omni舉例-humanOmni humanOmni:以人體姿態和人物交互為中心的全模態模型。 visual projector有3個,分別負責人臉標簽、姿態檢測、人和物交互。有點像moe。text enc…

linux中的日志分割

1.問題背景,nginx日志過大不好刪除 [rootlocalhost cron.daily]# cd /lk/nginx/log/ [rootlocalhost log]# ll 總用量 2386188 -rw-r--r--. 1 root root 2078699697 5月 9 13:02 access.log -rw-r--r--. 1 root root 11138 5月 6 10:28 error.log [rootloc…

華為云Flexus+DeepSeek征文|從開通到應用:華為云DeepSeek-V3/R1商用服務深度體驗

前言 本文章主要講述在華為云ModelArts Studio上 開通DeepSeek-V3/R1商用服務的流程,以及開通過程中的經驗分享和使用感受幫我更多開發者,在華為云平臺快速完成 DeepSeek-V3/R1商用服務的開通以及使用入門注意:避免測試過程中出現部署失敗等問…

【機器學習-線性回歸-5】多元線性回歸:概念、原理與實現詳解

線性回歸是機器學習中最基礎且廣泛應用的算法之一,而多元線性回歸則是其重要擴展。本文將全面介紹多元線性回歸的核心概念、數學原理及多種實現方式,幫助讀者深入理解這一強大的預測工具。 1. 多元線性回歸概述 1.1 什么是多元線性回歸 多元線性回歸(…

GOC指令

網絡版GoC常見繪圖命令說明 (V3.8) 目錄 l 基本畫圖命令 fd, bk, lt, rt l 設置筆狀態命令 c, rgb, size, up, down l 狀態命令 show, hide, speed, showXY, wait, pause, cls, clsRec l 增強畫圖命令 o, oo, e, ee, r, rr l 坐標命令 moveTo, lineTo, g…

Qt獲取CPU使用率及內存占用大小

Qt 獲取 CPU 使用率及內存占用大小 文章目錄 Qt 獲取 CPU 使用率及內存占用大小一、簡介二、關鍵函數2.1 獲取當前運行程序pid2.2 通過pid獲取運行時間2.3 通過pid獲取內存大小 三、具體實現五、寫在最后 ? 一、簡介 近期在使用軟件的過程中發現一個有意思的東西。如下所示&a…

期刊論文寫作注意點

下面給出關于期刊寫作的幾個關鍵注意點 一、摘要突出創新點 最重要的是論文的摘要,因為在論文送審的時候,編輯如果沒有時間,最先看的就是摘要。摘要要寫好。如果投的是頂刊,在摘要里面盡量不要寫是在什么方法的基礎上進行改進之類…

Swagger 3.0 中注解詳細示例

Swagger 3.0 提供了豐富的注解來詳細描述 API 的請求和響應。以下是一個使用 Operation、Parameter、RequestBody 和 ApiResponse 注解的示例,展示了如何設置請求頭、請求參數、路徑變量、請求體和響應體。代碼中未使用 DTO 對象,而是使用 Map 來傳遞參數…