一個請求過來都經過了什麼(2017 年 http 版)
點選上方「 程式設計一生 」,獲取更多技術乾貨!
我面試人家的時候特別喜歡問一個問題:”請描述一下一個請求過來到響應完成都做了什麼,越詳細越好。” 對於一個高手來說,他只要回答好了這一個問題,技術面試就通過了。所以如果我要去面試,我就把這個問題的答案壓縮到40分鐘到1個小時。因為一般的技術面試都是這個時間段噠,雖然我其實很想講上兩天。哎,一看我們部門就是做業務的。
為了讓人家聽懂,我一般會設定一個業務場景。比如說:現在使用者要開始上傳一個視訊。那麼業務上要經過使用者開啟瀏覽器頁面,使用者點選[選擇視訊檔案]按鈕,JS端呼叫系統本地檔案選擇器,JS端將視訊資訊寫入到瀏覽器頁面,使用者點選[開始上傳],此時開始傳送請求。
我去哪裡都是隨身帶紙筆噠,為了直觀,我會畫出頁面效果,還不忘補充一句:選擇完視訊,JS端是可以獲取到視訊資訊噠,如:名稱,大小。可以根據業務需要直接在頁面上展示,使用者可以對名稱進行編輯。
那麼我就開始描述一下這個請求到響應都做了什麼啦:
一、 首先是關於HTTPS的
請求通過POST的方式經過HTTPS協議傳送到伺服器端。HTTPS本身並非協議,而是標準的HTTP協議架在SSL/TLS協議之上的一種結構。由於HTTP協議是基於TCP/IP進行通訊的,所以HTTPS必須暴露IP和埠,這部分不加密。
HTTPS需要在伺服器端生成私鑰,我們伺服器端用的RSA演算法加密噠(對於有好幾個演算法發明專利的我來說,面試時涉及到演算法的地方是絕對不能漏掉噠)。然後建立簽名請求的證書,然後可以去CA授權或者自己簽發證書,最後將證書配置到nginx裡。因為伺服器上HTTPS是我配的,所以我會把這部分詳細的講出來。
HTTPS在傳輸資料前需要客戶端與服務端進行一次握手,在握手過程中將確立雙方加密傳輸資料的密碼資訊。握手的時候才用非對稱加密和HASH演算法,握手後資料的傳輸才用對稱加密。
握手過程如下:
1. 瀏覽器將自己支援的一套加密規則傳送給網站。
2. 網站從中選出一組加密演算法與HASH演算法,並將自己的身份資訊用證書的形式傳送給瀏覽器。證書裡包含了網站地址,加密公約,以及證書的頒發機構等資訊。
3. 獲得網站證書之後瀏覽器要做以下工作:
a) 驗證證書的合法性(盤發證書的機構是否合法,是否過期,證書中包含的網站地址是否與正在訪問的地址一致等),如果證書受信任,則瀏覽器欄裡面會顯示一個小鎖頭,否則會給出證書不受信任的提示。一般的瀏覽器對於自己簽發的證書都會給不收資訊的提示。
b) 如果證書受信任,或者使用者接受了不受信任的證書,瀏覽器會生成一串隨機數的密碼,用最開始約定好的HASH方式,把握手訊息取HASH值,然後用用證書中提供的公鑰加密”握手訊息+握手訊息HASH值”傳送給服務端。
c) 服務端拿到客戶端傳來的密碼,用自己的私鑰來解密握手訊息取出隨機數密碼,再用隨機數密碼解密握手訊息與HASH值,並與傳過來的HASH值做對比確認是否一致。然後服務端自己也生成一個隨機密碼加密一段握手訊息(握手訊息+握手訊息的HASH值)給客戶端。
d) 客戶端用隨機數解密並計算握手訊息的HASH,如果與服務端發來的HASH一致,此時握手過程結束,之後的所有通訊資料將有之前瀏覽器和伺服器生成的隨機碼生成的一個新的隨機密碼並利用對稱加密演算法進行加密。利用客戶端和服務端的隨機碼來生成資料傳輸的隨機碼是為了防止寫死的假隨機碼帶來的安全隱患,使用對稱加密是因為對稱加密的加密加密過程比非對稱快得多。
常用的非對稱加密演算法有: RSA,DSA/DSS; 對稱加密演算法有: AES,RC4,3DES; HASH演算法:MD5,SHA1,SHA256。
二、然後是關於DNS的
請求會訪問我們公司雲端儲存那邊的DNS(Domain Name System, 域名系統),這是因特網上作為域名和IP地址相互對映的一個分散式資料庫,能夠使使用者更方便的訪問網際網路,而不用去記住能夠被機器直接讀取的IP數串。通過域名或主機名,最終得到該域名或主機名對應的IP地址的過程叫做域名解析(或主機名解析)。DNS協議執行在UDP協議之上,使用埠號53。DNS會有一些策略將靜態的東西直接返回給瀏覽器,只有動態部分才繼續向後面傳遞。發生過測試人員在同一臺機器上在不同的瀏覽器上用不同使用者登入,結果重新整理頁面看到使用者變了的奇怪現象,是DNS策略造成的(不負責這一塊,不贅述)。
三、 下面開始公司的7層代理
主要作用是轉發到對應的業務nginx代理,公司使用的是SLB進行流量分發,負載均衡(不負責這一塊,略過)
四、 nginx反向代理和負載均衡
我有個什麼都想說明白的習慣。提到反向代理,就要先說正向代理。正向代理一般叫代理。就是請求發起人找一個代理做一件事情,真正做事情的人只認識代理不認識請求發起人。常用FQ,就是http中介的一種:代理。所以我們使用FQ代理伺服器連線上了國外的網站,但那個網站並不知道我們在使用。
反向代理是服務提供方出一個代理,請求人只跟代理打交道。比如這裡請求只發給了nginx這個代理,後面的就是nginx自己進行處理,找到真正的業務處理伺服器。如果是靜態請求,如css,js啥的,就直接轉向靜態伺服器。如果是動態請求就轉向WEB應用伺服器。然而不管是靜態伺服器還是WEB應用伺服器都是有好幾臺伺服器。Nginx按照一定的策略對請求向業務伺服器進行分配,讓壓力不集中在一臺伺服器,這就是負載均衡了。
Nginx除了配置實際處理業務的伺服器,還可以配置一些安全策略,比如如果一個IP在1秒內請求了100次,那麼將視為攻擊,直接返回錯誤碼,不向後傳遞請求。還可以配置超時等待時間,多媒體檔案的允許大小等。
其實……,負載均衡在SLB代理那層已經做了,但是安全方面需要各個業務端自己處理,我們每個物理機基本上只提供一個服務,所以可以直接使用80埠的
五、 WEB應用服務
WEB應用服務總體採用SOA架構。分成WEB前端,後臺服務,API服務,共同模組和代理介面模組。
WEB前端是返回頁面的,API服務是PC,手機各端都用到的共同介面。後臺服務通過代理介面模組來和API服務及WEB前端服務進行通訊。代理接口裡含有傳遞訊息的可序列化POJO。共同模組含有一些公共方法。
請求根據nginx提供的IP和埠找到伺服器上對應的WEB前端服務。我們伺服器上部署的是resin。我們的WEB 服務採用的是SpringMVC框架(面試官哥哥會不會汗流滿面,終於說到正題上了)。SpringMVC的實現原理和核心思想老掉牙了,略過(面試官哥哥是不是要驚愕了,其實只想聽這個)。不過spring作為一個久經考驗的框架,裡面用到了幾乎所有的設計模式,是應該好好總結一下的(以後專門寫一篇,此處略過,面試官哥哥是不是又該開始擦汗了)。
Resin WEB應用中除了SpringMVC還有一些細小的邏輯,如一般會配置utf8編碼器,logback監聽器,guava限流。請求經過Spring載入上下文,上下文可以使用類載入,配置檔案載入等多種方式(策略模式)。載入後我們呼叫的只是ApplicationContext(門面模式),當然它同時也是一個Bean工廠(工廠模式)。DispatcherServlet將請求對映到處理器。我看過DispatcherServlet的原始碼,在初始化策略的時候,還支援多種型別的處理器(介面卡模式)。處理器要經過攔截器對請求的使用者身份等進行校驗,校驗不通過直接返回錯誤,校驗通過找到對應的controller然後就開始進行引數校驗。
校驗不成功返回錯誤碼。成功後HttpClient呼叫採用了Restful架構的視訊資源系統進行內容建立。建立不成夠返回錯誤碼。成功則需要在資料庫插入一條記錄。呼叫DUBBO後臺服務,服務的service層呼叫couchbase快取取出當前記錄是否存在,不存在則呼叫mybatis持久層框架到mysql資料庫進行操作。操作成功後httpclient呼叫Restful架構的雲端儲存那邊返回視訊上傳初始化引數。結果回到controller裡包裝成ModelAndView物件,再經過攔截器注入一些session引數。因為http協議是無狀態的,現在的系統又都是分散式的,不支援session,所以每次都需要在攔截器裡將引數再塞入進去。然後原路返回響應。客戶端收到響應後斷開連線。因為http請求是無連線的。
當然處理過程中會用到好多基於AOP的,比如除錯,分析埋點,日誌。
瀏覽器收到響應裡含有的上傳初始化連結(返回結果裡帶著的)就會呼叫這個連結將真正的視訊介質傳進去。經過別的部門的雲端儲存,雲轉碼。轉碼那邊會給我用訊息佇列,我們用的是qpidd mq採用direct的exchange傳送到佇列。我們這邊的監聽器監聽到佇列裡的訊息就將訊息解析存到資料庫。總體流程圖如下:
總結
本次公眾號文章共三篇《一個請求過來都經過了什麼?(Thrift版)》、《一個請求過來都經過了什麼?(2017年http版)》、《思維發散的雙刃劍》。首篇是剛寫的,第二篇是17年寫的。最後一篇是一些總結思考。