公司内网环境中许多调用资源(数据库、web接口等)都是通过内网DNS服务来进行域名-IP的映射。
但经常出现DNS映射修改完毕后,应用中连接的资源迟迟没有变更。
以前一直笼统的认为是linux的dns缓存导致,今天做了一次完整的分析,结果如下:
1、Linux系统的本地DNS的缓存
CentOS系统本身并不包含DNS的缓存机制,除非安装并启动了nscd服务(name server cache daemon)。
nscd服务启动后会默认为本地的所有dns解析做一层缓存,过期时间默认为3600秒,重启应用程序也不会重置nscd的缓存,除非用/etc/init.d/nscd reload,强制刷新nscd缓存。
开启nscd服务可以大大降低应用程序请求DNS服务的频率,同时一定程度上可以对DNS服务的故障有一定容错。但缺点非常明显,DNS服务的映射改变无法实时的被应用程序感知,每次修改映射后都必须在所有客户端机器reload nscd。
以上结论通过DNS服务日志得到验证。
2、JVM虚拟机的本地DNS缓存
实现在java.net.InetAddress的一个简单的DNS缓存机制,以前被误认为是Linux的DNS缓存,jdk6/7中默认为缓存30秒。
缓存范围为JVM虚拟机进程,也就是说同一个JVM进程中,30秒内只会为一个域名请求DNS服务器一次,可以大大降低应用程序对DNS解析的网络损耗和对DNS服务产生的压力。
以上结论通过JAVA测试程序和DNS服务日志得到验证。
3、长连接的处理(数据库链接、redis连接、zookeeper、activeMQ连接等)
根据1、2两点结论,当内网DNS服务某一个域名映射修改后,应用程序最多在30秒内就会响应该变化。但实际确不是如此,原因就是很多资源是“长连接”方式。
比如数据库连接池这种就是典型的长连接,为了保证连接池效率,我们也不能把单个连接的有效期设的太短。这就导致了这类长连接无法快速响应DNS服务器的映射改变。
解决办法只有一个:DNS服务器的映射变更后,需要对应用程序做重启,以便让长连接按照新的DNS映射来进行建立。
以上结论通过JAVA测试程序和DNS服务日志得到验证。
饶了一圈,最后还是回到原点:
为了保证应用程序里的这些长连接资源能够及时响应DNS映射的改变,目前还是得靠重启应用来解决。