上一節我們知道了 DNS 的兩項功能,第一是根據名稱查到具體的地址,另外一個是可以針對多個地址做負載均衡,而且可以在多個地址中選擇一個距離你近的地方訪問,
然而有時候這個地址簿也經常給你指錯路,明明距離你 500 米就有個吃飯的地方,非要把你推薦到 5 公里外,為什么會出現這樣的情況呢?
還記得嗎?當我們發出請求決議 DNS 的時候,首先,會先連接到運營商本地的 DNS 服務器,由這個服務器幫我們去整棵 DNS 樹上進行決議,然后將決議的結果回傳給客戶端,但是本地的 DNS 服務器,作為一個本地導游,往往有自己的“小心思”,
傳統 DNS 存在哪些問題?
1. 域名快取問題
它可以在本地做一個快取,也就是說,不是每一個請求,它都會去訪問權威 DNS 服務器,而是訪問過一次就把結果快取到自己本地,當其他人來問的時候,直接就回傳這個快取資料,
這就相當于導游去過一個飯店,自己腦子記住了地址,當有一個游客問的時候,他就憑記憶回答了,不用再去查地址簿,這樣經常存在的一個問題是,人家那個飯店明明都已經搬了,結果作為導游,他并沒有重繪這個快取,結果你辛辛苦苦到了這個地點,發現飯店已經變成了服裝店,你是不是會非常失望?
另外,有的運營商會把一些靜態頁面,快取到本運營商的服務器內,這樣用戶請求的時候,就不用跨運營商進行訪問,這樣既加快了速度,也減少了運營商之間流量計算的成本,在域名決議的時候,不會將用戶導向真正的網站,而是指向這個快取的服務器,
很多情況下是看不出問題的,但是當頁面更新,用戶會訪問到老的頁面,問題就出來了,例如,你聽說一個餐館推出了一個新菜,你想去嘗一下,結果導游告訴你,在這里吃也是一樣的,有的游客會覺得沒問題,但是對于想嘗試新菜的人來說,如果導游說帶你去,但其實并沒有吃到新菜,你是不是也會非常失望呢?
再就是本地的快取,往往使得全域負載均衡失敗,因為上次進行快取的時候,快取中的地址不一定是這次訪問離客戶最近的地方,如果把這個地址回傳給客戶,那肯定就會繞遠路,
就像上一次客戶要吃西湖醋魚的事,導游知道西湖邊有一家,因為當時游客就在西湖邊,可是,下一次客戶在靈隱寺,想吃西湖醋魚的時候,導游還指向西湖邊的那一家,那這就繞得太遠了,
2. 域名轉發問題
快取問題還是說本地域名決議服務,還是會去權威 DNS 服務器中查找,只不過不是每次都要查找,可以說這還是大導游、大中介,還有一些小導游、小中介,有了請求之后,直接轉發給其他運營商去做決議,自己只是外包了出去,
這樣的問題是,如果是 A 運營商的客戶,訪問自己運營商的 DNS 服務器,如果 A 運營商去權威 DNS 服務器查詢的話,權威 DNS 服務器知道你是 A 運營商的,就回傳給一個部署在 A 運營商的網站地址,這樣針對相同運營商的訪問,速度就會快很多,
但是 A 運營商偷懶,將決議的請求轉發給 B 運營商,B 運營商去權威 DNS 服務器查詢的話,權威服務器會誤認為,你是 B 運營商的,那就回傳給你一個在 B 運營商的網站地址吧,結果客戶的每次訪問都要跨運營商,速度就會很慢,
但是 A 運營商偷懶,將決議的請求轉發給 B 運營商,B 運營商去權威 DNS 服務器查詢的話,權威服務器會誤認為,你是 B 運營商的,那就回傳給你一個在 B 運營商的網站地址吧,結果客戶的每次訪問都要跨運營商,速度就會很慢,
3、NAT轉換問題
前面講述網關的時候,我們知道,出口的時候,很多機房都會配置 NAT,也即網路地址轉換,使得從這個網關出去的包,都換成新的 IP 地址,當然請求回傳的時候,在這個網關,再將 IP 地址轉換回去,所以對于訪問來說是沒有任何問題,
但是一旦做了網路地址的轉換,權威的 DNS 服務器,就沒辦法通過這個地址,來判斷客戶到底是來自哪個運營商,而且極有可能因為轉換過后的地址,誤判運營商,導致跨運營商的訪問,
4. 域名更新問題
本地 DNS 服務器是由不同地區、不同運營商獨立部署的,對域名決議快取的處理上,實作策略也有區別,有的會偷懶,忽略域名決議結果的 TTL 時間限制,在權威 DNS 服務器決議變更的時候,決議結果在全網生效的周期非常漫長,但是有的時候,在 DNS 的切換中,場景對生效時間要求比較高,
例如雙機房部署的時候,跨機房的負載均衡和容災多使用 DNS 來做,當一個機房出問題之后,需要修改權威 DNS,將域名指向新的 IP 地址,但是如果更新太慢,那很多用戶都會出現訪問例外,
這就像,有的導游比較勤快、敬業,時時刻刻關注酒店、餐館、交通的變化,問他的時候,往往會得到最新情況,有的導游懶一些,8 年前背的導游詞就沒換過,問他的時候,指的路往往就是錯的,
5. 決議延遲問題
從上一節的 DNS 查詢程序來看,DNS 的查詢程序需要遞回遍歷多個 DNS 服務器,才能獲得最終的決議結果,這會帶來一定的時延,甚至會決議超時,
HttpDNS 的作業模式
既然 DNS 決議中有這么多問題,那怎么辦呢?難不成退回到直接用 IP 地址?這樣顯然不合適,所以就有了 HttpDNS,
HttpDNS 其實就是,不走傳統的 DNS 決議,而是自己搭建基于 HTTP 協議的 DNS 服務器集群,分布在多個地點和多個運營商,當客戶端需要 DNS 決議的時候,直接通過 HTTP 協議進行請求這個服務器集群,得到就近的地址,
這就相當于每家基于 HTTP 協議,自己實作自己的域名決議,自己做一個自己的地址簿,而不使用統一的地址簿,但是默認的域名決議都是走 DNS 的,因而使用 HttpDNS 需要繞過默認的 DNS 路徑,就不能使用默認的客戶端,使用 HttpDNS 的,往往是手機應用,需要在手機端嵌入支持 HttpDNS 的客戶端 SDK,
通過自己的 HttpDNS 服務器和自己的 SDK,實作了從依賴本地導游,到自己上網查詢做旅游攻略,進行自由行,愛怎么玩怎么玩,這樣就能夠避免依賴導游,而導游又不專業,你還不能把他怎么樣的尷尬,
下面我來決議一下 HttpDNS 的作業模式,
在客戶端的 SDK 里動態請求服務端,獲取 HttpDNS 服務器的 IP 串列,快取到本地,隨著不斷地決議域名,SDK 也會在本地快取 DNS 域名決議的結果,
當手機應用要訪問一個地址的時候,首先看是否有本地的快取,如果有就直接回傳,這個快取和本地 DNS 的快取不一樣的是,這個是手機應用自己做的,而非整個運營商統一做的,如何更新、何時更新,手機應用的客戶端可以和服務器協調來做這件事情,
如果本地沒有,就需要請求 HttpDNS 的服務器,在本地 HttpDNS 服務器的 IP 串列中,選擇一個發出 HTTP 的請求,會回傳一個要訪問的網站的 IP 串列,
請求的方式是這樣的,
curl http://106.2.xxx.xxx/d?dn=c.m.163.com
{"dns":[{"host":"c.m.163.com","ips":["223.252.199.12"],"ttl":300,"http2":0}],"client":{"ip":"106.2.81.50","line":269692944}}
手機客戶端自然知道手機在哪個運營商、哪個地址,由于是直接的 HTTP 通信,HttpDNS 服務器能夠準確知道這些資訊,因而可以做精準的全域負載均衡,
當然,當所有這些都不作業的時候,可以切換到傳統的 LocalDNS 來決議,慢也比訪問不到好,那 HttpDNS 是如何解決上面的問題的呢?
其實歸結起來就是兩大問題,一是決議速度和更新速度的平衡問題,二是智能調度的問題,對應的解決方案是 HttpDNS 的快取設計和調度設計,
HttpDNS 的快取設計
決議 DNS 程序復雜,通信次數多,對決議速度造成很大影響,為了加快決議,因而有了快取,但是這又會產生快取更新速度不及時的問題,最要命的是,這兩個方面都掌握在別人手中,也即本地 DNS 服務器手中,它不會為你定制,你作為客戶端干著急沒辦法,
而 HttpDNS 就是將決議速度和更新速度全部掌控在自己手中,一方面,決議的程序,不需要本地 DNS 服務遞回的呼叫一大圈,一個 HTTP 的請求直接搞定,要實時更新的時候,馬上就能起作用;另一方面為了提高決議速度,本地也有快取,快取是在客戶端 SDK 維護的,過期時間、更新時間,都可以自己控制,
HttpDNS 的快取設計策略也是咱們做應用架構中常用的快取設計模式,也即分為客戶端、快取、資料源三層,
- 對于應用架構來講,就是應用、快取、資料庫,常見的是 Tomcat、Redis、MySQL,
- 對于 HttpDNS 來講,就是手機客戶端、DNS 快取、HttpDNS 服務器,
只要是快取模式,就存在快取的過期、更新、不一致的問題,解決思路也是很像的,
例如 DNS 快取在記憶體中,也可以持久化到存盤上,從而 APP 重啟之后,能夠盡快從存盤中加載上次累積的經常訪問的網站的決議結果,就不需要每次都全部決議一遍,再變成快取,這有點像 Redis 是基于記憶體的快取,但是同樣提供持久化的能力,使得重啟或者主備切換的時候,資料不會完全丟失,
SDK 中的快取會嚴格按照快取過期時間,如果快取沒有命中,或者已經過期,而且客戶端不允許使用過期的記錄,則會發起一次決議,保障記錄是更新的,
決議可以同步進行,也就是直接呼叫 HttpDNS 的介面,回傳最新的記錄,更新快取;也可以異步進行,添加一個決議任務到后臺,由后臺任務呼叫 HttpDNS 的介面,
同步更新的優點是實時性好,缺點是如果有多個請求都發現過期的時候,同時會請求 HttpDNS 多次,其實是一種浪費,
同步更新的方式對應到應用架構中快取的 Cache-Aside 機制,也即先讀快取,不命中讀資料庫,同時將結果寫入快取,
異步更新的優點是,可以將多個請求都發現過期的情況,合并為一個對于 HttpDNS 的請求任務,只執行一次,減少 HttpDNS 的壓力,同時可以在即將過期的時候,就創建一個任務進行預加載,防止過期之后再重繪,稱為預加載,
它的缺點是當前請求拿到過期資料的時候,如果客戶端允許使用過期資料,需要冒一次風險,如果過期的資料還能請求,就沒問題;如果不能請求,則失敗一次,等下次快取更新后,再請求方能成功,
異步更新的機制對應到應用架構中快取的 Refresh-Ahead 機制,即業務僅僅訪問快取,當過期的時候定期重繪,在著名的應用快取 Guava Cache 中,有個 RefreshAfterWrite 機制,對于并發情況下,多個快取訪問不命中從而引發并發回源的情況,可以采取只有一個請求回源的模式,在應用架構的快取中,也常常用資料預熱或者預加載的機制,
HttpDNS 的調度設計
由于客戶端嵌入了 SDK,因而就不會因為本地 DNS 的各種快取、轉發、NAT,讓權威 DNS 服務器誤會客戶端所在的位置和運營商,而可以拿到第一手資料,
在客戶端,可以知道手機是哪個國家、哪個運營商、哪個省,甚至哪個市,HttpDNS 服務端可以根據這些資訊,選擇最佳的服務節點回傳,
如果有多個節點,還會考慮錯誤率、請求時間、服務器壓力、網路狀況等,進行綜合選擇,而非僅僅考慮地理位置,當有一個節點宕機或者性能下降的時候,可以盡快進行切換,
要做到這一點,需要客戶端使用 HttpDNS 回傳的 IP 訪問業務應用,客戶端的 SDK 會收集網路請求資料,如錯誤率、請求時間等網路請求質量資料,并發送到統計后臺,進行分析、聚合,以此查看不同的 IP 的服務質量,
在服務端,應用可以通過呼叫 HttpDNS 的管理介面,配置不同服務質量的優先級、權重,HttpDNS 會根據這些策略綜合地理位置和線路狀況算出一個排序,優先訪問當前那些優質的、時延低的 IP 地址,
HttpDNS 通過智能調度之后回傳的結果,也會快取在客戶端,為了不讓快取使得調度失真,客戶端可以根據不同的移動網路運營商 WIFI 的 SSID 來分維度快取,不同的運營商或者 WIFI 決議出來的結果會不同,
小結
- 傳統的 DNS 有很多問題,例如決議慢、更新不及時,因為快取、轉發、NAT 問題導致客戶端誤會自己所在的位置和運營商,從而影響流量的調度,
- HttpDNS 通過客戶端 SDK 和服務端,通過 HTTP 直接呼叫決議 DNS 的方式,繞過了傳統 DNS 的這些缺點,實作了智能的調度,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/194168.html
標籤:其他
