Author: Zhai Helong

In the computer field, one of the first principles that should be considered when it comes to performance optimization actions is to use caching. A reasonable data caching mechanism can bring the following benefits:

The data acquisition path is shortened, and hot data is cached nearby for subsequent fast reading, thereby significantly improving processing efficiency;

Reduce the frequency of remote data acquisition, relieve the pressure on back-end data services, and reduce the cost of network bandwidth between the front-end and back-end;

From the multi-level cache design of CPU hardware, to the quick display of pages in browsers, to the popular CDN, cloud storage gateway and other commercial products, the concept of caching is applied everywhere.

In the field of the public network, the caching mechanisms of mature products such as operating systems, browsers, and mobile APPs have greatly eliminated the challenges faced by network providers such as China Telecom and Mobile Unicom, and content providers such as major portal platforms and CDN manufacturers. Service pressure, the DNS of the operator can calmly face the DNS resolution of hundreds of millions per second, the network equipment cluster can easily bear the Internet bandwidth of Tbit level per second, and the CDN platform can quickly process hundreds of millions of requests per second.

Faced with the company’s current large and growing domain name access scale, the author’s team is constantly optimizing the cluster structure and improving the performance of DNS software, but also urgently needs to promote the optimization of domain name resolution request mechanisms in various client environments. Therefore, team members are specially organized to research and write this guide article, in order to give reasonable suggestions for the front-end development and maintenance personnel of the company, customers and partners, optimize the overall DNS request process, and increase business efficiency.

This article mainly discusses how to implement DNS resolution record cache locally on the client side under the background of different business and development languages. DNS resolution request normalization on the side.

1. Client

Clients mentioned in this article generally refer to all objects that actively initiate network requests, including but not limited to servers, PCs, mobile terminals, operating systems, command line tools, scripts, service software, user APPs, etc.

2. DNS

Domain Name System (Server/Service), domain name system (server/service), can be understood as a kind of database service;

The network communication between the client and the server is based on the IP address to identify each other; as the user of the client, it is difficult for human beings to remember a large number of IP addresses, so an easy-to-remember domain name such as was invented, and the domain name and IP The address mapping relationship is stored in DNS for client query;

Only after the client obtains the IP address of the server by initiating a domain name resolution request to the DNS, can it initiate a network communication request to the IP address and actually obtain the service or content carried by the domain name.

refer to:domain name system Domain name resolution process


Local DNS, local domain name server; the public network access environment is usually automatically assigned by the network provider (the provider has control, and even DNS hijacking, that is, tampering with the IP obtained by resolving the domain name), and the internal network environment is automatically set by the IT department. distribute;

Usually, Unix, Unix-like, and MacOS systems can view their own LDNS through /etc/resolv.conf, which is declared after the nameserver. This file also supports self-editing and modification by users to specify LDNS, such as common public DNS on the public network, such as Google DNS, 114DNS, etc.; in a pure intranet environment, it is generally not recommended to modify without consulting the IT department, which may cause the service to be unavailable; refer to man resolv.conf command result.

When domain name resolution is abnormal, the possibility of LDNS service abnormality or resolution hijacking should also be considered.

refer to:Windows system modify TCP/IP settings (including DNS);

4. hosts

The DNS system can dynamically provide the mapping relationship between domain names and IPs. The hosts file commonly found in various operating systems is a static record file of the mapping relationship between domain names and IPs.Usually hosts records are prioritized over DNS resolution, that is, when there is no local cache or a cache miss, the corresponding domain name records will be queried through hosts first, and if there is no relevant mapping for hosts, DNS requests will continue to be initiated. For the control of this logic in the Linux environment, please refer to the C/C++ language DNS cache introduction section below.

Therefore, in actual work, the above-mentioned default features are often used to write the mapping relationship between a specific domain name and a specific IP into the hosts file (commonly known as “fixed hosts”), which is used to bypass the DNS resolution process and perform targeted access to the target IP (other The effect is similar to curl’s -x option, or wget’s -e to specify the proxy option);

5. TTL

Time-To-Live, time-to-live value, this concept is applicable in many fields and may have different meanings.

