java dns解析缓存之源码解析

java默认情况会缓存dns解析的结果,导致的结果是当域名对应的ip已经变化后,正在运行的java程序不会立刻知道ip的变化,而是仍然访问的变化前的ip。问题来了,默认情况java虚拟机会缓存dns解析结果多少秒?如何修改缓存时间呢?能不能把dns解析缓存关掉呢?就这些问题,我研究了下源码,以下是源码解析。

dns缓存机制

以获取一个域名www.1688.com对应的ip为例,其代码踪迹是这样的:

  1. InetAddress.getByName(“www.1688.com”)
  2. InetAddress.getAllByName(“www.1688.com”)[0]
  3. InetAddress.getAllByName(“www.1688.com”, null)
  4. InetAddress.getAllByName0(“www.1688.com”, null, true)
  5. InetAddress.getCachedAddress(“www.1688.com”)

InetAddress.getCachedAddress的代码如下:

可以看到cache有两个,即有效DNS解析的cache(addressCache)和无效DNS解析的cache(negativeCache),为了让dns解析效率更高java很聪明的把dns解析正确的域名保存下来了,方便后续再查时能最快的返回结果;同时也缓存了无效的域名,因为根据DNS解析的原理,往往无效的域名解析耗时比正常的域名解析时间要长,所以缓存无效的域名可以有效的避免浪费时间在查找无效域名上,提升代码性能。下面是java.net.InetAddress$Cache的源码:

从上面的Cache类的get方法可以看到:

  • 如果policy为0,那么cache的get方法就永远返回null,相当于cache不起作用了;
  • 如果policy为-1,那么cache缓存过的entry永远都不会过期,也就是说就算域名对应的ip变了,cache再也不更新ip了
  • 如果policy为n,根据“System.currentTimeMillis() + (policy * 1000)”,那么这个entry就可以在cache中存活n秒钟,即n秒后,该entry会被移除出cache

问题是policy怎么来的,看方法Cache.getPolicy()方法里面,主要是InetAddressCachePolicy类的get()和getNegative()方法。看sun.net.InetAddressCachePolicy类的源码:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/sun/net/InetAddressCachePolicy.java#InetAddressCachePolicy

 

看到这个代码就恍然大悟了,policy的值可以来自两个地方(值根据需要自己设置,此处为示例):

  • 一个是在java.security文件中配置networkaddress.cache.ttl=1111networkaddress.cache.negative.ttl=2222
  • 另一个是在启动单数中添加这两个参数-Dsun.net.inetaddr.ttl=1111-Dsun.net.inetaddr.negative.ttl=2222

而且这两中配置方法有一个优先级,即第一种方法优先级高,但是第一种方法需要在打开Java中的SecurityManager,若没有打开则${java.home}/jre/lib/security/java.security文件的配置将不会生效,可以参考http://www.314p.com/gywm.aspx?lm=30&wzid=4077打开Java中的SecurityManager。

下面附上我本机的jdk1.6.0_35的${java.home}/jre/lib/security/java.security文件的部分源码:

以上就是默认的配置,可以看到

  • #networkaddress.cache.ttl=-1这一行是被注释掉的,也就是说默认的DEFAULT_POSITIVE,即30秒更新一次可访问的DNS解析
  • networkaddress.cache.negative.ttl=10没有被注释掉,说明不可访问的DNS解析缓存10秒

DNS解析缓存实现的一点思考

看了源码之后才发现里面有多处synchronized锁,主要是两块:

但是若仔细看会发现,对cache的synchronized是在getCachedAddress(host)方法上,也就是说每个获取域名的方法都需要调这个方法,而这个方法里面有对cache的synchronized,这会不会是一个性能考虑的点呢?这里是读cache,并不是写cache,为了保证写的时候(可能cache会扩容),度不会出问题,加了synchronized,感觉不值啊,这也是不建议有事没事调用InetAddress.getByName(host)的原因。

个人觉得,是否可以考虑把这个地方设计成Copy-On-Write的Map,也就是说在写cache的时候,copy一份写,读不加锁,写完后,把cache替换一下,可以保证数据的最终一致性。这样写性能还行,读没有任何锁,性能很多,对于获取域名这种场景,肯定是读远多于写的。

这里一篇关于Copy-On-Write的文章http://www.kankanews.com/ICkengine/archives/118889.shtml可以参考

看看大家的意见,欢迎抛砖。

 

 

 

小米1升级最新miui后很卡的请看这里

nnd,我的小米1是第一批买的,去年发现小米1的一些服务停止了,很不爽,只能升级到最新的miuiv4,后来又升级到miuiv5,近来几个月速度明显在下降,甚至到了很难用的地步,不知原因。

