iOS 之 DNS的缓存

DNS Cache

Posted by Elliot on October 18, 2016

版权声明:本文为博主原创文章,未经博主允许不得转载

DNS缓存

DNS的缓存策略,tcp连接优化处理

看了一篇DNS缓存的博客,想到我们自己的项目中也有和这个机制,于是整理了一下

大神博客的核心思路:

  • 降低DNS请求带来的延迟
  • 客户端app的请求第一步都是DNS解析,直接跳过DNS解析这一步,能提升网络性能
  • 预防DNS劫持
  • DNS劫持指的是改变DNS请求的返回结果,将目的ip指向另一个地址。客户端自己做DNS与ip地址的映射就跨过了解析,让劫持者无从下手。
  • 服务器动态部署(这个如果是直接连IP的话,好像用不到)

设计自己的DNS映射机制

  1. 一个打包到app包里面的默认映射文件,这样可以避免第一次去服务器取配置文件带来的延迟。
  2. 有一个定时器能每隔一段时间从服务器获取最新的映射,并覆盖本地。
  3. 每次取到最新的映射文件之后,同时把上一次的映射文件保存起来作为替补,一旦出现线上配置失误不至于导致请求无法处理。
  4. 如果映射文件不能处理域名,要能回滚使用默认的DNS解析服务。
  5. 如果一个映射过后的ip持续导致请求失败,应该能从机制上保证这个ip地址不再使用。也就是需要一个无效映射淘汰机制。
  6. 无效的ip地址能及时上报到服务器,及时发现问题更新映射文件。

我们项目中的IP缓存策略是:

  • 第一次连接,首先连接默认内置IP,同时另起线程对内置域名进行DNS解析,解析结果存入DNSPool,并且进行连接(最多6个); 当有IP连接上后断开其他连接
  • 连上服务器后下发一些IPs,存入serverPool (或者服务器IP有变,则下发临时IP,存入tempPool,为了防止某些情况下IP变化连接不上)
  • 第二次及之后连接,从serverPool中取最多6个IP出来进行连接(带上版本号),同时另起线程对内置域名进行DNS解析,解析结果存入DNSPool, 并且进行连接(最多6个);同时对比serverPool,将新解析的IP加入serverPool;
  • 当有IP连上服务器后,断开其他连接;服务器根据serverPool的版本号判断是否下发IPs存入serverPool (或者服务器IP有变,则下发临时IP,存入tempPool,为了防止某些情况下IP变化连接不上)
  • 建联时每次都会去DNS解析域名,结果存入DNSPool(最短时间间隔60s)

注:

  • serverPool:默认IP+服务器下发IP池
  • DNSPool:DNS解析IP池
  • tempPool:服务器下发临时IP池

其他

ipv4 vs ipv6

ipv4是互联网协议(Internet Protocol,IP)的第四版

ipv6用于替代现行版本IP协议(IPv4)的下一代IP协议

ssl:数据加密协议,ipv6需要支持

DNS解析:域名解析

IPv4中使用gethostbyname()函数完成主机名到地址解析,这个函数仅仅支持IPv4,且不允许调用者指定所需地址类型的任何信息,返回的结构只包含了用于存储IPv4地址的空间。

IPv6中引入了getaddrinfo()的新API,它是协议无关的,既可用于IPv4也可用于IPv6。getaddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个addrinfo的结构(列表)指针而不是一个地址清单。这些addrinfo结构随后可由套接口函数直接使用。如此以来,getaddrinfo函数把协议相关性安全隐藏在这个库函数内部。应用程序只要处理由getaddrinfo函数填写的套接口地址结构。

代码:
+ (NSArray *)syncGetaddrinfoWithDomain:(NSString *)domain
{
    if (domain.length == 0) {
        return nil;
    }
    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_protocol = IPPROTO_TCP;
    struct addrinfo *addrs, *addr;

    int getResult = getaddrinfo([domain UTF8String], NULL, &hints, &addrs);
    if (getResult || addrs == nil) {
        NSLogToFile(@"Warn: DNS with domain:%@ failed:%d", domain, getResult);
        return nil;
    }
    addr = addrs;
    NSMutableArray *result = [NSMutableArray array];
    for (addr = addrs; addr; addr = addr->ai_next) {
        char host[NI_MAXHOST];
        memset(host, 0, NI_MAXHOST);
        getnameinfo(addr->ai_addr, addr->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
        if (strlen(host) != 0) {
            [result addObject:[NSString stringWithUTF8String:host]];
        }
    }
    freeaddrinfo(addrs);

    NSLogToFile(@"Info: DNS with domain:%@ -> %@", domain, result);
    return result;
}

DEMO在此