深入分析Java Web技术内幕读书笔记(一)浅析Web请求过程
隨著Web技術的快速發(fā)展,互聯(lián)網(wǎng)的網(wǎng)絡架構已經(jīng)從傳統(tǒng)的C/S架構轉變?yōu)锽/S架構,B/S架構相較于傳統(tǒng)的C/S架構,有諸多優(yōu)點,例如:提供了統(tǒng)一的操作方式,簡化了用戶的學習成本;便捷的開發(fā)方式大大提高了開發(fā)者的開發(fā)效率;遵循統(tǒng)一的HTTP請求協(xié)議,開發(fā)運營維護十分方便。
一、B/S網(wǎng)絡架構簡單概述
B/S網(wǎng)絡架構采用的是統(tǒng)一的應用層協(xié)議HTTP來進行數(shù)據(jù)的交互,與傳統(tǒng)的C/S應用采用的長連接交互方式不同,B/S應用是無狀態(tài)的短連接的通信方式。也就是說,一次請求對應一次響應,響應結束后,本次通信也就結束了,這種方式可以滿足大數(shù)據(jù)量的用戶的訪問需求,節(jié)約了物理資源。
我們最常見的操作就是在瀏覽器的地址欄輸入一個網(wǎng)絡地址,敲擊回車鍵即可在瀏覽器容器上看到服務器返回來的內(nèi)容,但是,在敲擊回車鍵之后,客戶端到服務端都具體完成了哪些操作,才能將數(shù)據(jù)以最美的狀態(tài)呈現(xiàn)在我們的面前,這個也是需要了解一下的。
根據(jù)上圖來簡單解釋一下當用戶輸入完網(wǎng)絡地址和敲擊回車鍵之后,瀏覽器和服務器都做了些什么動作:
-
當用戶在瀏覽器的地址欄輸入了www.csdn.net之后,首先瀏覽器將請求DNS服務器,請求DNS服務器解析當前URL,匹配當前URL對應的實際IP地址,當配到IP地址,瀏覽器向這個IP地址發(fā)送get請求,遠程服務器接收到請求之后,將用戶需要的數(shù)據(jù)返回給用戶。
-
在實際的服務端,往往伴隨著很多復雜的業(yè)務邏輯,服務器有很多臺,但是具體有哪一臺服務器來提供服務,往往是由負載均衡設備來均衡分配。還有一點就是用戶請求的數(shù)據(jù)也許是一個文件,那么服務器就需要訪問文件系統(tǒng),獲取指定文件,也許用戶需要的資源在緩存系統(tǒng)中已經(jīng)緩存了,那么服務器優(yōu)先訪問緩存文件,也許用戶需要的數(shù)據(jù)直接存儲在數(shù)據(jù)庫中,那么服務器就需要訪問數(shù)據(jù)庫系統(tǒng),獲取數(shù)據(jù)。
-
當瀏覽器接收到服務器返回的數(shù)據(jù)后,解析發(fā)現(xiàn)有許多靜態(tài)資源是存儲在CDN上,那么將再次向CDN服務器發(fā)送HTTP請求,那么CDN又將會處理這些請求,流程和上面的類似。至于這里面的更多細節(jié),都會影響最終的數(shù)據(jù)完整返回。
那么對于一個完整的B/S應用,不管網(wǎng)絡架構如何變化,它應該始終需要遵循一些基本原則:
-
每一個資源存在互聯(lián)網(wǎng)的某一個角落,訪問該資源,需要使用唯一的一個URL來描述其位置;
-
資源的訪問與交互都需要基于HTTP協(xié)議,這樣才可以與遠程服務器正確地打招呼;
-
需要使用瀏覽器來還原數(shù)據(jù),客戶端拿到數(shù)據(jù)以后,數(shù)據(jù)的展示一般都需要瀏覽器來進行渲染還原。
二、如何發(fā)起一個請求
發(fā)起HTTP請求最常見的方式就是在瀏覽器地址欄輸入URL,敲擊回車鍵就發(fā)起了一個HTTP請求,比如在地址欄輸入www.csdn.net,敲擊回車鍵之后很快瀏覽器就接收到了服務器返回的數(shù)據(jù)并渲染完畢,以最佳的方式將數(shù)據(jù)還原,這是一種最基本的發(fā)起請求的方式。還有稍微復雜一點的方式,那就是自己組裝HTTP請求頭和請求體,也可以實現(xiàn)脫離瀏覽器發(fā)起HTTP請求。對于發(fā)起HTTP請求,其實和服務器建立Socket連接區(qū)別不大,只不過outputStream.write方法輸出的二進制數(shù)據(jù)格式要符合HTTP規(guī)范。在瀏覽器和服務器建立Socket連接之前,必須要執(zhí)行的一個動作就是解析URL的域名,獲取域名對應的IP地址,在根據(jù)這個地址和默認的80端口建立起Socket連接,然后在獲取URL中的參數(shù)組成一個get請求,使用outputStream.write方法發(fā)送到目標服務器,服務器等待inputStream.read方法讀入?yún)?shù)并執(zhí)行處理邏輯,然后返回數(shù)據(jù)后斷開連接。
對于復雜一點的HTTP請求,我們完全可以根據(jù)HTTP的規(guī)范來自己組裝請求頭和請求體,從而實現(xiàn)模仿瀏覽器發(fā)起請求。下面的代碼是借助hutool工具包來發(fā)起一個post請求。
這里是我在項目中封裝了一個私有方法,傳入的參數(shù)是請求頭模型和參數(shù)模型,請求頭模型中可以提供host,RESTful API,method等信息,請求參數(shù)體直接通過JSONUtil轉換為json字符串,然后直接發(fā)起post請求。當然,這里使用的是hutool工具包的發(fā)起請求的方法,還有較為出名的HttpClient也可以做到從代碼層面處理HTTP請求。
在linux系統(tǒng)中,還可以通過命令行來發(fā)起請求,例如curl "https://www.baidu.com",可以返回百度首頁頁面的HTML數(shù)據(jù),由于不是使用瀏覽器發(fā)起的請求,所以這些數(shù)據(jù)無法正常解析從而展示百度首頁。當然linux中還有wget命令可以實現(xiàn)發(fā)起文件下載的請求,可以輕松輕松從互聯(lián)網(wǎng)下載文件。
如果需要查看本次訪問的HTTP頭的信息,在命令后面加上-I即可:
三、分析常見的HTTP信息
常常與B/S網(wǎng)絡架構打交道的開發(fā)者,都需要對HTTP有一定的了解,HTTP是B/S網(wǎng)絡架構的精髓,想要理解HTTP,那么首先得熟悉HTTP Header,HTTP Header基本上是控制了用戶訪問互聯(lián)網(wǎng)資源的命脈,比如訪問資源的位置,訪問的方式,解析響應內(nèi)容的解碼方式,內(nèi)容的獲取是否優(yōu)先從緩存中獲取等等。當響應頭中檢測到404狀態(tài)碼,瀏覽器就會渲染出頁面丟失或者不存的提示信息等。HTTP Header通常分為四部分,分別是:General Headers, Request Headers, Response Headers, Entity Headers。查看這些基本的消息頭,可以使用瀏覽器自帶的控制臺對其進行查看。
表1-1 :常見的General Header
| Request URL | 請求的URL | https://www.baidu.com/ |
| Request Method | 請求方法 | GET |
| Status Code | 狀態(tài)碼 | 200 |
| Remote Address | 遠程IP地址 | 111.13.100.91:443 |
| Referrer Policy | 來源協(xié)議 | unsafe-url |
關于上面的各個通用頭的示例值,往往有多種,比如Request Method還有POST、PUT等方法,Status Code還有403、404、500等狀態(tài)碼,Referrer Policy還有no-referrer、origin等來源協(xié)議。通過瀏覽器自帶的控制臺可以查看這些基本信息:
表1-2:常見的Request Headers
| Accept | 用于指定客戶端可接收的內(nèi)容 | text/html,application |
| Accept-Charset | 用于指定客戶端接收的字符集 | UTF-8 |
| Accept-Encoding | 用于可接收的內(nèi)容編碼 | gzip, deflate, br |
| Accept-Language | 用于指定一種自然語言 | zh-cn |
| Host | 用于指定被請求的資源的主機和端口號 | www.baidu.com |
| User-Agent | 瀏覽器將本機系統(tǒng)、瀏覽器等屬性作為值傳遞給服務器 | Mozilla/5.0… |
| Cookie | 一般存儲一些與服務器交互的基本憑證信息 | … |
訪問百度首頁時候的請求頭截圖如下:
表1-3:常見的Response Header
| Server | 遠程的服務器名稱 | BWS/1.1 |
| Content-Type | 用于指明發(fā)送給接收者的實體正文的媒體類型 | text/html;charset=utf-8 |
| Content-Encoding | 與請求報頭Accept-Encoding對應,告訴瀏覽器服務端編碼方式 | gzip |
| Content-Language | 與請求報頭Accept-Encoding對應,描述了當前資源的采用的自然語言 | zh-cn |
| Content-Length | 指明實體正文的長度 | 128 |
| Keep-Alive | 保持連接的時間 | timeout=5,max=120 |
訪問百度首頁時候的響應頭截圖如下:
表1-4:常見的HTTP狀態(tài)碼
| 200 | 客戶端請求成功 |
| 302 | 臨時跳轉,跳轉的地址由Location指定 |
| 400 | 服務器無法識別客戶端的請求,請求語法錯誤 |
| 403 | 服務器接收到請求,但是拒絕為客戶端提供服務 |
| 404 | 請求資源不存在 |
| 500 | 服務器內(nèi)部錯誤 |
四、理解瀏覽器的緩存機制
瀏覽器的緩存機制是一個比較復雜且很重要的機制,在實際的使用中往往會提高頁面的響應速度,但在開發(fā)過程中,往往許多靜態(tài)資源的修改卻不能及時從服務器同步到瀏覽器,導致開發(fā)效率下降。一般來說,在開發(fā)過程中如果頁面上某些功能沒有生效,優(yōu)先考慮的應該是緩存的原因導致的,所以推薦windows用戶按Ctrl+F5組合鍵來對頁面重新發(fā)起請求,推薦Mac用戶使用Command+Shift+R組合鍵來強制刷新,使得數(shù)據(jù)都從服務器獲取而不是瀏覽器緩存中獲取。雖然強制刷新是將請求發(fā)送到了服務器,但是獲取的數(shù)據(jù)也不一定是最新的,因為某些服務器也會對數(shù)據(jù)進行緩存,為了提高自身的響應速度,所以為了保證用戶獲取的最新的數(shù)據(jù),可以通過HTTP Header來進行控制。
細心的朋友會發(fā)現(xiàn),使用Ctrl+F5的強制刷新方式和普通的刷新方式,在瀏覽器的控制臺會顯示出不同的內(nèi)容,一般的普通刷新方式,在瀏覽器的控制臺的NetWork一欄里會顯示很多請求(大部分靜態(tài)資源)會顯示“from disk cache”,如下圖所示:
當強制刷新之后,則不會顯示“from disk cache”,如下圖所示:
當然,這只是表面現(xiàn)象,其實,普通刷新和強制刷新的區(qū)別應該從請求頭上來體現(xiàn)。當使用普通刷新的時候,請求頭顯示如下:
當使用強制刷新的時候,請求頭顯示如下:
觀察這兩次刷新發(fā)起的請求,第二次請求在請求頭中添加了一對鍵值屬性:Pragma:no-cache,并且將Cache-Control的值有max-age=0變成了no-cache,為什么改變了這兩個配置項,就可以實現(xiàn)繞過緩存,直接向服務器發(fā)起請求?還有那些配置項有類似的作用?
Cache-Control/Pragma
這兩個HTTP Head字段起到了控制瀏覽器和緩存代理服務器的緩存行為,它們作用與請求鏈和響應鏈中,使得在緩存行為中必須遵循這兩個字段的要求。Cache-Control是最重要的規(guī)則。這個字段用于指定所有緩存機制在整個請求/響應鏈中必須服從的指令。這些指令指定用于阻止緩存對請求或響應造成不利干擾的行為。這些指令通常覆蓋默認緩存算法。緩存指令是單向的,即請求中存在一個指令并不意味著響應中將存在同一個指令。Cache-Control的屬性值可以配置如下表1-5:
表1-5 Cache-Control字段的可選值
| Public | 所有內(nèi)容都將被緩存,在響應頭中設置 |
| Private | 內(nèi)容只設置到私有緩存中,在響應頭中設置 |
| no-cache | 所有內(nèi)容都不會被緩存,可以在請求頭和響應頭中設置 |
| no-store | 所有內(nèi)容都不會被緩存到緩存或者Internet臨時文件中,在響應頭中設置 |
| must-revalidation/proxy-revalidation | 如果緩存內(nèi)容失效,請求必須發(fā)送到服務器/代理以進行重新驗證,在請求頭中設置 |
| max-age=xxx | 緩存將在xxx秒后失效,這個選項只可在HTTP 1.1中可用,和Last-Modified一起使用時優(yōu)先級較高,在響應頭中設置 |
Pragma字段的作用和Cache-Control有點類似,它也是在HTTP頭中包含一個特殊的指令,使相關的服務器遵守該指令,最常用的說就是Pragma:no-cache,他和Cache-Control:no-cache作用是一樣的。
Expires
Expires指的是過期時間,由響應頭設置,常見的格式是Expires:Thur,02 Aug 2018 09:50:34 GMT,后面跟著一個日期和時間,超過這個值后,緩存就過期了,瀏覽器在發(fā)起請求之前,會檢查該值,如果過期,就直接向服務器發(fā)起請求。
Last-Modified/Etag
Last-Modified表示資源在服務器上最后的修改時間,靜態(tài)資源返回到客戶端的時候會自動帶上這個字段,并指明最后修改時間,動態(tài)資源可以由代碼來進行控制,比如Servlet提供了一個getLastModified方法來檢查某個動態(tài)資源是否已經(jīng)改變,這個字段可以保證當前請求的資源是最新的。一般瀏覽器在發(fā)起請求的時候,會在請求頭中多出一個字段,If-Modified-Since:Thur,02 Aug 2018 09:50:34 GMT,檢查當前是否最后的修改是不是當前這個時間,如果是,將直接從緩存中獲取數(shù)據(jù),并返回304狀態(tài)碼,否則將重新發(fā)起請求從服務器獲取最新數(shù)據(jù)。
Etag標簽是讓服務器為每個頁面分配一個唯一的編號,然后通過這個編號來辨別當前資源是否是最新的,它比Last-Modified更加靈活,但是有多臺后臺服務器的時候,服務器要記住所有資源的編號,那就顯得有點多余了。
本篇文章簡單的分析了一下發(fā)起一個HTTP請求的一些細節(jié)問題,當然真正的請求肯定不是這般輕描淡寫而能表達清楚的,后面的系列文章將繼續(xù)記錄一些細節(jié)問題,歡迎關注。
深入分析Java Web技術內(nèi)幕系列讀書筆記文章列表:
深入分析Java Web技術內(nèi)幕讀書筆記(一)淺析Web請求過程
深入分析Java Web技術內(nèi)幕讀書筆記(二)淺析DNS域名解析過程
更多干貨分享,歡迎關注我的微信公眾號:爪哇論劍(微信號:itlemon)
總結
以上是生活随笔為你收集整理的深入分析Java Web技术内幕读书笔记(一)浅析Web请求过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 老婆云南旅行日志
- 下一篇: Springer Latex 引用参考文