11.25 為什麼 DNS 使用 UDP 協議?

為什麼這麼設計(Why’s THE Design)是一系列關於計算機領域中程序設計決策的文章,我們在這個系列的每一篇文章中都會提出一個具體的問題並從不同的角度討論這種設計的優缺點、對具體實現造成的影響。如果你有想要了解的問題,可以在文章下面留言。

今天要分析的具體問題是『為什麼 DNS 使用 UDP 協議』,DNS 作為整個互聯網的電話簿,它能夠將可以被人理解的域名翻譯成可以被機器理解的 IP 地址,使得互聯網的使用者不再需要直接接觸很難閱讀和理解的 IP 地址。作者曾經在 一文中介紹過 DNS 的實現原理,這篇文章中就不會介紹 DNS 的實現原理了,感興趣的讀者可以看一下。

為什麼 DNS 使用 UDP 協議?

相信 DNS 使用 UDP 協議已經成為了軟件工程師的常識,對計算機網絡稍有了解的人都知道 DNS 會使用 UDP 協議傳輸數據,但是這一觀點其實不是完全正確的,我們在這裡就會詳細分析『為什麼 DNS 會使用 UDP 傳輸數據』以及『為什麼 DNS 不止會使用 UDP 傳輸數據』兩個問題,希望能夠幫助各位讀者理解 DNS 協議的全貌。

概述

我們將要討論的兩個問題其實並不衝突,在絕大多數情況下,DNS 都是使用 UDP 協議進行通信的,DNS 協議在設計之初也推薦我們在進行域名解析時首先使用 UDP,這確實能解決很多需求,但是不能解決全部的問題。

實際上,DNS 不僅使用了 UDP 協議,也使用了 TCP 協議,不過在具體介紹今天的問題之前,我們還是要對 DNS 協議進行簡單的介紹:DNS 查詢的類型不止包含 A 記錄、CNAME 記錄等常見查詢,還包含 AXFR 類型的特殊查詢,這種特殊查詢主要用於 ,它的作用就是在多個命名服務器之間快速遷移記錄,由於查詢返回的響應比較大,所以會使用 TCP 協議來傳輸數據包。

作為被廣泛使用的協議,我們能夠找到非常多 DNS 相關的 RFC 文檔, 中列出了將近 300 個與 DNS 協議相關的 RFC 文檔,其中有 6 個是目前的互聯網標準,有 102 個是 DNS 相關的提案,這些文檔共同構成了我們目前對於 DNS 協議的設計理解,作者也沒有辦法去一一閱讀其中的內容,只選擇了其中一些重要的文檔幫我們理解 DNS 的發展史以及它與 UDP/TCP 協議的關係,這裡只會摘抄文檔中與 UDP/TCP 協議相關的內容:

  1. Internet Standard, 1987-11
  • DNS 查詢可以通過 UDP 數據包或者 TCP 連接進行傳輸;
  • 由於 DNS 區域傳輸的功能對於數據的準確有著較強的需求,所以我們必須使用 TCP 或者其他的可靠協議來處理 AXFR 類型的請求;

2. 互聯網支持命名服務器通過 TCP 或者 UDP 協議進行訪問;

  • UDP 協議攜帶的消息不應該超過 512 字節,超過的消息會被截斷並設置 DNS 協議的 TC 位,UDP 協議對於區域傳輸功能是不可接受的,不過是互聯網上標準查詢的推薦協議。通過 UDP 協議發送的查詢可能會丟失,所以需要重傳策略解決這個問題;

3. Internet Standard, 1989-10

  • 未來定義的新 DNS 記錄類型可能會包含超過 512 字節的信息,所以我們應該使用 TCP 協議來傳輸 DNS 記錄;因此解析器和命名服務需要使用 TCP 協議作為 UDP 無法滿足需求時的備份;
  • DNS 解析器和遞歸服務器必須支持 UDP 協議,並且應該支持使用 TCP 協議發送非區域傳輸的查詢;也就是說,DNS 解析器或者服務器在發送非區域傳輸查詢時,必須先發送一個 UDP 查詢,如果該查詢的響應被截斷,它應該嘗試使用 TCP 協議重新請求;

4. Internet Standard, 2003-10

  • 通過 DNS 擴展支持 IPv6 協議,每個 IPv6 佔 16 個字節是 IPv4 的四倍;

5. Independent, 2007-10

  • 新增多種資源記錄為 DNS 客戶端的 DNS 數據來源進行認證,記錄包含的數據往往較大;

6. Internet Standard, 2011-09

  • 選擇合適的鍵大小進行加密是需要在成本、性能和風險之間的權衡,然而大的鍵(4096-bit)可能沒有辦法直接放到 DNS UDP 響應包中直接返回;

