我眼中的 Nginx(六):深刻 Nginx/Openresty 服務裏的 DNS 解析

張超:又拍雲系統開發高級工程師,負責又拍雲 CDN 平臺相關組件的更新及維護。Github ID: tokers,活躍於 OpenResty 社區和 Nginx 郵件列表等開源社區,專一於服務端技術的研究;曾爲 ngx_lua 貢獻源碼,在 Nginx、ngx_lua、CDN 性能優化、日誌優化方面有較爲深刻的研究。html

DNS 解析在 Nginx/OpenResty 的服務裏是不可分割的一個功能,本文主要來介紹下 Nginx 和 OpenResty 服務裏的一些不一樣的 DNS 解析方式以及它們之間的優缺點。nginx

配置解析階段

不少時候咱們會在 Nginx 配置文件裏配置上一些域名,好比配置咱們的上游服務器。緩存

upstream example.com {
    server foo.example.com;
}
複製代碼

對於這類域名,Nginx 會在配置解析階段就將其解析出來,接下來(請求處理過程)使用的都是當時解析獲得的 IP。Nginx 核心有一個函數 ngx_parse_url,負責對 url 格式進行分析,包括解析出主機名,端口號以及 URL path 等。針對 IPv4 的狀況,它會調用 ngx_parse_inet_url進行具體的解析任務,若是必要,最終它會調用到 ngx_inet_resolve_host進行域名解析,ngx_inet_resolve_host 大多狀況下會使用 getaddrinfo 進行解析,最終向 /etc/resolv.conf 下所配置的 DNS server 發起解析請求。性能優化

概括來講這個解析過程有兩個特色,一是使用了系統配置的 DNS server;二是解析過程是同步且阻塞的,所以這種解析方式僅在 Nginx 配置解析階段會被使用。另外這種解析方式的缺點就是隻解析一次,因此若是在 Nginx 運行過程當中域名解析發生了改變也是沒法感知到的,除非手動重啓 Nginx 服務。bash

運行時 DNS resolver

Nginx 核心提供了一套供運行時使用的 DNS 解析機制,它充分契合 Nginx 的事件模型,一樣是異步非阻塞的,而且提供了緩存機制。http、stream 和 mail 模塊分別提供了配置指令(好比 http 模塊提供的 resolver),供咱們配置相關 DNS server 地址等信息。服務器

下面這個簡單的反向代理配置,就會在進行代理前解析 www.upyun.com 這個域名。網絡

location / {
    set $myupstream www.upyun.com;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_pass http://${myupstream}/index.html;
}
複製代碼

注意若是直接在 proxy_pass 指令裏寫明須要代理的域名(即不使用變量的方式),那麼域名解析就會發生在配置解析階段了,即上面所講的過程。這其實也是一種實現動態 upstream 的方式。併發

這套運行時 DNS resolver 實際上是一個 DNS client 的角色,由它本身組織查詢報文併發送給目標 DNS 服務器,同時支持解析 IPv6 地址(從 1.5.8 開始),支持反向地址解析和 SRV 解析。它把對每一個域名的解析抽象爲一棵紅黑樹的節點,包括任何須要的信息。同時這棵紅黑樹也充當着緩存,查詢時會以域名做爲 key,若是對應緩存是新鮮的,即會複用緩存,而且會對解析獲得的地址順序進行必定的迴轉後再提供給上層使用。若是沒有緩存或者緩存過時,新的 DNS 請求會被構建而且發送。異步

固然,不少時候這套運行時的 DNS resolver 也不能徹底知足需求:socket

  1. 沒法配置主備 DNS 服務器地址,咱們在 resolver 指令裏配置的地址都會按順序被輪詢到。
  2. 沒法在 DNS 服務器故障或者網絡質量不佳的狀況下複用陳舊的緩存,這可能致使上層服務不可用。
  3. 每一個 Nginx worker 進程獨享解析緩存。

Cosocket

Cosocket 是 lua-nginx-module 提供的最強有力的接口(我的來看沒有之一)。它的 connect方法一樣支持傳入域名,以後會調用上面介紹的 Nginx 運行時 resolver 來解析對應域名,而後隨機挑選一個 IP 做爲本次鏈接的目標 IP 地址。因爲是使用的 Nginx 運行時 resolver ,若是 DNS resolver 沒法正常進行解析,則利用 Cosocket 構建的服務也都會受到影響。

lua-resty-dns

OpenResty 官方開源的 lua-resty-dns 是利用 Cosocket 實現的一套百分百非阻塞的 DNS resolver,它僅僅充當了一個 DNS 解析器,沒有任何其餘的附加功能,包括緩存。

前面介紹過 Nginx 運行時 DNS resolver 在不少時候是有諸多的不便的,而 lua-resty-dns 則給咱們留了不少的擴展空間:

  1. 提供主備 DNS 服務器地址 —— 結合像 lua-resty-checkups 這樣的工具能夠很容易的維護主備 DNS 服務器地址,包括進行心跳檢測。
  2. 進程間共享緩存 —— 結合使用 lua-nginx-module 提供的共享內存字典,能夠很是容易地管理緩存。
  3. 故障時使用陳舊緩存 —— 在 lua-resty-dns 報告異常時再次使用緩存便可(只要緩存拷貝沒有被強行淘汰)。

目前 OpenResty 生態圈已經有一些基於lua-resty-dns 實現的增強版實現,好比 Kong 的 lua-resty-dns-client。

總的來講,每一種 DNS 解析方式都有它適用的場景,也有它本身的不足,沒有最好的,只有最合適的。在程序設計的時候,須要找到最合適本身業務場景的方式,才能最大程度地保障服務的可用性和可靠性,避免帶來一些災難。

推薦閱讀:

我眼中的 Nginx(五):Nginx - 子請求設計之道​ 我眼中的 Nginx(四):是什麼讓你的 Nginx 服務退出這麼慢?​ 我眼中的 Nginx(三):Nginx 變量和變量插值 我眼中的 Nginx(二):HTTP/2 dynamic table size update​ 我眼中的 Nginx(一):Nginx 和位運算​

相關文章
相關標籤/搜索