一次完整的HTTP接口請求過程及針對優化
客戶端發起http請求,基本的經歷過程如下:
域名解析 -> TCP三次握手 -> 建立TCP連接後發起HTTP請求 -> Nginx反向代理 -> 應用層 -> 服務層 -> 緩存/數據庫
一、域名解析
首先Chrome瀏覽器會解析 www.linux178.com 這個域名(準確的叫法應該是主機名)對應的IP地址。怎麽解析到對應的IP地址?
① Chrome瀏覽器 會首先搜索瀏覽器自身的DNS緩存(緩存時間比較短,大概只有1分鐘,且只能容納1000條緩存),看自身的緩存中是否有www.linux178.com 對應的條目,而且沒有過期,如果有且沒有過期則解析到此結束。
註:我們怎麽查看Chrome自身的緩存?可以使用 chrome://net-internals/#dns 來進行查看
② 如果瀏覽器自身的緩存裏面沒有找到對應的條目,那麽Chrome會搜索操作系統自身的DNS緩存,如果找到且沒有過期則停止搜索解析到此結束.
註:怎麽查看操作系統自身的DNS緩存,以Windows系統為例,可以在命令行下使用 ipconfig /displaydns 來進行查看
③ 如果在Windows系統的DNS緩存也沒有找到,那麽嘗試讀取hosts文件(位於C:\Windows\System32\drivers\etc),看看這裏面有沒有該域名對應的IP地址,如果有則解析成功。
④ 如果在hosts文件中也沒有找到對應的條目,瀏覽器就會發起一個DNS的系統調用,就會向本地配置的首選DNS服務器(一般是電信運營商提供的,也可以使用像Google提供的DNS服務器)發起域名解析請求(通過的是UDP協議向DNS的53端口發起請求,這個請求是遞歸的請求,也就是運營商的DNS服務器必須得提供給我們該域名的IP地址),運營商的DNS服務器首先查找自身的緩存,找到對應的條目,且沒有過期,則解析成功。如果沒有找到對應的條目,則有運營商的DNS代我們的瀏覽器發起叠代DNS解析請求,它首先是會找根域的DNS的IP地址(這個DNS服務器都內置13臺根域的DNS的IP地址),找打根域的DNS地址,就會向其發起請求(請問www.linux178.com這個域名的IP地址是多少啊?),根域發現這是一個頂級域com域的一個域名,於是就告訴運營商的DNS我不知道這個域名的IP地址,但是我知道com域的IP地址,你去找它去,於是運營商的DNS就得到了com域的IP地址,又向com域的IP地址發起了請求(請問www.linux178.com這個域名的IP地址是多少?),com域這臺服務器告訴運營商的DNS我不知道www.linux178.com這個域名的IP地址,但是我知道linux178.com這個域的DNS地址,你去找它去,於是運營商的DNS又向linux178.com這個域名的DNS地址(這個一般就是由域名註冊商提供的,像萬網,新網等)發起請求(請問www.linux178.com這個域名的IP地址是多少?),這個時候linux178.com域的DNS服務器一查,誒,果真在我這裏,於是就把找到的結果發送給運營商的DNS服務器,這個時候運營商的DNS服務器就拿到了www.linux178.com這個域名對應的IP地址,並返回給Windows系統內核,內核又把結果返回給瀏覽器,終於瀏覽器拿到了www.linux178.com 對應的IP地址,該進行一步的動作了。
註:一般情況下是不會進行以下步驟的
如果經過以上的4個步驟,還沒有解析成功,那麽會進行如下步驟(以下是針對Windows操作系統):
⑤ 操作系統就會查找NetBIOS name Cache(NetBIOS名稱緩存,就存在客戶端電腦中的),那這個緩存有什麽東西呢?凡是最近一段時間內和我成功通訊的計算機的計算機名和Ip地址,就都會存在這個緩存裏面。什麽情況下該步能解析成功呢?就是該名稱正好是幾分鐘前和我成功通信過,那麽這一步就可以成功解析。
⑥ 如果第⑤步也沒有成功,那會查詢WINS 服務器(是NETBIOS名稱和IP地址對應的服務器)
⑦ 如果第⑥步也沒有查詢成功,那麽客戶端就要進行廣播查找
⑧ 如果第⑦步也沒有成功,那麽客戶端就讀取LMHOSTS文件(和HOSTS文件同一個目錄下,寫法也一樣)
如果第八步還沒有解析成功,那麽就宣告這次解析失敗,那就無法跟目標計算機進行通信。只要這八步中有一步可以解析成功,那就可以成功和目標計算機進行通信。
建議:
(1)使用HTTPDNS之類的技術方案解決:HTTPDNS說白了就是用HTTP協議進行域名解析,替代現有的基於UDP的DNS協議,避免域名解析失敗,域名劫持等問題
(2)客戶端和服務器之間保持長連接,使用socket通信
(3)本地維護一個ip列表,直接使用ip進行請求,而非用域名,並定期去更新這個列表
二、TCP三次握手
建議:無
三、發起HTTP請求
建議:
(1)並行連接:通過多條TCP連接發起並發的HTTP請求
(2)持久連接:keep-alive,長連接,重用TCP連接,以消除連接和關閉的時延,以事務個數和時間來決定是否關閉連接
(3)管道化連接:通過共享TCP連接發起並發的HTTP請求
(4)使用Restful風格的API
(5)縮短參數長度、數量
(6)壓縮報文頭或請求內容的相關信息
(7)合並多個HTTP請求,避免不必要的浪費
四、Nginx反向代理
Nginx可以做反向代理服務器,在真正的服務層前面設置網關,用來接收接口請求,然後根據策略分發給後面的web服務器。這一層做好冗余:有兩臺nginx,一臺對線上提供服務,另一臺冗余以保證高可用,常見的實踐是keepalived存活探測,相同virtual IP提供服務。
負載均衡調度策略:
輪詢(RR):按一次循環的方式將請求調度到不同的服務器上。輪詢算法假設所有的服務器處理請求的能力都一樣,調度器會將所有的請求平均分配給每個真實服務器
加權輪詢(WRR):LVS會考慮每臺服務器的性能,並給每臺服務器添加一個權值,如果服務器A的權值為1,服務器B的權值為2,則調度器調度到服務器B的請求會是A的兩倍
最小連接(LC):把請求調度到連接數量最小的服務器上
加權最小連接(WLC):給每個服務器一個權值,調度器會盡可能保持服務器連接數量與權值之間的平衡
基於局部性最少的連接(lblc):根據請求的目標IP尋找最近該目標IP地址所使用的服務器,如果這臺服務器依然可用,並且有能力處理該請求,調度器會盡量選擇相同的服務器,否則會繼續選擇其他可行的服務器
帶復制的基於局部性最少連接(lblcr):記錄的不是一個目標IP與一臺服務器之間的連接記錄,它會維護一個目標IP到一組服務器之間的映射關系,防止單點服務器負載過高
目標地址散列調度(DH):通過散列函數將目標IP與服務器建立映射關系,出現服務器不可用或負載過高的情況下,發往該目標IP的請求會固定發給該服務器
源地址散列調度(SH):根據源地址散列算法進行靜態分配給固定的服務器資源
五、應用層
這一層涉及到路由協議,即請求的URL對應到哪個controller的代碼去進行處理,通常有框架會去做路由協議處理,這時就會涉及到參數過濾、添加header或body、格式化返回結果等。
這一層的高可用通過集群實現,假設反向代理層是nginx,nginx.conf裏能夠配置多個web後端,並且nginx能夠探測到多個後端的存活性。
自動故障轉移:當web-server掛了的時候,nginx能夠探測到,會自動的進行故障轉移,將流量自動遷移到其他的web-server,整個過程由nginx自動完成,對調用方是透明的。
應用邏輯處理:
讀:先讀緩存,緩存要有一定時間,盡量使緩存保持高命中率;緩存沒有則讀數據庫,讀完根據實際需要去保存到緩存中;緩存可以壓縮保存,減少空間
寫:能用異步代替同步的,使用異步(從業務角度來看);能用同步的,盡量不用異步(從技術角度來看)。
假如處理邏輯復雜,而且時間長,再不影響業務感知的情況下用異步代替同步,將數據寫入消息隊列,再由腳本去讀取執行,這樣在高並發情況下提高速度,減少崩潰。後期數據不一致可進行腳本或人工修補。
假如處理短小,而且涉及事務操作,那可以使用同步,減少不一致等錯誤。
六、服務層
這一層通常是微服務使用到,相關的讀寫處理邏輯如上。同時為上一層提供連接池,減少資源、時間開銷。
七、緩存、數據庫
這裏也提供連接池。
緩存層、數據庫層的話需要主從同步,然後雙主同步,一臺對線上提供服務,另一臺冗余以保證高可用。
mysql並發數 < redis並發數 < 消息隊列並發數 < 服務器的TCP連接數
TCP連接數:理論無上限:2的32次方(ip數)×2的16次方(port數),但是在unix/linux下限制連接數的主要因素是內存和允許的文件描述符個數(每個tcp連接都要占用一定內存,每個socket就是一個文件描述符),另外1024以下的端口通常為保留端口。
對server端,通過增加內存、修改最大文件描述符個數等參數,單機最大並發TCP連接數超過10萬是沒問題的。
一次完整的HTTP接口請求過程及針對優化