Skip to content

Latest commit

 

History

History
335 lines (263 loc) · 17.1 KB

README.md

File metadata and controls

335 lines (263 loc) · 17.1 KB

Java Dns Cache Manipulator(DCM) Library

Build Status Coveralls branch JDK support
License Javadocs Maven Central GitHub release
Chat at gitter.im GitHub Stars GitHub Forks user repos GitHub issues GitHub Contributors

👉 用编码的方式设置/查看JVMDNS(实际上设置的是DNS Cache),支持JDK 6+,支持IPv6

🔧 功能

  • 设置/重置DNS(不会再去Lookup DNS
    • 可以设置单条
    • 或是通过Properties文件批量设置
  • 清空DNS Cache(即所有的域名重新Lookup DNS
  • 删除一条DNS Cache(即重新Lookup DNS
  • 查看DNS Cache内容
  • 设置/查看JVM缺省的DNS的缓存时间

🎨 需求场景

  1. 一些库中写死了连接域名,需要通过设置host文件绑定才能做测试。结果是:
    • 自动持续集成的机器上一般同学是没有权限去设置host文件的,导致项目不能持续集成。
      实际上是因为这点,催生这个库的需求。 😣🔫
    • 单元测试需要每个开发都在开发机上做host绑定,增加了依赖的配置操作且繁琐重复。
  2. 一些功能需要域名作为输入参数,如使用HTTP请求的网关 或是 有域名检查限制的Web应用。
    这种情况下,让需要让一个域名连接到测试机器的IP上,或是 使用一个还不存在的域名但又不想或不能去配置DNS
  3. 在性能测试时,
    • 不去做网络的DNS LookupDNS解析消耗),这样使得压测更加关注服务器响应,压测更充分反应出实现代码的性能。
    • 可以动态设置DNS缓存,无需修改host文件和http链接等不灵活的方式。
    • 一个JVM进程可以对应一套域名绑定,相互之间不影响,可以实现多场景,多域名绑定的需求压测。
  4. 打开Java中的SecurityManager时(如在Web容器Tomcat中的Web应用),JavaDNS缺省是不会失效的。 如果域名绑定的IP变了,可以通过这个库重置DNS
    • 通过Java Dns Cache Manipulator Tool设置运行中JVM DNS Cache
      无需 应用包含了Java Dns Cache Manipulator Library依赖(即Jar)。
    • 或通过执行入口调用Java Dns Cache Manipulator Library的方法,比如远程调用或是jvm-ssh-groovy-shell
      需要 应用已经包含了Java Dns Cache Manipulator Library依赖(即Jar)。

👥 User Guide

通过类DnsCacheManipulator设置DNS

直接设置

DnsCacheManipulator.setDnsCache("www.hello.com", "192.168.1.1");
DnsCacheManipulator.setDnsCache("www.world.com", "1234:5678:0:0:0:0:0:200e"); // 支持IPv6地址

// 上面设置全局生效,之后Java中的所有的域名解析逻辑都会是上面设定的IP。
// 下面用一个简单获取域名对应的IP,来演示一下:

String ip = InetAddress.getByName("www.hello.com").getHostAddress();
// ip = "192.168.1.1"
String ipv6 = InetAddress.getByName("www.world.com").getHostAddress();
// ipv6 = "1234:5678:0:0:0:0:0:200e"


// 可以设置多个IP
DnsCacheManipulator.setDnsCache("www.hello-world.com", "192.168.2.1", "192.168.2.2");

String ipHw = InetAddress.getByName("www.hello-world.com").getHostAddress();
// ipHw = 192.168.2.1 ,读到第一个IP
InetAddress[] allIps = InetAddress.getAllByName("www.hello-world.com");
// 上面读到设置的多个IP

// 设置失效时间,单元毫秒
DnsCacheManipulator.setDnsCache(3600 * 1000, "www.hello-hell.com", "192.168.1.1", "192.168.1.2");

通过dns-cache.properties文件批量配置

在代码测试中,会期望把域名绑定写在配置文件。

使用方式如下:

ClassPath上,提供文件dns-cache.properties

# 配置格式:
# <host> = <ip>
www.hello-world.com=192.168.1.1
# 支持设置多个IP,用逗号分隔
www.foo.com=192.168.1.2,192.168.1.3
# 支持IPv6
www.bar.com=1234:5678:0:0:0:0:0:200e

注:
dns-cache.properties是缺省文件名,可以通过JVM-D选项dcm.config.filename修改使用的配置文件名,如
-Ddcm.config.filename=my-dns-cache.properties

然后通过下面的一行代码完成批量设置:

DnsCacheManipulator.loadDnsCacheConfig();

在单元测试中,往往会写在测试类的setUp方法中,如:

@BeforeClass
public static void beforeClass() throws Exception {
    DnsCacheManipulator.loadDnsCacheConfig();
}

清空JVM DNS Cache

DnsCacheManipulator.clearDnsCache();

删除一条DNS Cache

即重新Lookup DNS

DnsCacheManipulator.removeDnsCache("aliyun.com");

查看JVM DNS Cache

DnsCache dnsCache = DnsCacheManipulator.getWholeDnsCache()
System.out.println(dnsCache);

设置/查看JVM缺省的DNS的缓存时间

// 查看缓存时间,单位秒。-1表示永远缓存,0表示不缓存
int cachePolicy = DnsCacheManipulator.getDnsCachePolicy();
// 设置缓存时间
DnsCacheManipulator.setDnsCachePolicy(2);

// 查看未命中条目的缓存时间
DnsCacheManipulator.getDnsNegativeCachePolicy()
// 设置未命中条目的缓存时间
DnsCacheManipulator.setDnsNegativeCachePolicy(0);

使用注意

  • 域名不区分大小写,域名会统一转成小写,再进入DNS Cache
    其中一个引发的现象是,DNS查询结果的域名会和输入的域名大小写不同,如果输入的域名有大写字母。
  • 对于已经完成解析保存了IP的逻辑,设置JVM DNS缓存,不会生效!可以重新创建 连接或Client解决。
    如对于HttpClient:
HttpClient client = new HttpClient();
GetMethod m1 = new GetMethod("https://www.aliyun.com");
client.executeMethod(m1);
String content = m1.getResponseBodyAsString();

// 设置DNS,绑定到自己的机器
DnsCacheManipulator.setDnsCache("www.aliyun.com", "192.168.1.1");

// 重新执行m1,仍然是老结果
client.executeMethod(m1);
String content = m1.getResponseBodyAsString();

// 重新创建GetMethod,才能得到自己机器上的结果
GetMethod m2 = new GetMethod("https://www.aliyun.com");
client.executeMethod(m2);
content = m2.getResponseBodyAsString();

更多详细功能

参见类DnsCacheManipulator的文档说明。

🔌 Java API Docs

Java API文档地址: http://alibaba.github.io/java-dns-cache-manipulator/apidocs

🍪 依赖

Maven示例:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dns-cache-manipulator</artifactId>
    <version>1.6.0-RC1</version>
</dependency>

可以在search.maven.org查看最新的版本。

👓 经过测试的JDK

JDK 系统 On 备注
openjdk6 64-Bit Linux travis-ci
oraclejdk7 64-Bit Linux travis-ci
openjdk7 64-Bit Linux travis-ci
oraclejdk8 64-Bit Linux travis-ci
applejdk6 64-Bit Mac 个人Mac jdk6由Apple提供下载地址
oraclejdk7 64-Bit Mac 个人Mac 从jdk7开始,Mac jdk直接在Oracle下载。
oraclejdk8 64-Bit Mac 个人Mac
oraclejdk6 64-Bit windows server 2012 r2 appveyor
oraclejdk6 32-Bit windows server 2012 r2 appveyor
oraclejdk7 64-Bit windows server 2012 r2 appveyor
oraclejdk7 32-Bit windows server 2012 r2 appveyor
oraclejdk8 64-Bit windows server 2012 r2 appveyor
oraclejdk8 32-Bit windows server 2012 r2 appveyor

PS:
感谢 travis-ciappveyor 免费提供了持续集成环境。

🎓 Developer Guide

如何设置JVMDNS Cache

JVMDNS Cache维护在类InetAddress的私有字段中,通过反射来设置,具体实现参见

注意设置JVMDNS Cache的线程安全问题

JVMDNS Cache显然是全局共用的,所以设置保证是线程安全的没有并发问题。

JDK 8为例,通过查看类InetAddress的实现可以确定:通过以addressCache字段为锁的synchronized块来保证线程安全。

其中关键代码如下:

/*
 * Cache the given hostname and addresses.
 */
private static void cacheAddresses(String hostname,
                                   InetAddress[] addresses,
                                   boolean success) {
    hostname = hostname.toLowerCase();
    synchronized (addressCache) {
        cacheInitIfNeeded();
        if (success) {
            addressCache.put(hostname, addresses);
        } else {
            negativeCache.put(hostname, addresses);
        }
    }
}

InetAddressCacheUtilForJdk8Minus类中对DNS Cache的读写也一致地加了以addressCache为锁的synchronized块,以保证线程安全。

需要测试不同版本JDK

本库实现使用了JDK的非公开API,不同JDK实现会不一样,即需要有兼容逻辑,并对不同版本JDK进行测试,以保证功能。

目前测试包含JDK版本参见【经过测试的JDK】一节。

📚 相关资料