7

dns隐藏的一个坑

 2 years ago
source link: https://qiankunli.github.io/2017/11/29/dns.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

TCP/IP 网络是通过 IP 地址来确定通信对象的,因此不知道 IP 地址就无法将消息发送给对方,这和我们打电话的时候必须要知道对方的电话号 码是一个道理。然而,就像你很难记住电话号码一样,要记住一串由数字组成 的 IP 地址也非常困难。因此,相比 IP 地址来说,网址中还是使用服务器名称比较好。

有人问了:“既然如此,那干脆不要用 IP 地址,而是用名称来确定通信对象不就好了吗?从运行效率上来看, IP 地址的长度为 32 比特,也就是 4 字节,相对地,域名最短也要几十个字节,最长甚至可以达到 255 字节。这增加了路由器的负担,传送 数据也会花费更长的时间 。

dns基本过程

DNS 原理入门

  1. 主机向dns server(比如192.168.1.253) 查询域名的ip地址
  2. dns server如何查询域名的ip地址:分级查询

www.example.com为例

名称 备注
根域名 .root www.example.com本质是www.example.com.root
顶级域名 .com/.net  
次级域名 .example 用户可以注册
主机/三级域名 www 用户任意分配

所以内网dns server192.168.1.253 的查询过程是

  1. .root的NS记录和IP地址一般是不会变化的,所以内置在DNS服务器里面
  2. .root查询.com的NS记录和A记录
  3. .com查询.example的NS记录和A记录
  4. .example查询www的IP地址

公网dns server,比较有名的是google的8.8.8.8和国内的114.114.114.114

dns在java应用中可能碰到的问题

jvm dns缓存问题解决方式

在主机名解析为 IP 地址后,资源 IP 地址将保存在 JVM 的高速缓存中。如果改变了资源的 IP 地址,则需要重新启动应用服务器,使 Identity Manager 能够检测所做更改 (ID-3635)。这是 Sun JDK(1.3 及更高版本)中的设置,可以使用 sun.net.inetaddr.ttl 属性设置解析成功的域名记录JVM中缓存的有效时间,0表示禁止缓存,-1表示永远有效,JVM默认是永远有效

jvm 默认永远有效,就会带来一些问题。比如访问一些第三方公司的开放服务,对方给一个域名,作为调用方,老往一个ip发请求,在极端情况下非常容易有问题。比如,向APNs发送推送,短时间向某一个APNs server ip发送大量推送,极易受到GoAway frame(http2协议 frame的一种),继而关闭连接。解决办法:

  1. 如果对方开放服务提供多个域名,则轮流使用域名建立连接
  2. 如果只有一个域名

    • 设置jvm的sun.net.inetaddr.ttl
    • 若想完全控制ip的选取,可以只用dnsjava在代码层面获取域名对应的ip,然后每次轮流选取,将负载平摊到每个ip上

dnsjava和netty的结合问题

我们知道,netty等异步框架的代码本质上是由“事件驱动引擎”执行的,因此代码中应尽量避免阻塞操作,但dnsjava获取域名ip的过程带有阻塞性质。常见的办法是,维护一个ip容器做缓冲,调用方作为消费者直接从容器中获取ip,若拿不到则使用java默认解析。另启一个单独线程作为生产者将解析到的ip注入到ip容器中。

此外,netty本身提供RoundRobinInetAddressResolver,使用方式大致为

bootstrap.resolver(new AddressResolverGroup<InetSocketAddress>() {
    @Override
    protected AddressResolver<InetSocketAddress> newResolver(EventExecutor executor) throws Exception {
        AddressResolver<InetSocketAddress> addressResolver = new RoundRobinInetAddressResolver(executor, new DefaultNameResolver(executor)
        ).asAddressResolver();
        return addressResolver;
    }
});

与InetAddress根据域名拿到一个ip不同,RoundRobinInetAddressResolver启动时根据域名获取多个ip,后续使用时,随机返回ip。优点是RoundRobinInetAddressResolver异步方式获取ip(可以学习一个独立的功能如何提供异步接口),但缺点是,ip池第一次获取后,进程存续期间便不再变化。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK