1、背景
在計算機(jī)領(lǐng)域,涉及性能優(yōu)化動作時首先應(yīng)被考慮的原則之一便是使用緩存,合理的數(shù)據(jù)緩存機(jī)制能夠帶來以下收益:
1)縮短數(shù)據(jù)獲取路徑,熱點數(shù)據(jù)就近緩存以便后續(xù)快速讀取,從而明顯提升處理效率;
2)降低數(shù)據(jù)遠(yuǎn)程獲取頻次,緩解后端數(shù)據(jù)服務(wù)壓力、減少前端和后端之間的網(wǎng)絡(luò)帶寬成本;
從 CPU 硬件的多級緩存設(shè)計,到瀏覽器快速展示頁面,再到大行其道的 CDN、云存儲網(wǎng)關(guān)等商業(yè)產(chǎn)品,處處應(yīng)用了緩存理念。
在公網(wǎng)領(lǐng)域,如操作系統(tǒng)、瀏覽器和移動端 APP 等成熟產(chǎn)品所具備的緩存機(jī)制,極大的消解了網(wǎng)絡(luò)提供商如電信移動聯(lián)通、內(nèi)容提供商如各大門戶平臺和 CDN 廠商直面的服務(wù)壓力,運(yùn)營商的 DNS 才能從容面對每秒億萬級的 DNS 解析,網(wǎng)絡(luò)設(shè)備集群才能輕松承擔(dān)每秒 Tbit 級的互聯(lián)網(wǎng)帶寬,CDN 平臺才能快速處理每秒億萬次的請求。
面對公司目前龐大且仍在不斷增長的的域名接入規(guī)模,筆者所在團(tuán)隊在不斷優(yōu)化集群架構(gòu)、提升 DNS 軟件性能的同時,也迫切需要推動各類客戶端環(huán)境進(jìn)行域名解析請求機(jī)制的優(yōu)化,因此,特組織團(tuán)隊成員調(diào)研、編寫了這篇指南文章,以期為公司、客戶及合作方的前端開發(fā)運(yùn)維人員給出合理建議,優(yōu)化 DNS 整體請求流程,為業(yè)務(wù)增效。
本文主要圍繞不同業(yè)務(wù)和開發(fā)語言背景下,客戶端本地如何實現(xiàn) DNS 解析記錄緩存進(jìn)行探討,同時基于筆者所在團(tuán)隊對 DNS 本身及公司網(wǎng)絡(luò)環(huán)境的掌握,給出一些其他措施,最終致力于客戶端一側(cè)的 DNS 解析請求規(guī)范化。
2、名詞解釋
1. 客戶端
本文所述客戶端,泛指所有主動發(fā)起網(wǎng)絡(luò)請求的對象,包括但不限于服務(wù)器、PC、移動終端、操作系統(tǒng)、命令行工具、腳本、服務(wù)軟件、用戶 APP 等。
2. DNS
Domain Name System(Server/Service),域名系統(tǒng)(服務(wù)器/服務(wù)),可理解為一種類數(shù)據(jù)庫服務(wù);
客戶端同服務(wù)端進(jìn)行網(wǎng)絡(luò)通信,是靠 IP 地址識別對方;而作為客戶端的使用者,人類很難記住大量 IP 地址,所以發(fā)明了易于記憶的域名如 www.jd.com,將域名和 IP 地址的映射關(guān)系,存儲到 DNS 可供客戶端查詢;
客戶端只有通過向 DNS 發(fā)起域名解析請求從而獲取到服務(wù)端的 IP 地址后,才能向 IP 地址發(fā)起網(wǎng)絡(luò)通信請求,真正獲取到域名所承載的服務(wù)或內(nèi)容。
參考:域名系統(tǒng) ?域名解析流程?
3. LDNS
Local DNS,本地域名服務(wù)器;公網(wǎng)接入環(huán)境通常由所在網(wǎng)絡(luò)供應(yīng)商自動分配(供應(yīng)商有控制權(quán),甚至可作 DNS 劫持,即篡改解析域名得到的 IP),內(nèi)網(wǎng)環(huán)境由 IT 部門設(shè)置自動分配;
通常 Unix、類Unix、MacOS系統(tǒng)可通過 /etc/resolv.conf 查看自己的 LDNS,在 nameserver 后聲明,該文件亦支持用戶自助編輯修改,從而指定 LDNS,如公網(wǎng)常見的公共 DNS 如谷歌 DNS、114DNS 等;純內(nèi)網(wǎng)環(huán)境通常不建議未咨詢IT部門的情況下擅自修改,可能導(dǎo)致服務(wù)不可用;可參考 man resolv.conf 指令結(jié)果。
當(dāng)域名解析出現(xiàn)異常時,同樣應(yīng)考慮 LDNS 服務(wù)異常或發(fā)生解析劫持情況的可能。
參考:windows系統(tǒng)修改TCP/IP設(shè)置(含DNS);
4. hosts
DNS 系統(tǒng)可以動態(tài)的提供域名和IP的映射關(guān)系,普遍存在于各類操作系統(tǒng)的hosts文件則是域名和IP映射關(guān)系的靜態(tài)記錄文件,且通常 hosts 記錄優(yōu)先于 DNS 解析,即本地?zé)o緩存或緩存未命中時,則優(yōu)先通過 hosts 查詢對應(yīng)域名記錄,若 hosts 無相關(guān)映射,則繼續(xù)發(fā)起 DNS 請求。關(guān)于 Linux 環(huán)境下此邏輯的控制,請參考下文 C/C++ 語言 DNS 緩存介紹部分。
所以在實際工作中,常利用上述默認(rèn)特性,將特定域名和特定 IP 映射關(guān)系寫到 hosts 文件中(俗稱“固定 hosts”),用于繞開 DNS 解析過程,對目標(biāo) IP 作針對性訪問(其效果與 curl 的-x選項,或 wget 的 -e 指定 proxy 選項,異曲同工);
5. TTL
Time-To-Live,生存時間值,此概念在多領(lǐng)域適用且可能有不同意義。
本文涉及到 TTL 描述均針對數(shù)據(jù)緩存而言,可直白理解為已緩存數(shù)據(jù)的“有效期”,從數(shù)據(jù)被緩存開始計,在緩存中存在時長超過 TTL 規(guī)定時長的數(shù)據(jù)被視為過期數(shù)據(jù),數(shù)據(jù)被再次調(diào)用時會立刻同權(quán)威數(shù)據(jù)源進(jìn)行有效性確認(rèn)或重新獲取。
因緩存機(jī)制通常是被動觸發(fā)和更新,故在客戶端的緩存有效期內(nèi),后端原始權(quán)威數(shù)據(jù)若發(fā)生變更,客戶端不會感知,表現(xiàn)為業(yè)務(wù)上一定程度的數(shù)據(jù)更新延遲、緩存數(shù)據(jù)與權(quán)威數(shù)據(jù)短時不一致。
對于客戶端側(cè) DNS 記錄的緩存 TTL,我們建議值為 60s;同時如果是低敏感度業(yè)務(wù)比如測試、或域名解析調(diào)整不頻繁的業(yè)務(wù),可適當(dāng)延長,甚至達(dá)到小時或天級別;
3、DNS解析優(yōu)化建議
3.1 各語言網(wǎng)絡(luò)庫對 DNS 緩存的支持調(diào)研
以下調(diào)研結(jié)果,推薦開發(fā)人員參考,以實現(xiàn)自研客戶端 DNS 緩存。各開發(fā)語言對 DNS 緩存支持可能不一樣,在此逐個分析一下。
C/C++ 語言
(1) glibc 的 getaddrinfo 函數(shù)
Linux環(huán)境下的 glibc 庫提供兩個域名解析的函數(shù):gethostbyname 函數(shù)和 getaddrinfo 函數(shù),gethostbyname 是曾經(jīng)常用的函數(shù),但是隨著向 IPv6 和線程化編程模型的轉(zhuǎn)移,getaddrinfo 顯得更有用,因為它既解析 IPv6 地址,又符合線程安全,推薦使用 getaddrinfo 函數(shù)。
函數(shù)原型:
int getaddrinfo( const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
getaddrinfo 函數(shù)是比較底層的基礎(chǔ)庫函數(shù),很多開發(fā)語言的域名解析函數(shù)都依賴這個函數(shù),因此我們在此介紹一下這個函數(shù)的處理邏輯。通過 strace 命令跟蹤這個函數(shù)系統(tǒng)調(diào)用。
1)查找 nscd 緩存(nscd 介紹見后文)
我們在 linux 環(huán)境下通過 strace 命令可以看到如下的系統(tǒng)調(diào)用
//連接nscd socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3 connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory) close(3)
通過 unix socket 接口"/var/run/nscd/socket"連接nscd服務(wù)查詢DNS緩存。
2)查詢 /etc/hosts 文件
如果nscd服務(wù)未啟動或緩存未命中,繼續(xù)查詢hosts文件,我們應(yīng)該可以看到如下的系統(tǒng)調(diào)用
//讀取 hosts 文件 open("/etc/host.conf", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=9, ...}) = 0 ... open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 3 fcntl(3, F_GETFD) = 0x1 (flags FD_CLOEXEC) fstat(3, {st_mode=S_IFREG|0644, st_size=178, ...}) = 0
3)查詢 DNS 服務(wù)
從 /etc/resolv.conf 配置中查詢到 DNS 服務(wù)器(nameserver)的IP地址,然后做 DNS 查詢獲取解析結(jié)果。我們可以看到如下系統(tǒng)調(diào)用
//獲取 resolv.conf 中 DNS 服務(wù) IP open("/etc/resolv.conf", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=25, ...}) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fef2abee000 read(3, "nameserver 114.114.114.114 ", 4096) = 25 ... //連到 DNS 服務(wù),開始 DNS 查詢 connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("114.114.114.114")}, 16) = 0 poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}])
而關(guān)于客戶端是優(yōu)先查找 /etc/hosts 文件,還是優(yōu)先從 /etc/resolv.conf 中獲取 DNS 服務(wù)器作查詢解析,是由 /etc/nsswitch.conf 控制:
#/etc/nsswitch.conf 部分配置 ... #hosts: db files nisplus nis dns hosts: files dns ...
實際通過 strace 命令可以看到,系統(tǒng)調(diào)用 nscd socket 之后,讀取 /etc/resolv.conf 之前,會讀取該文件
newfstatat(AT_FDCWD, "/etc/nsswitch.conf", {st_mode=S_IFREG|0644, st_size=510, ...}, 0) = 0 ... openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
4)驗證
#include#include #include #include #include #include #include int gethostaddr(char * name); int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "%s $host", argv[0]); return -1; } int i = 0; for(i = 0; i < 5; i++) { int ret = -1; ret = gethostaddr(argv[1]); if (ret < 0) { fprintf(stderr, "%s $host", argv[0]); return -1; } //sleep(5); } return 0; } int gethostaddr(char* name) { struct addrinfo hints; struct addrinfo *result; struct addrinfo *curr; int ret = -1; char ipstr[INET_ADDRSTRLEN]; struct sockaddr_in *ipv4; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(name, NULL, &hints, &result); if (ret != 0) { fprintf(stderr, "getaddrinfo: %s ", gai_strerror(ret)); return ret; } for (curr = result; curr != NULL; curr = curr->ai_next) { ipv4 = (struct sockaddr_in *)curr->ai_addr; inet_ntop(curr->ai_family, &ipv4->sin_addr, ipstr, INET_ADDRSTRLEN); printf("ipaddr:%s ", ipstr); } freeaddrinfo(result); return 0; }
綜上分析,getaddrinfo 函數(shù)結(jié)合 nscd ,是可以實現(xiàn) DNS 緩存的。
(2)libcurl 庫的域名解析函數(shù)
libcurl 庫是 c/c++ 語言下,客戶端比較常用的網(wǎng)絡(luò)傳輸庫,curl 命令就是基于這個庫實現(xiàn)。這個庫也是調(diào)用 getaddrinfo 庫函數(shù)實現(xiàn) DNS 域名解析,也是支持 nscd DNS 緩存的。
int Curl_getaddrinfo_ex(const char *nodename, const char *servname, const struct addrinfo *hints, Curl_addrinfo **result) { ... error = getaddrinfo(nodename, servname, hints, &aihead); if(error) return error; ... }
Java 語言是很多公司業(yè)務(wù)系統(tǒng)開發(fā)的主要語言,通過編寫簡單的 HTTP 客戶端程序測試驗證 Java 的網(wǎng)絡(luò)庫是否支持 DNS 緩存。測試驗證了 Java 標(biāo)準(zhǔn)庫中 HttpURLConnection 和 Apache httpcomponents-client 這兩個組件。
(1)Java 標(biāo)準(zhǔn)庫 HttpURLConnection
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class HttpUrlConnectionDemo { public static void main(String[] args) throws Exception { String urlString = "http://example.my.com/"; int num = 0; while (num < 5) { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.flush(); os.close(); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } System.out.println("rsp:" + sb.toString()); } else { System.out.println("rsp code:" + conn.getResponseCode()); } num++; } } }
測試結(jié)果顯示 Java 標(biāo)準(zhǔn)庫 HttpURLConnection 是支持 DNS 緩存,5 次請求中只有一次 DNS 請求。
(2)Apache httpcomponents-client
import java.util.ArrayList; import java.util.List; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.message.BasicNameValuePair; public class QuickStart { public static void main(final String[] args) throws Exception { int num = 0; while (num < 5) { try (final CloseableHttpClient httpclient = HttpClients.createDefault()) { final HttpGet httpGet = new HttpGet("http://example.my.com/"); try (final CloseableHttpResponse response1 = httpclient.execute(httpGet)) { System.out.println(response1.getCode() + " " + response1.getReasonPhrase()); final HttpEntity entity1 = response1.getEntity(); EntityUtils.consume(entity1); } } num++; } } }
測試結(jié)果顯示 Apache httpcomponents-client 支持 DNS 緩存,5 次請求中只有一次 DNS 請求。
從測試中發(fā)現(xiàn) Java 的虛擬機(jī)實現(xiàn)一套 DNS 緩存,即實現(xiàn)在 java.net.InetAddress 的一個簡單的 DNS 緩存機(jī)制,默認(rèn)為緩存 30 秒,可以通過 networkaddress.cache.ttl 修改默認(rèn)值,緩存范圍為 JVM 虛擬機(jī)進(jìn)程,也就是說同一個 JVM 進(jìn)程中,30秒內(nèi)一個域名只會請求DNS服務(wù)器一次。同時 Java 也是支持 nscd 的 DNS 緩存,估計底層調(diào)用 getaddrinfo 函數(shù),并且 nscd 的緩存級別比 Java 虛擬機(jī)的 DNS 緩存高。
# 默認(rèn)緩存 ttl 在 jre/lib/security/java.security 修改,其中 0 是不緩存,-1 是永久緩存 networkaddress.cache.ttl=10 # 這個參數(shù) sun.net.inetaddr.ttl 是以前默認(rèn)值,目前已經(jīng)被 networkaddress.cache.ttl 取代
Go
隨著云原生技術(shù)的發(fā)展,Go 語言逐漸成為云原生的第一語言,很有必要驗證一下 Go 的標(biāo)準(zhǔn)庫是否支持 DNS 緩存。通過我們測試驗證發(fā)現(xiàn) Go 的標(biāo)準(zhǔn)庫 net.http 是不支持 DNS 緩存,也是不支持 nscd 緩存,應(yīng)該是沒有調(diào)用 glibc 的庫函數(shù),也沒有實現(xiàn)類似 getaddrinfo 函數(shù)的功能。這個跟 Go語言的自舉有關(guān)系,Go 從 1.5 開始就基本全部由 Go(.go) 和匯編 (.s) 文件寫成的,以前版本的 C(.c) 文件被全部重寫。不過有一些第三方 Go 版本 DNS 緩存庫,可以自己在應(yīng)用層實現(xiàn),還可以使用 fasthttp 庫的 httpclient。
(1)標(biāo)準(zhǔn)庫net.http
package main import ( "flag" "fmt" "io/ioutil" "net/http" "time" ) var httpUrl string func main() { flag.StringVar(&httpUrl, "url", "", "url") flag.Parse() getUrl := fmt.Sprintf("http://%s/", httpUrl) fmt.Printf("url: %s ", getUrl) for i := 0; i < 5; i++ { _, buf, err := httpGet(getUrl) if err != nil { fmt.Printf("err: %v ", err) return } fmt.Printf("resp: %s ", string(buf)) time.Sleep(10 * time.Second) # 等待10s發(fā)起另一個請求 } } func httpGet(url string) (int, []byte, error) { client := createHTTPCli() resp, err := client.Get(url) if err != nil { return -1, nil, fmt.Errorf("%s err [%v]", url, err) } defer resp.Body.Close() buf, err := ioutil.ReadAll(resp.Body) if err != nil { return resp.StatusCode, buf, err } return resp.StatusCode, buf, nil } func createHTTPCli() *http.Client { readWriteTimeout := time.Duration(30) * time.Second tr := &http.Transport{ DisableKeepAlives: true, //設(shè)置短連接 IdleConnTimeout: readWriteTimeout, } client := &http.Client{ Timeout: readWriteTimeout, Transport: tr, } return client }
從測試結(jié)果來看,net.http 每次都去 DNS 查詢,不支持 DNS 緩存。
(2)fasthttp 庫
fasthttp 庫是 Go 版本高性能 HTTP 庫,通過極致的性能優(yōu)化,性能是標(biāo)準(zhǔn)庫 net.http 的 10 倍,其中一項優(yōu)化就是支持 DNS 緩存,我們可以從其源碼看到
//主要在fasthttp/tcpdialer.go中 type TCPDialer struct { ... // This may be used to override DNS resolving policy, like this: // var dialer = &fasthttp.TCPDialer{ // Resolver: &net.Resolver{ // PreferGo: true, // StrictErrors: false, // Dial: func (ctx context.Context, network, address string) (net.Conn, error) { // d := net.Dialer{} // return d.DialContext(ctx, "udp", "8.8.8.8:53") // }, // }, // } Resolver Resolver // DNSCacheDuration may be used to override the default DNS cache duration (DefaultDNSCacheDuration) DNSCacheDuration time.Duration ... }
可以參考如下方法使用 fasthttp client 端
func main() { // You may read the timeouts from some config readTimeout, _ := time.ParseDuration("500ms") writeTimeout, _ := time.ParseDuration("500ms") maxIdleConnDuration, _ := time.ParseDuration("1h") client = &fasthttp.Client{ ReadTimeout: readTimeout, WriteTimeout: writeTimeout, MaxIdleConnDuration: maxIdleConnDuration, NoDefaultUserAgentHeader: true, // Don't send: User-Agent: fasthttp DisableHeaderNamesNormalizing: true, // If you set the case on your headers correctly you can enable this DisablePathNormalizing: true, // increase DNS cache time to an hour instead of default minute Dial: (&fasthttp.TCPDialer{ Concurrency: 4096, DNSCacheDuration: time.Hour, }).Dial, } sendGetRequest() sendPostRequest() }
(3)第三方DNS緩存庫
這個是 github 中的一個 Go 版本 DNS 緩存庫?
可以參考如下代碼,在HTTP庫中支持DNS緩存
r := &dnscache.Resolver{} t := &http.Transport{ DialContext: func(ctx context.Context, network string, addr string) (conn net.Conn, err error) { host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err } ips, err := r.LookupHost(ctx, host) if err != nil { return nil, err } for _, ip := range ips { var dialer net.Dialer conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(ip, port)) if err == nil { break } } return }, }
(1)requests 庫
#!/bin/python import requests url = 'http://example.my.com/' num = 0 while num < 5: headers={"Connection":"close"} # 開啟短連接 r = requests.get(url,headers = headers) print(r.text) num +=1 (2)httplib2 庫 #!/usr/bin/env python import httplib2 http = httplib2.Http() url = 'http://example.my.com/' num = 0 while num < 5: loginHeaders={ 'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.0 Chrome/30.0.1599.101 Safari/537.36', 'Connection': 'close' # 開啟短連接 } response, content = http.request(url, 'GET', headers=loginHeaders) print(response) print(content) num +=1
(3)urllib2 庫
#!/bin/python import urllib2 import cookielib httpHandler = urllib2.HTTPHandler(debuglevel=1) httpsHandler = urllib2.HTTPSHandler(debuglevel=1) opener = urllib2.build_opener(httpHandler, httpsHandler) urllib2.install_opener(opener) loginHeaders={ 'User-Agent': 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.0 Chrome/30.0.1599.101 Safari/537.36', 'Connection': 'close' # 開啟短連接 } num = 0 while num < 5: request=urllib2.Request('http://example.my.com/',headers=loginHeaders) response = urllib2.urlopen(request) page='' page= response.read() print response.info() print page num +=1
Python 測試三種庫都是支持 nscd 的 DNS 緩存的(推測底層也是調(diào)用 getaddrinfo 函數(shù)),以上測試時使用 HTTP 短連接,都在 python2 環(huán)境測試。
總結(jié)
針對 HTTP 客戶端來說,可以優(yōu)先開啟 HTTP 的 keep-alive 模式,可以復(fù)用 TCP 連接,這樣可以減少 TCP 握手耗時和重復(fù)請求域名解析,然后再開啟 nscd 緩存,除了 Go 外,C/C++、Java、Python 都可支持 DNS 緩存,減少 DNS查詢耗時。
這里只分析了常用 C/C++、Java、Go、Python 語言,歡迎熟悉其他語言的小伙伴補(bǔ)充。
3.2 Unix/類 Unix 系統(tǒng)常用 dns 緩存服務(wù):
在由于某些特殊原因,自研或非自研客戶端本身無法提供 DNS 緩存支持的情況下,建議管理人員在其所在系統(tǒng)環(huán)境中部署DNS緩存程序;
現(xiàn)介紹 Unix/類 Unix 系統(tǒng)適用的幾款常見輕量級 DNS 緩存程序。而多數(shù)桌面操作系統(tǒng)如 Windows、MacOS 和幾乎所有 Web 瀏覽器均自帶 DNS 緩存功能,本文不再贅述。
P.S. DNS 緩存服務(wù)請務(wù)必確保隨系統(tǒng)開機(jī)啟動;
nscd
name service cache daemon 即裝即用,通常為 linux 系統(tǒng)默認(rèn)安裝,相關(guān)介紹可參考其 manpage:man nscd;man nscd.conf
(1)安裝方法:通過系統(tǒng)自帶軟件包管理程序安裝,如 yum install nscd (2)緩存管理(清除):
service nscd restart 重啟服務(wù)清除所有緩存;
nscd -i hosts 清除 hosts 表中的域名緩存(hosts 為域名緩存使用的 table 名稱,nscd 有多個緩存 table,可參考程序相關(guān) manpage)
dnsmasq
較為輕量,可選擇其作為 nscd 替代,通常需單獨安裝
(1)安裝方法:通過系統(tǒng)自帶軟件包管理程序安裝,如 yum install dnsmasq (2)核心文件介紹(基于 Dnsmasq version 2.86,較低版本略有差異,請參考對應(yīng)版本文檔如 manpage 等) (3)/etc/default/dnsmasq 提供六個變量定義以支持六種控制類功能 (4)/etc/dnsmasq.d/ 此目錄含 README 文件,可參考;目錄內(nèi)可以存放自定義配置文件
(5)/etc/dnsmasq.conf 主配置文件,如僅配置 dnsmasq 作為緩存程序,可參考以下配置
listen-address=127.0.0.1 #程序監(jiān)聽地址,務(wù)必指定本機(jī)內(nèi)網(wǎng)或回環(huán)地址,避免暴露到公網(wǎng)環(huán)境 port=53 #監(jiān)聽端口 resolv-file=/etc/dnsmasq.d/resolv.conf #配置dnsmasq向自定義文件內(nèi)的 nameserver 轉(zhuǎn)發(fā) dns 解析請求 cache-size=150 #緩存記錄條數(shù),默認(rèn) 150 條,可按需調(diào)整、適當(dāng)增大 no-negcache #不緩存解析失敗的記錄,主要是 NXDOMAIN,即域名不存在 log-queries=extra #開啟日志記錄,指定“=extra”則記錄更詳細(xì)信息,可僅在問題排查時開啟,平時關(guān)閉 log-facility=/var/log/dnsmasq.log #指定日志文件 #同時需要將本機(jī) /etc/resolv.conf 第一個 nameserver 指定為上述監(jiān)聽地址,這樣本機(jī)系統(tǒng)的 dns 查詢請求才會通過 dnsmasq 代為轉(zhuǎn)發(fā)并緩存響應(yīng)結(jié)果。 #另 /etc/resolv.conf 務(wù)必額外配置 2 個 nameserver,以便 dnsmasq 服務(wù)異常時支持系統(tǒng)自動重試,注意 resolv.conf 僅讀取前 3 個 nameserver(6)緩存管理(清除):
kill -s HUP `pidof dnsmasq` 推薦方式,無需重啟服務(wù)
kill -s TERM `pidof dnsmasq` 或 service dnsmasq stop
service dnsmasq force-reload 或 service dnsmasq restart
(7)官方文檔:https://thekelleys.org.uk/dnsmasq/doc.html?
3.3 純內(nèi)網(wǎng)業(yè)務(wù)取消查詢域名的AAAA記錄的請求
以 linux 操作系統(tǒng)為例,常用的網(wǎng)絡(luò)請求命令行工具常常通過調(diào)用 getaddrinfo() 完成域名解析過程,如 ping、telnet、curl、wget 等,但其可能出于通用性的考慮,均被設(shè)計為對同一個域名每次解析會發(fā)起兩個請求,分別查詢域名 A 記錄(即 IPV4 地址)和 AAAA 記錄(即 IPV6 地址)。
因目前大部分公司的內(nèi)網(wǎng)環(huán)境及云上內(nèi)網(wǎng)環(huán)境還未使用 ipv6 網(wǎng)絡(luò),故通常 DNS 系統(tǒng)不為內(nèi)網(wǎng)域名添加 AAAA 記錄,徒勞請求域名的 AAAA 記錄會造成前端應(yīng)用和后端 DNS 服務(wù)不必要的資源開銷。因此,僅需請求內(nèi)網(wǎng)域名的業(yè)務(wù),如決定自研客戶端,建議開發(fā)人員視實際情況,可將其設(shè)計為僅請求內(nèi)網(wǎng)域名 A 記錄,尤其當(dāng)因故無法實施本地緩存機(jī)制時。
3.4規(guī)范域名處理邏輯
客戶端需嚴(yán)格規(guī)范域名/主機(jī)名的處理邏輯,避免產(chǎn)生大量對不存在域名的解析請求(確保域名從權(quán)威渠道獲取,避免故意或意外使用隨機(jī)構(gòu)造的域名、主機(jī)名),因此類請求的返回結(jié)果(NXDOMAIN)通常不被緩存或緩存時長較短,且會觸發(fā)客戶端重試,對后端 DNS 系統(tǒng)造成一定影響。
審核編輯:湯梓紅
-
cpu
+關(guān)注
關(guān)注
68文章
10863瀏覽量
211797 -
Linux
+關(guān)注
關(guān)注
87文章
11304瀏覽量
209536 -
緩存
+關(guān)注
關(guān)注
1文章
240瀏覽量
26680 -
DNS
+關(guān)注
關(guān)注
0文章
218瀏覽量
19844 -
C++
+關(guān)注
關(guān)注
22文章
2108瀏覽量
73657
原文標(biāo)題:各開發(fā)語言DNS緩存配置建議
文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論