Java自定义DNS解析器负载均衡实现

在上文JAVA自定义DNS解析器实践中,我们没有讲到org.Apache.http.conn.DnsResolver具体如何实现负载均衡,今天我们就分享一下,负载均衡的具体实现 。
InMemoryDnsResolver被淘汰首先上期文章提到的org.apache.http.impl.conn.InMemoryDnsResolver类是无法实现负载均衡的,原因是这个实现类是将host和IP存在一个java.util.concurrent.ConcurrentHashMap中,然后解析的时候从java.util.concurrent.ConcurrentHashMap根据host获取到IP的,所以无法进行负载均衡 。
使用的Demo如下:
/*** 重写Java自定义DNS解析器,非负载均衡** @return*/private static DnsResolver getDnsResolver2() {InMemoryDnsResolver dnsResolver = new InMemoryDnsResolver();try {logger.warn("调用一次");dnsResolver.add("fun.tester", InetAddress.getByName("127.0.0.1"));} catch (Exception e) {e.printStackTrace();}return dnsResolver;其中org.apache.http.impl.conn.InMemoryDnsResolver#add方法源码如下:
public void add(String host, InetAddress... ips) {Args.notNull(host, "Host name");Args.notNull(ips, "Array of IP addresses");this.dnsMap.put(host, ips);}然后我们看一下org.apache.http.impl.conn.InMemoryDnsResolver#dnsMap相关初始化代码:
/*** In-memory collection that will hold the associations between a host name* and an array of InetAddress instances.*/private final Map<String, InetAddress[]> dnsMap;/*** Builds a DNS resolver that will resolve the host names against a* collection held in-memory.*/public InMemoryDnsResolver() {dnsMap = new ConcurrentHashMap<String, InetAddress[]>();}SystemDefaultDnsResolver最终我放弃了自定义的org.apache.http.conn.DnsResolver接口的方案,选择了org.apache.http.impl.conn.SystemDefaultDnsResolver重写resolve方法的方案,具体实现如下:
/*** 重写Java自定义DNS解析器,负载均衡** @return*/private static DnsResolver getDnsResolver() {return new SystemDefaultDnsResolver() {@Overridepublic InetAddress[] resolve(final String host) throws UnknownHostException {if (host.equalsIgnoreCase("fun.tester")) {return new InetAddress[]{SourceCode.random(ips)};} else {return super.resolve(host);}}};}其中ips是全局的静态变量,初始化方法如下:
/*** 初始化DNS配置IP** @return*/private static List<InetAddress> getAddress() {try {return Arrays.asList(InetAddress.getByName("127.0.0.1"),InetAddress.getByName("0.0.0.0"));} catch (Exception e) {FailException.fail("DNS IP解析失败!");}return null;}PS:如果你选择使用了自定义的DNS解析器,那么系统hosts配置的功能就会失效,所以谨慎使用 。
测试为了验证结果,我对com.funtester.httpclient.ClientManage#getDnsResolver方法进行了改造,每次获取到IP的时候我都打印出来 。
/*** 重写Java自定义DNS解析器,负载均衡** @return*/private static DnsResolver getDnsResolver() {return new SystemDefaultDnsResolver() {@Overridepublic InetAddress[] resolve(final String host) throws UnknownHostException {if (host.equalsIgnoreCase("fun.tester")) {InetAddress random = SourceCode.random(ips);logger.info(random);return new InetAddress[]{random};} else {return super.resolve(host);}}};}单线程下面看我的测试,首先分享测试用例:
public static void main(String[] args) {String url = "http://fun.tester:12345/"def get = getHttpGet(url)def test = {getHttpResponse(get)}10.times {test()}}控制台输出:
INFO-> 13.691 main###### #### ####### ################## ############################### ########################### #####################################INFO-> 14.408 main /0.0.0.0INFO-> 14.460 main 请求uri:http://fun.tester:12345/ , 耗时:451 ms , HTTPcode: 200INFO-> 14.462 main 请求uri:http://fun.tester:12345/ , 耗时:2 ms , HTTPcode: 200****省略多余的内容****可以看出,单线程请求HTTP服务,DNS只会解析一次,经过多次尝试,解析的IP会在设定的两个IP之间随机出现,但这明显不符合我们的需求 。
多线程测试用例如下:
public static void main(String[] args) {String url = "http://fun.tester:12345/"def get = getHttpGet(url)def test = {fun {getHttpResponse(get)}}10.times {test()}}控制台输出:
INFO-> 03.636 main###### #### ####### ################## ############################### ########################### #####################################INFO-> 04.581 Deamon 守护线程开启!INFO-> 04.843 F-6/0.0.0.0INFO-> 04.843 F-4/127.0.0.1INFO-> 04.843 F-7/0.0.0.0INFO-> 04.844 F-2/0.0.0.0INFO-> 04.844 F-10 /0.0.0.0INFO-> 04.844 F-1/0.0.0.0INFO-> 04.844 F-5/127.0.0.1INFO-> 04.844 F-3/127.0.0.1INFO-> 04.844 F-8/0.0.0.0INFO-> 04.844 F-9/127.0.0.1INFO-> 04.903 F-7请求uri:http://fun.tester:12345/ , 耗时:309 ms , HTTPcode: 200INFO-> 04.903 F-3请求uri:http://fun.tester:12345/ , 耗时:309 ms , HTTPcode: 200INFO-> 04.903 F-2请求uri:http://fun.tester:12345/ , 耗时:309 ms , HTTPcode: 200****省略多余的内容****


推荐阅读