The description of TTL in this article is all about data caching, which can be directly understood as the “validity period” of cached data, starting from the time when the data is cached, and the data that exists in the cache for longer than the specified time of TTL is regarded as expired data. When it is called again, it will immediately confirm the validity with the authoritative data source or obtain it again.

Because the cache mechanism is usually triggered and updated passively, within the valid period of the client’s cache, if the original authoritative data on the backend changes, the client will not be aware of it. inconsistent.

For the cache TTL of DNS records on the client side, we recommend a value of 60s; at the same time, if it is a low-sensitivity business such as testing, or business with infrequent domain name resolution adjustments, it can be extended appropriately, even to the hour or day level;

1. Research on the support of DNS cache in various language network libraries

The following research results are recommended for developers to implement self-developed client-side DNS caching. Each development language may have different support for DNS cache, so let’s analyze them one by one.

C/C++ language

(1) The getaddrinfo function of glibc

The glibc library in the Linux environment provides two domain name resolution functions:gethostbyname function and getaddrinfo function,gethostbyname used to be a commonly used function, but with the shift to IPv6 and threaded programming models, getaddrinfo is more useful because it resolves IPv6 addresses and is thread-safe. It is recommended to use getaddrinfo function.

Function prototype:

int getaddrinfo( const char *node, 
                 const char *service,
                 const struct addrinfo *hints,
                 struct addrinfo **res);

The getaddrinfo function is a relatively low-level basic library function. The domain name resolution functions of many development languages ​​​​depend on this function, so we will introduce the processing logic of this function here. This function system call is traced by the strace command.

1) Find the nscd cache (see the introduction of nscd later)

We can see the following system calls through the strace command in the Linux environment

connect(3, {sa_family=AF_LOCAL, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)

Connect to the nscd service through the unix socket interface “/var/run/nscd/socket” to query the DNS cache.

2) Query /etc/hosts file

If the nscd service is not started or the cache misses, continue to query the hosts file, we should see the following system calls

//读取 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) Query DNS service

Query the IP address of the DNS server (nameserver) from the /etc/resolv.conf configuration, and then do a DNS query to obtain the resolution result.We can see the following system calls

//获取 resolv.conf 中 DNS 服务 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\n\n", 4096) = 25
//连到 DNS 服务,开始 DNS 查询
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("")}, 16) = 0
poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}])

As for whether the client first searches for the /etc/hosts file or obtains the DNS server from /etc/resolv.conf for query resolution, it is controlled by /etc/nsswitch.conf:

#/etc/nsswitch.conf 部分配置
#hosts:     db files nisplus nis dns
hosts:      files dns

You can actually see through the strace command that after the system calls nscd socket and before reading /etc/resolv.conf, the file will be read

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) Verify

#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

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;

    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\n", 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\n", ipstr);

    return 0;

Based on the above analysis, getaddrinfo function combined with nscd can realize DNS cache.

(2) Domain name resolution function of libcurl library

The libcurl library is a network transmission library commonly used by clients in the c/c++ language, and the curl command is implemented based on this library. This library also calls the getaddrinfo library function to implement DNS domain name resolution, and also supports nscd DNS cache.

Curl_getaddrinfo_ex(const char *nodename,
                    const char *servname,
                    const struct addrinfo *hints,
                    Curl_addrinfo **result)
    error = getaddrinfo(nodename, servname, hints, &aihead);
        return error;


The Java language is the main language for business system development in many companies. By writing a simple HTTP client program, it is tested to verify whether the Java network library supports DNS caching. The tests validate the two components of the Java standard library, HttpURLConnection and Apache httpcomponents-client.

(1) Java standard library HttpURLConnection


public class HttpUrlConnectionDemo {

