客户端:外网游戏玩家
服务端:外网游戏玩家直连服务。部署架构如下:
ServerA
/ | \
/ | \
LB1 LB2 LB3
\ | /
\ | /
ServerB
DNS: example.com –> IP1(LB1), IP2(LB2), IP3(LB3)
游戏客户端通过 example.com 访问服务,ServerA 和 ServerB 互备。
为了防止跨运营商网络故障及提升用户体验,三个 LB 分别对应移动、联通、电信。DNS 解析的目标是:移动用户返回移动的 LB, 联通的访问联通 LB, 因此类推,公司的 GSLB 系统实现了这一点。
按照上文的 DNS 配置,虽然一个域名配置了 3 个 IP, 但是为了实现就近接入,客户端每次域名解析实际上只返回一个 IP. 但是倘若访问的 LB 故障,那该客户端重试的机会都没有。
Note: 由于 DNS 是分级实现,且每一级都存在缓存,客户端是没办法做到短时间之内多次域名解析获取不同的 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.