7. Internet Standard, 2013-04

  • 使用 UDP 進行傳輸的 DNS 查詢和響應最大不能超過 512 字節,不能支持大量 IPv6 地址或者 DNS 安全簽名等記錄的傳輸;
  • EDNS 為 DNS 提供了擴展功能,讓 DNS 通過 UDP 協議攜帶最多 4096 字節的數據;

8. Proposed Standard, 2016-03

  • 當客戶端接收到一個被階段的 DNS 響應時,應該通過 TC 字段判斷是否需要通過 TCP 協議重複發出 DNS 查詢請求;
  • DNSSEC 的引入使得截斷的 UDP 數據包變得非常常見;
  • 使用 UDP 傳輸 DNS 的數據包大小超過最大傳輸單元(MTU)時可能會導致 IP 數據包的分片,RFC1123 文檔中預測的未來已經到來了,唯一一個用於增加 UDP 能夠攜帶數據包大小的 EDNS 機制被認為不夠可靠;
  • 所有通用 DNS 實現必須要同時支持 UDP 和 TCP 傳輸協議,其中包括權威服務器、遞歸服務器以及樁解析器;
  • 樁解析器和遞歸解析器可以根據情況選擇使用 TCP 或者 UDP 查詢直接請求目標服務器,以 UDP 協議來開始發起 DNS 請求不再是強制性的,TCP 協議與 UDP 協議在 DNS 查詢中可以互相替代,而不是作為重試機制;

9. Proposed Standard, 2016-05

  • 在 DNS 協議中引入 TLS 來為用戶提供隱私,減少對 DNS 查詢的竊聽和篡改,但是 TLS 協議的引入會帶來一些性能方面的額外開銷;

10. Proposed Standard, 2018-10

  • 定義了一種通過 HTTPS 發送 DNS 查詢和獲取 DNS 響應的協議;

我們可以簡單總結一下 DNS 的發展史,1987 年的 和 定義了最初版本的 DNS 協議,剛被設計出來的 DNS 就會同時使用 UDP 和 TCP 協議,對於絕大多數的 DNS 查詢來說都會使用 UDP 數據報進行傳輸,TCP 協議只會在區域傳輸的場景中使用,其中 UDP 數據包只會傳輸最大 512 字節的數據,多餘的會被截斷;兩年後發佈的 預測了 DNS 記錄中存儲的數據會越來越多,同時也第一次顯示的指出了發現 UDP 包被截斷時應該通過 TCP 協議重試。

為什麼 DNS 使用 UDP 協議?

過了將近 20 年的時間,由於互聯網的發展,人們發現 IPv4 已經不夠分配了,所以引入了更長的 IPv6,DNS 也在 2003 年發佈的 中進行了協議上的支持;隨後發佈的 和 增加了在鑑權和安全方面的支持,但是也帶來了巨大的 DNS 記錄,UDP 數據包被截斷變得非常常見。

提供的 DNS 擴展機制能夠幫助我們在一定程度上解決大數據包被截斷的問題,減少了使用 TCP 協議進行重試的需要,但是由於最大傳輸單元的限制,這並不能解決所有問題。

DNS 出現之後的 30 多年, 才終於提出了使用 TCP 協議作為主要協議來解決 UDP 無法解決的問題,TCP 協議也不再只是一種重試時使用的機制,隨後出現的 DNS over TLS 和 DNS over HTTP 也都是對 DNS 協議的一種補充。

從這段發展時來看,DNS 並不只是使用 UDP 數據包進行通信,在 DNS 的標準中就一直能看到 TCP 協議的身影,我們在今天也是想要站在歷史的角度上分析 ——『為什麼 DNS 查詢選擇使用 UDP/TCP 協議』。

設計

在這一節中,我們將根據 DNS 使用協議的不同,分兩個部分介紹 UDP 和 TCP 兩種不同的協議在支持 DNS 查詢和響應時有哪些優點和缺點,在分析的過程中我們也會結合歷史上的上下文,還原做出設計決策時的場景。

UDP

UDP 協議在過去的幾十年中其實都是 DNS 主要使用的協議,作為互聯網的標準,目前的絕大多數 DNS 請求和響應都會使用 UDP 協議進行數據的傳輸,我們通過抓包工具就能輕鬆獲得以 UDP 協議為載體的 DNS 請求和響應。

DNS 請求的數據都會以二進制的形式封裝成如下的所示的 UDP 數據包中,下面就是一個調用 DNS 服務器獲取 www.baidu.com 域名 IP 地址的請求,從第四行的 05 字節開始到最後就是 DNS 請求的內容,整個數據包中除了 DNS 協議相關的內容之外,還包含以太網、IP 和 UDP 的協議頭:

0000 b0 6e bf 6a 4c 40 38 f9 d3 ce 10 a6 08 00 45 00 [email protected].

0010 00 3b 97 ae 00 00 40 11 0b 0a c0 a8 32 6d 72 72 .;[email protected]

0020 72 72 f3 27 00 35 00 27 6b ee 0c 5a 01 00 00 01 rr.'.5.'k..Z....

0030 00 00 00 00 00 00 03 77 77 77→05 62 61 69 64 75 .......www.baidu

0040 03 63 6f 6d 00 00 01 00 01 .com.....

雖然每一個 UDP 數據包中都包含了很多以太網、IP、UDP 以及 DNS 協議的相關內容,但是上面的 DNS 請求大小隻有 73 個字節,上述 DNS 請求的響應也只有 132 個字節,這對於今天其他的常見請求來講都是非常小的數據包:

0000 38 f9 d3 ce 10 a6 b0 6e bf 6a 4c 40 08 00 45 00 [email protected].

0010 00 76 00 00 00 00 96 11 4c 7d 72 72 72 72 c0 a8 .v......L}rrrr..

0020 32 6d 00 35 f3 27 00 62 5b c2 0c 5a 81 80 00 01 2m.5.'.b[..Z....

0030 00 03 00 00 00 00 03 77 77 77 05 62 61 69 64 75 .......www.baidu

0040 03 63 6f 6d 00 00 01 00 01 c0 0c 00 05 00 01 00 .com............

0050 00 02 cb 00 0f 03 77 77 77 01 61 06 73 68 69 66 ......www.a.shif

0060 65 6e c0 16 c0 2b 00 01 00 01 00 00 01 18 00 04 en...+..........

0070 3d 87 a9 7d c0 2b 00 01 00 01 00 00 01 18 00 04 =..}.+..........

0080 3d 87 a9 79 =..y

UDP 和 TCP 的通信機制非常不同,作為可靠的傳輸協議,TCP 協議需要通信的雙方通過 建立 TCP 連接後才可以通信,但是在 30 年前的 DNS 查詢的場景中我們其實並不需要穩定的連接(或者以為不需要),每一次 DNS 查詢都會直接向命名服務器發送 UDP 數據報,與此同時常見 DNS 查詢的數據包都非常小,TCP 建立連接會帶來以下的額外開銷:

  • TCP 建立連接需要進行三次網絡通信;
  • TCP 建立連接需要傳輸 ~130 字節的數據;
  • TCP 銷燬連接需要進行四次網絡通信;
  • TCP 銷燬連接需要傳輸 ~160 字節的數據;

假設網絡通信所消耗的時間是可以忽略的不計的,如果我們只考慮 TCP 建立連接時傳輸的數據的話,可以簡單來算一筆賬:

為什麼 DNS 使用 UDP 協議?
  • 使用 TCP 協(共 330 字節)
  • 三次握手 — 14x3(Ethernet) + 20x3(IP) + 44 + 44 + 32 字節
  • 查詢協議頭 — 14(Ethernet) + 20(IP) + 20(TCP) 字節
  • 響應協議頭 — 14(Ethernet) + 20(IP) + 20(TCP) 字節
  • 使用 UDP 協議(共 84 字節)
  • 查詢協議頭 — 14(Ethernet) + 20(IP) + 8(UDP) 字節
  • 響應協議頭 — 14(Ethernet) + 20(IP) + 8(UDP) 字節

需要注意的是,我們在這裡計算結果的前提是 DNS 解析器只需要與一個命名服務器或者權威服務器進行通信就可以獲得 DNS 響應,但是在實際場景中,DNS 解析器可能會遞歸地與多個命名服務器進行通信,這也加倍地放大了 TCP 協議在額外開銷上的劣勢。

如果 DNS 查詢的請求體和響應分別是 15 和 70 字節,那麼 TCP 相比於 UDP 協議會增加 ~250 字節和 ~145% 的額外開銷,所以當請求體和響應的大小比較小時,通過 TCP 協議進行傳輸不僅需要傳輸更多的數據,還會消耗更多的資源,多次通信以及信息傳輸帶來的時間成本在 DNS 查詢較小時是無法被忽視的,TCP 連接帶來的可靠性在 DNS 的場景中沒能發揮太大的作用。

TCP

今天的網絡狀況其實沒有幾十年前設計的那麼簡單,我們不僅遇到了 IPv4 即將無法分配的狀況,而且還需要引入 DNSSEC 等機制來保證 DNS 查詢和請求的完整性以及傳輸安全,總而言之,DNS 協議需要處理的數據包越來越大、數據也越來越多,但是『為什麼當需要傳輸的數據較多時我們就必須使用 TCP 協議呢?』,如果繼續使用 UDP 協議就不能完成 DNS 解析麼。