    public static void main(String[] args) throws Exception {
        String urlString = "";

        int num = 0;
        while (num < 5) {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            OutputStream os = conn.getOutputStream();

            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) {
                System.out.println("rsp:" + sb.toString());
            } else {
                System.out.println("rsp code:" + conn.getResponseCode());

The test results show that the Java standard library HttpURLConnection supports DNS caching, and there is only one DNS request in 5 requests.

(2) Apache http components-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.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("");
                try (final CloseableHttpResponse response1 = httpclient.execute(httpGet)) {
                    System.out.println(response1.getCode() + " " + response1.getReasonPhrase());
                    final HttpEntity entity1 = response1.getEntity();

The test results show that Apache httpcomponents-client supports DNS caching, and there is only one DNS request out of 5 requests.

From the test, it is found that the Java virtual machine implements a set of DNS cache, that is, a simple DNS cache mechanism implemented in The default cache is 30 seconds. The default value can be modified through networkaddress.cache.ttl. The cache range is JVM virtual machine process, that is to say, in the same JVM process, a domain name will only request the DNS server once within 30 seconds. At the same time, Java also supports the DNS cache of nscd. It is estimated that the bottom layer calls the getaddrinfo function, and the cache level of nscd is higher than the DNS cache of the Java virtual machine.

# 默认缓存 ttl 在 jre/lib/security/ 修改,其中 0 是不缓存,-1 是永久缓存

# 这个参数 是以前默认值,目前已经被 networkaddress.cache.ttl 取代


With the development of cloud native technology, the Go language has gradually become the first language of cloud native. It is necessary to verify whether the standard library of Go supports DNS caching. Through our testing and verification, we found that Go’s standard library net.http does not support DNS caching, nor does it support nscd caching. It should be that the library function of glibc is not called, and the function similar to the getaddrinfo function is not implemented. This is the same as GoThe bootstrapping of the language is related. Go has been basically written in Go (.go) and assembly (.s) files since 1.5, and the C (.c) files of the previous version have been completely rewritten. However, there are some third-party Go version DNS caching libraries, which can be implemented in the application layer by themselves, or the httpclient of the fasthttp library can be used.

(1) Standard library net.http

package main

import (

var httpUrl string

func main() {
    flag.StringVar(&httpUrl, "url", "", "url")
    getUrl := fmt.Sprintf("http://%s/", httpUrl)

    fmt.Printf("url: %s\n", getUrl)
    for i := 0; i < 5; i++ {
        _, buf, err := httpGet(getUrl)
        if err != nil {
            fmt.Printf("err: %v\n", err)
        fmt.Printf("resp: %s\n", string(buf))
        time.Sleep(10 * time.Second)    # 等待10s发起另一个请求

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,  //设置短连接
        IdleConnTimeout:   readWriteTimeout,
    client := &http.Client{
        Timeout:   readWriteTimeout,
        Transport: tr,
    return client

From the test results, net.http goes to DNS query every time and does not support DNS cache.

(2) fasthttp library

The fasthttp library is a Go version high-performance HTTP library. Through extreme performance optimization, the performance is 10 times that of the standard library net.http. One of the optimizations is to support DNS caching, as we can see from its source code

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", "")
    // 		},
    // 	},
    // }
    Resolver Resolver

    // DNSCacheDuration may be used to override the default DNS cache duration (DefaultDNSCacheDuration)
    DNSCacheDuration time.Duration

You can refer to the following methods to use the 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,

(3) Third-party DNS cache library

This is a Go version in github DNS cache library

You can refer to the following code to support DNS caching in the HTTP library

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 {


(1) requests library


import requests


num = 0
while num < 5:
    headers={"Connection":"close"}     # 开启短连接
    r = requests.get(url,headers = headers)
    num +=1

(2) httplib2 library

#!/usr/bin/env python
import httplib2
http = httplib2.Http()

num = 0
while num < 5:
        '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)
    num +=1

(3) urllib2 library


import urllib2
import cookielib

httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
opener = urllib2.build_opener(httpHandler, httpsHandler)

    '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:
    response = urllib2.urlopen(request)
    print page
    num +=1

The three libraries tested by Python all support the DNS cache of nscd (it is speculated that the underlying layer also calls the getaddrinfo function). The above tests use HTTP short connections and are all tested in the python2 environment.


For HTTP clients, HTTP can be enabled first The keep-alive mode can reuse TCP connections, which can reduce the time-consuming TCP handshake and repeated requests for domain name resolution, and then enable the nscd cache. In addition to Go, C/C++, Java, and Python can all support DNS cache to reduce DNS queries time consuming.

Only the commonly used C/C++, Java, Go, and Python languages ​​are analyzed here, and friends who are familiar with other languages ​​are welcome to add.

2. Commonly used dns caching service in Unix/Unix-like systems:

For some special reasons, if the self-developed or non-self-developed client itself cannot provide DNS caching support, it is recommended that administrators deploy DNS caching programs in their system environment;

Now introduce several common lightweight DNS caching programs suitable for Unix/Unix-like systems. Most desktop operating systems such as Windows, MacOS and almost all web browsers have their own DNS caching function, so this article will not repeat them.

PS DNS cache service, please make sure to start it with the system;


The name service cache daemon is installed and used out-of-the-box. It is usually installed by default in the Linux system. For related introductions, please refer to its manpage:man nscd;man nscd.conf

(1) Installation method: Install through the system’s own software package management program, such as yum install nscd

(2) Cache management (clear):

service nscd restart Restart the service to clear all caches;

nscd -i hosts Clear the domain name cache in the hosts table (hosts is the table name used by the domain name cache, nscd has multiple cache tables, please refer to the relevant manpage of the program)


It is relatively lightweight and can be selected as an alternative to nscd, which usually needs to be installed separately

(1) Installation method: Install through the system’s own software package management program, such as yum install dnsmasq

(2) Core file introduction (based on Dnsmasq version 2.86, the lower version is slightly different, please refer to the corresponding version documents such as manpage, etc.)

(3) /etc/default/dnsmasq provides six variable definitions to support six control functions

(4) /etc/dnsmasq.d/ This directory contains README files, which can be referred to; custom configuration files can be stored in the directory

(5) /etc/dnsmasq.conf main configuration file, if only dnsmasq is configured as a cache program, please refer to the following configuration

listen-address=                #程序监听地址,务必指定本机内网或回环地址,避免暴露到公网环境
port=53                                 #监听端口
resolv-file=/etc/dnsmasq.d/resolv.conf  #配置dnsmasq向自定义文件内的 nameserver 转发 dns 解析请求
cache-size=150                          #缓存记录条数,默认 150 条,可按需调整、适当增大
no-negcache                             #不缓存解析失败的记录,主要是 NXDOMAIN,即域名不存在
log-queries=extra                       #开启日志记录,指定“=extra”则记录更详细信息,可仅在问题排查时开启,平时关闭
log-facility=/var/log/dnsmasq.log       #指定日志文件

#同时需要将本机 /etc/resolv.conf 第一个 nameserver 指定为上述监听地址,这样本机系统的 dns 查询请求才会通过 dnsmasq 代为转发并缓存响应结果。
#另 /etc/resolv.conf 务必额外配置 2 个 nameserver,以便 dnsmasq 服务异常时支持系统自动重试,注意 resolv.conf 仅读取前 3 个 nameserver

(6) Cache management (clear):

kill -s HUP `pidof dnsmasq` Recommended way, no need to restart the service

kill -s TERM `pidof dnsmasq` or service dnsmasq stop

service dnsmasq force-reload or service dnsmasq restart

(7) Official documents:

3. Pure intranet business cancels the request to query the AAAA record of the domain name

Taking the linux operating system as an example, commonly used network request command line tools often complete the domain name resolution process by calling getaddrinfo(), such as ping, telnet, curl, wget, etc. Each resolution of the same domain name will initiate two requests, querying domain name A record (ie IPV4 address) and AAAA record (ie IPV6 address) respectively.

Because most of the company’s intranet environment and cloud intranet environment have not yet used the ipv6 network, usually the DNS system does not add AAAA records for intranet domain names, and requesting AAAA records for domain names in vain will cause front-end applications and back-end DNS services Unnecessary resource overhead. Therefore, for services that only need to request the intranet domain name, if you decide to develop your own client, it is recommended that developers design it to only request the A record of the intranet domain name according to the actual situation, especially when the local caching mechanism cannot be implemented for some reason.

4. Standardize domain name processing logic

The client needs to strictly regulate the processing logic of domain names/host names to avoid generating a large number of resolution requests for non-existing domain names (ensure that domain names are obtained from authoritative channels, and avoid intentionally or accidentally using randomly constructed domain names and host names), so the return of such requests The result (NXDOMAIN) is usually not cached or cached for a short period of time, and will trigger the client to retry, which will have a certain impact on the backend DNS system.

#DNS #Cache #Configuration #Suggestions #Development #Language #Cloud #Developers #Personal #Space #News Fast Delivery

Leave a Comment

Your email address will not be published. Required fields are marked *