可能大家都知道或者被問過一個問題,那就是很經典的「從瀏覽器輸入 URL 再到頁面展示,都發生了什么」。這個問題雖然簡單,但是真的能夠從回答的各種細節上看出不同人之間的水平差距。
這篇文章主要是聊一聊輸入 URL 之后的第一步——域名解析
域名就類似於 www.google.com,而通過 ping
命令,就可以查詢到對應域名的 IP 地址了。

那為什么又要有域名,又要有 IP 呢?
域名、IP 共存
首先還是解釋一下,為什么會出現現在這種域名、IP 地址共存的情況。主要有兩個點:
- 提升用戶體驗
- 提高運行效率
分別解釋一下,IP 地址長度為 32 位,平時用十進制來表示的話,就長這樣——192.168.1.0
,但是想象一下,如果我們要訪問某個網站需要讓我們輸入這么一長串數字,體驗肯定相當差,首先記憶這么長串數字對很多人來說就很痛苦,更何況我們常用的網站肯定不止一個。
除此之外,如果你給其他人推廣你的網站,你吧啦吧啦說了一大堆,然后來個「如果你感興趣,請訪問我們的網站 192.168.1.0」,然后就沒有然后了。
這也是為啥現在仍然在使用域名,方便人腦去記憶。
那為啥還需要 IP 地址呢?因為 IPv4 中的 IP 地址只需要 4 個字節,而用字符串表示的域名最少也需要幾十個字節,長的甚至達到幾百字節,而這會大大的增加底層路由器的負擔。
這也是為啥 IP 地址仍然在被使用。人來使用域名,而路由器層則使用 IP 地址,就跟我們書寫的是我們能認識的字符,而最終計算機認識的是一堆二進制一樣。
DNS 解析
知道了這個背景之后,我們就可以來看看「域名」是如果變成「IP 地址」的。
首先我們知道,會往 DNS 服務器發送請求,那問題就來了,瀏覽器怎么知道 DNS 服務器的地址是啥?

答案是提前配置好的。當然這不是唯一的方式,DNS 也有可能通過 DHCP(Dynamic Host Configuration Protocol) 動態分配的。
例如,MacOS 中的 DNS 配置就長下面這樣。

當然,你也可以通過命令行來查看、修改,地址在 /etc/resolv.conf
。
有了 DNS 服務器,那么你可能會覺得,接下來的事情就很簡單了:

我給你傳個域名,你返給我對應的 IP 地址即可。那問題來了,現在互聯網中有數萬台的 DNS 服務器,我怎么知道數據在哪台服務器上?難道要一台一台的遍歷請求這數萬台服務器嗎?
我相信你肯定沒有感知到在瀏覽器中輸入域名到頁面展示會花費那么久,這也說明肯定不是一台一台服務器進行遍歷的。
域名的組成
要了解 DNS 是如何對其進行優化的,我們需要先知道域名的組成部分。看到這,很可能你會這么想:
啥組成?不就是一堆字符串嗎?
實際上,域名是有由不同的域組成的,每個 .
隔開的部分就是一個域。
這里舉個例子,假設我們分析的域名為 www.google.com
,從我們平時寫快遞的收貨地址的慣性思維來看,這個域的各個部分大小可能是這樣的:
www > google > com
但是實際上並不是這樣,而是:
. > com > google > www
你甚至發現,最大的還是個 .
。其實完整的域名應該是 www.google.com.
,.
代表根域,因為根域對於所有的域名來說,意義都一樣,所以平時我們都把最后的點給省略了。
每個域都有自己的專屬名詞:
. > com > google > www
根域 | 一級級域|二級域名|(子域名)|主機名
當然,我們知道還可以針對二級域名再划分子域名,類似於 mail.google.com
。
所以看到這,你應該能夠理解域名是由層次的這個概念了,我再舉個比較的通俗的例子。
com 公司的 google 部門的 www。https://mail.google.com/mail/u/0/#inbox
DNS 的分層
了解完域名的分層之后,DNS 是如何優化域名解析的問題就迎刃而解了,那就是——分層。
DNS 服務器會將域名的數據分布式的存儲在各個 DNS 服務器上,但是同一個域的數據,會存儲在同一台 DNS 服務器上,同一台 DNS 服務器可以存儲多個域的數據。
這么說可能會有些抽象,一圖勝千言,其實就是這樣:

有了對數據的分層,那么查詢數據就會很有節奏感。
查詢域名數據
一圖勝千言,有了分層的機制,整個的查詢過程就會長這樣:

首先會去配置的 DNS 服務器中查詢,這個其實一般都是本地或者內網中的 DNS 服務器。如果沒有找到,就會去問根域要,說哥們,我這里需要 www.google.com
的 IP 地址。
根域一看,我這里沒有啊,但是我知道 com
域的 DNS 服務器地址,他可能知道。
然后 com
域的 DNS 服務器一看,www.google.com
的 IP 地址我也不知道,但是我知道 google.com
域的 DNS 服務器的地址,他可能知道,你再去問問他。
就這樣一路問下去,最終就能夠找到 www.google.com
所對應的 IP 地址了。
根域名服務器
看了上面的流程,可能你還是會有點疑問。因為去找 DNS 服務器查詢 IP 地址時,初始的 DNS 的服務器的 IP 地址是走的本地計算機的配置的。那在分層查詢時,我怎么知道有哪些根服務器?以及我怎么知道這些根服務器的 IP 地址是啥?
答案是內置。
我們的設備,或者說所有能上網的設備都會內置根服務器的列表。總共有 13 台根 DNS 服務器,分別是[a-m].root-servers.net
,這些根服務器的地址根本不需要查詢就能直接獲取。
當然,稍微想想也知道,13 台服務器是很難扛住全球互聯網用戶的請求的,實際上對於這 13 台服務器有很多的鏡像服務器。
眼見為實
說了這么多虛的概念,接下來我們通過 dig
命令來實際操作一下。

可以看到,在 QUESTION SECTION
下的完整域名是 www.google.com.
是帶了根域的,那后面的這個 IN
和 A
又是啥意思呢?
這是因為,在向 DNS 服務器查詢請求的時候,需要三個參數,分別是:
- 域名(例如 www.google.com)
- 網絡類型(Class 設計之初,考慮到了多種網絡並存的場景,但是目前實際上只有一種網絡——互聯網,所以該參數的值一直都會為 ——
IN
) - 類型(例如
A
表示 IP 地址,而MX
則表示郵件服務器的地址)
而在 ANSWER SECTION
中,則是 DNS 服務的響應結果,上圖中顯示了總有 6
條 DNS 記錄,並且在后面返回了其對應的 IP 地址。
而其中的 69
則是 TTL,單位是秒,代表了在 69 之內都不用再次發送請求了。
最下面則是統計的信息,本次 DNS 查詢所話費的時間,以及請求的 DNS 服務器的地址和端口。這個服務器地址是我們本機配置的 DNS 服務器的地址。
眼尖的可能發現了,上圖中根本沒有設計到對根服務器的請求。這是因為這個命令把這部分給省略掉了,我們可能通過加上 +trace
命令行參數來查看詳細的分級查詢過程。
這次我們以 www.36kr.com
來作為例子。

可以看到,上圖中列出了所有的根域名服務器,然后去找 com
域要,然后再找 36kr.com
域去要,最終是拿到了 www.36kr.com
的 IP 地址。
緩存機制
當然,如果每次都從根服務器開始往下找,明顯是不合理的,因為域名和 IP 地址的對應關系本來變動的就不頻繁,所以 DNS 服務器是都會將結果緩存的。
並且,在下圖中:

我只寫了一個 DNS 服務器中有同級別的域信息,但是實際上不同層級的域信息可能存在於同一個 DNS 服務器,舉個例子,com
域和 google.com
域可能在同一台機器上。
但是,這個緩存是有有效期的。如果在這個有效期內 DNS 數據發生了變化,那么緩存中的數據就會不正確,此時需要手動的將 DNS 刪除。