DNS 是否适合做容灾

目录

    • 目录
    • 场景
    • 问题
      • DNS server 返回一个 IP
      • DNS server 返回多个 IP
    • 解决方案 - 地址列表

场景

客户端:外网游戏玩家
服务端:外网游戏玩家直连服务。部署架构如下:

     ServerA
    /   |   \
   /    |    \
LB1    LB2    LB3
   \    |    /
    \   |   /
     ServerB

DNS: example.com –> IP1(LB1), IP2(LB2), IP3(LB3)

游戏客户端通过 example.com 访问服务,ServerA 和 ServerB 互备。
为了防止跨运营商网络故障及提升用户体验,三个 LB 分别对应移动、联通、电信。DNS 解析的目标是:移动用户返回移动的 LB, 联通的访问联通 LB, 因此类推,公司的 GSLB 系统实现了这一点。

问题

DNS server 返回一个 IP

按照上文的 DNS 配置,虽然一个域名配置了 3 个 IP, 但是为了实现就近接入,客户端每次域名解析实际上只返回一个 IP. 但是倘若访问的 LB 故障,那该客户端重试的机会都没有。
Note: 由于 DNS 是分级实现,且每一级都存在缓存,客户端是没办法做到短时间之内多次域名解析获取不同的 IP 的。

DNS server 返回多个 IP

如果一次解析返回多个 IP, 客户端逐个尝试是否可行?这样的确可以保证在不是所有节点都故障的情况下能连接成功,但是并不能保证就近接入。因为 DNS server 是没办法保证返回的 IP 的顺序的,这样就无法将就近网络的 IP 放在第一位。可以看一下 getaddrinfo() 的例子:

from optparse import OptionParser
import socket
import json

def resolve(host, cname=False, sort=False):
    flags = 0
    if cname: flags = socket.AI_CANONNAME
    result = socket.getaddrinfo(host, None, socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, flags)

    addr_list = []
    for one in result:
        one_addr = (one[4][0], one[3])
        addr_list.append(one_addr)
    if sort: addr_list.sort(key=lambda x:x[0], reverse=True)
    print json.dumps(addr_list, indent = 4)

def main():
    usage = "%prog --host=HOST [--cname] [--sort]"
    parser = OptionParser(usage=usage)
    parser.add_option("--host",
                      action="store",
                      type="string",
                      dest="host",
                      help="domain name",
                      metavar="HOST")
    parser.add_option("--cname",
                      action="store_true",
                      dest="cname",
                      default=False,
                      help="set AI_CANONNAME flag")
    parser.add_option("--sort",
                      action="store_true",
                      dest="sort",
                      default=False,
                      help="sort IP addresses in the result")

    (options, args) = parser.parse_args()

    if not options.host:
        parser.error("option --host not supplied")

    resolve(options.host, options.cname, options.sort)

if __name__ == "__main__":
    main()
$ python dns_resolve_getaddrinfo.py --host=www.baidu.com
[
    [
        "115.239.210.27", 
        ""
    ], 
    [
        "115.239.211.112", 
        ""
    ]
]
$ python dns_resolve_getaddrinfo.py --host=www.baidu.com
[
    [
        "115.239.211.112", 
        ""
    ], 
    [
        "115.239.210.27", 
        ""
    ]
]

$ python dns_resolve_getaddrinfo.py --host=www.163.com  
[
    [
        "101.227.66.207", 
        ""
    ], 
    [
        "114.80.143.193", 
        ""
    ]
]
$ python dns_resolve_getaddrinfo.py --host=www.163.com
[
    [
        "114.80.143.193", 
        ""
    ], 
    [
        "101.227.66.207", 
        ""
    ]
]

此外,倘若客户端访问的 DNS server 故障或被劫持,那就所有节点都无法访问了。

解决方案 - 地址列表

综合上文分析,我们的目标是:在服务端网络正常的情况下实现就近接入,异常的时候能使用其他节点,此外,还需防止域名解析故障。最好的方式是 DNS server 只返回一个就近的 IP, 客户端使用地址列表,即:

example.com;IP1;IP2;IP3

客户端优先使用 example.com 解析出的 IP, 当这个 IP 访问异常或域名解析失败时再使用后续的 IP.

你可能感兴趣的:(高可用架构)