從理論上來說,一個 UDP 數據包的大小最多可以達到 64KB,這對於一個常見的 DNS 查詢其實是一個非常大的數值;但是在實際生產中,一旦數據包中的數據超過了傳送鏈路的最大傳輸單元(MTU,也就是單個數據包大小的上限,一般為 1500 字節),當前數據包就可能會被分片傳輸、丟棄,部分的網絡設備甚至會直接拒絕處理包含 EDNS(0) 選項的請求,這就會導致使用 UDP 協議的 DNS 不穩定。

TCP 作為可靠的傳輸協議,可以非常好的解決這個問題,通過序列號、重傳等機制能夠保證消息的不重不漏,消息接受方的 TCP 棧會對分片的數據重新進行拼裝,DNS 等應用層協議可以直接使用處理好的完整數據。同時,當數據包足夠大的時候,TCP 三次握手帶來的額外開銷比例就會越來越小,與整個包的大小相比就會趨近於 0:

  • 當 DNS 數據包大小為 500 字節時,TCP 協議的額外開銷為 ~41.2%;
  • 當 DNS 數據包大小為 1100 字節時,TCP 協議的額外開銷為 ~20.7%;
  • 當 DNS 數據包大小為 2300 字節時,TCP 協議的額外開銷為 ~10.3%;
  • 當 DNS 數據包大小為 4800 字節時,TCP 協議的額外開銷為 ~5.0%;
為什麼 DNS 使用 UDP 協議?

所以,我們在 DNS 中存儲較多的內容時,TCP 三次握手以及協議頭帶來的額外開銷就不是關鍵因素了,不過我們 TCP 三次握手帶來的三次網絡傳輸耗時還是沒有辦法避免的,這也是我們在目前的場景下不得不接受的問題。

總結

很多人認為 DNS 使用了 UDP 協議來獲取域名對應的 IP 地址,這個觀點雖然沒錯,但是還是有一些片面,更加準確的說法其實是 DNS 查詢在剛設計時主要使用 UDP 協議進行通信,而 TCP 協議也是在 DNS 的演進和發展中被加入到規範的:

  1. DNS 在設計之初就在區域傳輸中引入了 TCP 協議,在查詢中使用 UDP 協議;
  2. 當 DNS 超過了 512 字節的限制,我們第一次在 DNS 協議中明確了『當 DNS 查詢被截斷時,應該使用 TCP 協議進行重試』這一規範;
  3. 隨後引入的 EDNS 機制允許我們使用 UDP 最多傳輸 4096 字節的數據,但是由於 MTU 的限制導致的數據分片以及丟失,使得這一特性不夠可靠;
  4. 在最近的幾年,我們重新規定了 DNS 應該同時支持 UDP 和 TCP 協議,TCP 協議也不再只是重試時的選擇;

這篇文章已經詳細介紹了 DNS 的歷史以及選擇不同協議時考慮的關鍵點,在這裡我們重新回顧一下 DNS 查詢選擇 UDP 或者 TCP 兩種不同協議時的主要原因:

  • UDP 協議
  • DNS 查詢的數據包較小、機制簡單;
  • UDP 協議的額外開銷小、有著更好的性能表現;
  • TCP 協議
  • DNS 查詢由於 DNSSEC 和 IPv6 的引入迅速膨脹,導致 DNS 響應經常超過 MTU 造成數據的分片和丟失,我們需要依靠更加可靠的 TCP 協議完成數據的傳輸;
  • 隨著 DNS 查詢中包含的數據不斷增加,TCP 協議頭以及三次握手帶來的額外開銷比例逐漸降低,不再是佔據總傳輸數據大小的主要部分;

無論是選擇 UDP 還是 TCP,最核心的矛盾就在於需要傳輸的數據包大小,如果數據包小到一定程度,UDP 協議絕對最佳的選擇,但是當數據包逐漸增大直到突破 512 字節以及 MTU 1500 字節的限制時,我們也只能選擇使用更可靠的 TCP 協議來傳輸 DNS 查詢和相應。到最後,我們還是來看一些比較開放的相關問題,有興趣的讀者可以仔細思考一下下面的問題:

  • 如何對使用 TCP 協議的 DNS 進行一些優化,減少一些額外開銷?
  • 我們現在已經可以使用 UDP/TCP/TLS/HTTPS 四種方式傳輸 DNS 數據,這些方式有什麼異同?是否還可以通過其他的協議實現 DNS 查詢?

如果對文章中的內容有疑問或者想要了解更多軟件工程上一些設計決策背後的原因,可以在博客下面留言,作者會及時回覆本文相關的疑問並選擇其中合適的主題作為後續的內容。

如果對本文 的內容有疑問,請在下面的評論系統中留言,謝謝。

原文鏈接:

Follow:


分享到:


相關文章: