如果大家记忆力不太差的话,那么应该会记得前段时间发生的全国性DNS解析故障:很多顶级域名被解析到了IP地址 「65.49.2.178」,导致中国互联网瘫痪了几个小时。不过在那起事件里一些移动客户端应用得以幸免,其原因在于它们使用了云端Hosts。
所谓云端Hosts,就是把原本放在本地的Hosts放到了云端,如果用JSON的话类似:
代码语言:javascript复制{
"foo.com": "1.2.3.4",
"bar.com": "2.4.6.8"
}
客户端跳过DNS查询过程,直接通过IP与服务端交互。用Shell模拟的话好比:
代码语言:javascript复制shell> curl -H "Host: foo.com" http://1.2.3.4/path
shell> wget -e "http_proxy=2.4.6.8" http://bar.com/path
鉴于国内网络情况的复杂性,很多服务往往会同时部署在网通,电信等机房,此时应该给每个机房配置独立的云端Hosts文件,以便用户能够选择合适线路的服务器。
有时候问题可能会更复杂。举个例子:若干HTTP接口,部署在北京网通和上海电信两个机房,全国各地的客户端在请求接口时,不管地理位置在哪,只要是网通线路就走北京网通;只要是电信线路就走上海电信。这些没有什么问题,但如果再加一个需求就可能有麻烦了:客户端通过HTTP接口能够拿到CDN下载地址,与HTTP接口只有北京上海两个机房不同的是,CDN下载地址所在的机房遍布全国。因为云端Hosts是通过HTTP接口服务器下发的,但是HTTP接口服务器机房数远远小于CDN下载服务器机房数,所以就产生了不和谐因素,假设一个来自辽宁电信的请求,通过北京电信获取云端Hosts,那么应该返回哪个机房的CDN地址比较好?理论上当然是返回辽宁电信的机房最好,可是如果CDN没有辽宁电信机房怎么办?如何从其它电信机房里选择一个最合适的?
问了一些朋友,他们的答复是需要一个测速系统,我从没接触过这玩意,从字面意思上猜测,我觉得大概意思可能是:请求到达后,随机返回一个节点,并记录数据传输的速度,通过一段时间的学习,就能找到最佳选择。不过对我来说这也太复杂了,我想要的其实仅仅是一个简易的云端Hosts,它不需要太智能,差不多够用就行。
最终我的解决方案很简单:经纬度!详细点说的话,客户端请求云端Hosts的时候,服务端通过IP判断客户端所属省份城市,然后计算出该地址的经纬度,进而和各个机房的经纬度做勾股定理运算,从而得出本线路里物理路径最近的机房。
网上有很多查询省份城市的IP库,推荐全球 IPv4 地址归属地数据库。有了省份城市,还要获取经纬度,好在百度、谷歌之类的大公司都提供了相应的服务。不过如果每次请求都要通过服务查询经纬度的话,无疑是不可取的,幸运的是在忽略大陆板块漂移影响的前提下,经纬度基本是不变的,所以我们可以事先都查询好保存起来,如此一来的话还需要一份省市行政区划大全的数据,这个可以参考我以前写文章。
实际查询经纬度的时候,我推荐使用谷歌的服务,比如查询我东北老家的经纬度:
http://maps.googleapis.com/maps/api/geocode/json?language=zh&address=新宾
不过这便引出另一个问题:谷歌总被墙怎么办?别忘了本文的精髓:不用域名用IP!我们可以通过「ping g.cn」查询谷歌在国内的服务器IP列表,然后通过IP访问服务即可:
http:///maps/api/geocode/json?language=zh&address=新宾
另外需要说一点安全上的建议:客户端在和服务端交互获取云端Hosts时,最好做数据签名或者走加密协议,否则数据有被篡改的风险,从而导致潜在的隐患。
结尾说点番外话,文章开头我们提到了那起席卷全国的DNS解析故障,关于故障原因普遍猜测是功夫网在做DNS污染时误操作所致。以后遇到类似情况应该如何判断呢?
以微博为例,先看一次正常的DNS请求应该是什么样的:
代码语言:javascript复制shell> dig trace weibo.com
. 7109 IN NS g.root-servers.net.
. 7109 IN NS k.root-servers.net.
. 7109 IN NS j.root-servers.net.
. 7109 IN NS e.root-servers.net.
. 7109 IN NS f.root-servers.net.
. 7109 IN NS d.root-servers.net.
. 7109 IN NS l.root-servers.net.
. 7109 IN NS i.root-servers.net.
. 7109 IN NS a.root-servers.net.
. 7109 IN NS m.root-servers.net.
. 7109 IN NS h.root-servers.net.
. 7109 IN NS c.root-servers.net.
. 7109 IN NS b.root-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
weibo.com. 172800 IN NS ns1.sina.com.cn.
weibo.com. 172800 IN NS ns2.sina.com.cn.
weibo.com. 172800 IN NS ns3.sina.com.cn.
weibo.com. 172800 IN NS ns4.sina.com.cn.
weibo.com. 172800 IN NS ns4.sina.com.
weibo.com. 172800 IN NS ns3.sina.com.
weibo.com. 60 IN A 180.149.134.141
weibo.com. 60 IN A 180.149.134.142
weibo.com. 86400 IN NS ns2.sina.com.cn.
weibo.com. 86400 IN NS ns3.sina.com.
weibo.com. 86400 IN NS ns4.sina.com.cn.
weibo.com. 86400 IN NS ns4.sina.com.
weibo.com. 86400 IN NS ns1.sina.com.cn.
weibo.com. 86400 IN NS ns3.sina.com.cn.
以推特为例,再看一次异常的DNS请求应该是什么样的:
代码语言:javascript复制shell> dig trace twitter.com
. 7109 IN NS g.root-servers.net.
. 7109 IN NS k.root-servers.net.
. 7109 IN NS j.root-servers.net.
. 7109 IN NS e.root-servers.net.
. 7109 IN NS f.root-servers.net.
. 7109 IN NS d.root-servers.net.
. 7109 IN NS l.root-servers.net.
. 7109 IN NS i.root-servers.net.
. 7109 IN NS a.root-servers.net.
. 7109 IN NS m.root-servers.net.
. 7109 IN NS h.root-servers.net.
. 7109 IN NS c.root-servers.net.
. 7109 IN NS b.root-servers.net.
twitter.com. 34092 IN A 59.24.3.173
明显看出,在查询微博的请求里,结果分为四个部分,首先返回ROOT域名服务器,然后返回GTLD域名服务器,接着返回微博域名服务器,最后返回IP地址;然而在查询推特的请求里,结果明显被篡改了,跳过了GTLD域名服务器和推特域名服务器,直接返回了假的IP地址,这正是DNS污染时的典型特征,大家下次遇到类似问题的时候试试吧!