我一度在小米论坛上找破解方案。有人说小米已经抛弃米1了,不支持小米1了,所以把系统升级到最新的miuiv5后就自动慢了,还是回复出厂设置使用原来的android 2.3的miui吧;也有人说要想变得快点,就需要三清(清楚缓存,清楚用户数据,清楚所有数据)后,刷卡重装,感觉挺费劲的。

最近实在是不能忍受米1的慢了,正好米4出来了,看起来还不错,想入手玩玩,结果发现如今的手机没有当年那么好抢了,抢了几次都没有抢到。没办法,还是回来整米1吧。

话说红米手机的配置和米1差不多,为啥红米跑miuiv5一点问题都没有,我的米1这么卡呢,应该不是硬件配置的问题。所以我猜测是我曾经的多次系统升级,而软件不一定赶上了系统的版本升级,导致软件与系统有一定的调优方面的不兼容,另外,手机用了快3年了,卡和rom里的太多了,没有清理过,估计碎片也很多。所以这次我狠下心,不管费不费劲都要彻底的清空重装。

按照miui官网的教程这样刷机http://www.miui.com/shuaji-301.html:

  1. 下载相应的miui最新的稳定版安装包:http://www.miui.com/download.html
  2. 重命名安装包为 update.zip 拷贝至格式化后的SD卡的根目录
  3. 三清(清楚缓存,清楚用户数据,清楚所有数据),在关机状态下,同时按+音量键和关机键 进入recovery模式(此模式下:关机键为确认键,音量键为上下移动的按键),然后选择“简体中文”–>”清楚数据”–>分别清楚缓存,清楚用户数据,清楚所有数据,然后关机
  4. 把SD卡插入后,再使用步骤3中的方法进入recovery模式,选择“简体中文”后,选择“将update.zip安装至系统一”并确认。选择确认等待完成,然后重启即可

现在感觉我的米1速度快了不少,和当初刚买来是差不多的速度了。

eclipse中文乱码问题解决方案汇总

eclipse中文乱码都是因为字符编码与默认的编码不符合导致的,有很多的方法可以解决,不需要安装任何插件就可以搞定。针对不同的情况,需要使用不同的方案,下面就针对一些案例讲解如何解决乱码问题。解决乱码问题的主要思路是设置正确合适的编码,如果不知道目标文件原本的编码,可以进行一定的尝试,通常尝试下GBK和UTF-8这两个编码即可。

1. 设置单个文件的字符编码,解决单个文件的乱码问题

有时候不小心copy来的单个文件编码与你workspace的默认编码不一致,就导致了单个乱码。解决办法:在Pakcage Explorer或者Project Explorer视图里面,右键点击该文件–>选择“Properties”–>”Text file encoding”–>给”Other”项设置相应的编码。(需要注意的是,如果copy来的文件在eclipse中显示的是正常,但是编码与其他文件不一致,若你想统一编码,就需要在设置编码前,记得先把文件内容copy一下,然后设置好编码,再把copy的内容粘贴到编码修改后的文件中,这样会不乱码;一修改编码文件内容就会乱码),如下图:

eclipse设置单个文件的字符编码
eclipse设置单个文件的字符编码

2. 设置第三方jar包的字符编码,解决整个jar的乱码问题

第三方jar包的编码问题可能是最常见的问题,其解决方案与单个文件的比较类似,在Pakcage Explorer或者Project Explorer视图里面,右键第三方jar包–>选择“Properties”–>给”Encoding”项设置相应的编码,如下图:

在eclipse中给jar包设置字符编码
在eclipse中给jar包设置字符编码

3. 分别设置不同类型文件的字符编码

有时候为了统一所有工程的编码,可能需要提前设置好各类型文件的字符编码,比如:把所有的jar类都设置为GBK编码,把所有的xml设置为UTF-8编码等。这时候就需要有针对性的给不同类型的文件设置不同的编码。步骤是这样的:Windows–>Perferences–>General–>Content Types–>选择文件类型–>设置“Default encoding”项。如图下:

在eclipse中给指定类型的文件设置字符编码
在eclipse中给指定类型的文件设置字符编码

4. 设置整个workspace的文本文件的默认字符编码

往往workspace都有一个默认的字符编码,如果你的工程大部分或者全部是另一个编码,那么你可以重新设置一个默认的字符编码,后续新建工程就不需要再去设置编码了。步骤是这样的:Windows–>Perferences–>General–>Workspace–>Test file encoding–>给“Other:”设置相应的编码,如下图:

在eclipse中设置workspace的字符编码
在eclipse中设置workspace的字符编码

个人经验

其实一般开源的代码都是UTF-8编码的,我们平时开发的时候最好也都统一使用UTF-8,在开发初期直接把workspace的文本文件的编码设置为UTF-8,这比较省事。在开发过程中,如果发现添加进来的三方jar包编码不是UTF-8,那么可以针对这个jar设置其编码;若copy某个源码文件到工程中发现是乱码的,那么可以仅设置这一个文件的编码成正确的编码。