我面試人家的時候特別喜歡問一個問題:”請描述一下一個請求過來到響應完成都做了什么,越詳細越好。” 對於一個高手來說,他只要回答好了這一個問題,技術面試就通過了。所以如果我要去面試,我就把這個問題的答案壓縮到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反向代理和負載均衡
我有個什么都想說明白的習慣。提到反向代理,就要先說正向代理。正向代理一般叫代理。就是請求發起人找一個代理做一件事情,真正做事情的人只認識代理不認識請求發起人。常用翻牆,就是http中介的一種:代理。所以我們使用翻牆代理服務器連接上了國外的網站,但那個網站並不知道我們在使用。
反向代理是服務提供方出一個代理,請求人只跟代理打交道。比如這里請求只發給了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發送到隊列。我們這邊的監聽器監聽到隊列里的消息就將消息解析存到數據庫。總體流程圖如下:

