简介
dns基本过程
基本过程
- 主机向dns server(比如
192.168.1.253
) 查询域名的ip地址 - dns server如何查询域名的ip地址:分级查询
以www.example.com
为例
名称 | 备注 | |
---|---|---|
根域名 | .root |
www.example.com 本质是www.example.com.root |
顶级域名 | .com/.net |
|
次级域名 | .example |
用户可以注册 |
主机/三级域名 | www |
用户任意分配 |
所以内网dns server192.168.1.253
的查询过程是
.root
的NS记录和IP地址一般是不会变化的,所以内置在DNS服务器里面- 从
.root
查询.com
的NS记录和A记录 - 从
.com
查询.example
的NS记录和A记录 - 从
.example
查询www
的IP地址
公网dns server,比较有名的是google的8.8.8.8
和国内的114.114.114.114
dns在java应用中可能碰到的问题
在主机名解析为 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的一种),继而关闭连接。解决办法:
- 如果对方开放服务提供多个域名,则轮流使用域名建立连接
-
如果只有一个域名
- 设置jvm的
sun.net.inetaddr.ttl
- 若想完全控制ip的选取,可以只用dnsjava在代码层面获取域名对应的ip,然后每次轮流选取,将负载平摊到每个ip上
- 设置jvm的
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池第一次获取后,进程存续期间便不再变化。