您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

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

时间:2022-02-11 10:00:54  来源:  作者:FunTester

在上文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() {
            @Override
            public 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() {
            @Override
            public 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.0
INFO-> 14.460 main 请求uri:http://fun.tester:12345/ , 耗时:451 ms , HTTPcode: 200
INFO-> 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.0
INFO-> 04.843 F-4  /127.0.0.1
INFO-> 04.843 F-7  /0.0.0.0
INFO-> 04.844 F-2  /0.0.0.0
INFO-> 04.844 F-10 /0.0.0.0
INFO-> 04.844 F-1  /0.0.0.0
INFO-> 04.844 F-5  /127.0.0.1
INFO-> 04.844 F-3  /127.0.0.1
INFO-> 04.844 F-8  /0.0.0.0
INFO-> 04.844 F-9  /127.0.0.1

INFO-> 04.903 F-7  请求uri:http://fun.tester:12345/ , 耗时:309 ms , HTTPcode: 200
INFO-> 04.903 F-3  请求uri:http://fun.tester:12345/ , 耗时:309 ms , HTTPcode: 200
INFO-> 04.903 F-2  请求uri:http://fun.tester:12345/ , 耗时:309 ms , HTTPcode: 200
****省略多余的内容****

这下我们就能看出每个线程都执行了一次org.apache.http.impl.conn.SystemDefaultDnsResolver#resolve方法,获取到了IP也是随机的,而且每次请求的耗时都是比较长的。这里让我心生疑惑,相当于每个线程请求都是重新重建了连接,于是就有了下面的测试。

单个连接

这里我把HttpClient的连接池的最大连接数改成了1:public static int MAX_PER_ROUTE_CONNECTION = 1;或者public static int MAX_TOTAL_CONNECTION = 1;,这个之前分享过,这里不多讲了,上用例:

用例同多线程用例

控制台输出:

INFO-> 02.928 main 
  ###### #     #  #    # ####### ######  #####  ####### ######  #####
  #      #     #  ##   #    #    #       #         #    #       #    #
  ####   #     #  # #  #    #    ####    #####     #    ####    #####
  #      #     #  #  # #    #    #            #    #    #       #   #
  #       #####   #    #    #    ######  #####     #    ######  #    #

INFO-> 03.648 Deamon 守护线程开启!
INFO-> 03.910 F-5  /0.0.0.0
INFO-> 03.961 F-6  请求uri:http://fun.tester:12345/ , 耗时:299 ms , HTTPcode: 200
INFO-> 03.961 F-5  请求uri:http://fun.tester:12345/ , 耗时:299 ms , HTTPcode: 200
INFO-> 03.961 F-4  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-7  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-3  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-2  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-1  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-9  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-8  请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
INFO-> 03.961 F-10 请求uri:http://fun.tester:12345/ , 耗时:300 ms , HTTPcode: 200
WARN-> 04.673 Deamon 异步线程池关闭!

这里看到虽然我起了10个线程分别执行请求,但是每个请求的耗时都是非常长的,但是只有F-5这个线程执行了一次org.apache.http.impl.conn.SystemDefaultDnsResolver#resolve方法,由于HttpClient只有一个连接。所以应当是每个连接创建的时候会调用org.apache.http.impl.conn.SystemDefaultDnsResolver#resolve方法,而每个线程请求耗时比较高,原因是因为每个线程去获取到链接资源之后,会重新进行建联的过程导致的。

实践出真知,奇怪的知识又增加了。



Tags:DNS解析   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
在上文Java自定义DNS解析器实践中,我们没有讲到org.apache.http.conn.DnsResolver具体如何实现负载均衡,今天我们就分享一下,负载均衡的具体实现。InMemoryDnsResolver被淘汰首...【详细内容】
2022-02-11  Tags: DNS解析  点击:(0)  评论:(0)  加入收藏
由于IP地址信息不太好记忆,所以网络中出现了域名这个名字,在访问时我们这需要输入这个好记忆的域名即可,网络中会存在着自动将相应的域名解析成IP地址的服务器,这就是Dns服务器...【详细内容】
2022-01-10  Tags: DNS解析  点击:(44)  评论:(0)  加入收藏
要想深入理解DNS解析的过程,我们需要知道一些底层的东西和一些周边的知识,本篇文章会围绕这些来构建。名词解释前置知识DNS域名系统DNS(Domain Name System)是因特网使用的命...【详细内容】
2020-08-20  Tags: DNS解析  点击:(90)  评论:(0)  加入收藏
概述DNS(Domain Name System,域名系统)DNS的作用,简单的说:就是把我们输入的网站域名翻译成IP地址的系统。本文建立在已搭建好DNS服务器,这里讨论为linux机器配置DNS服务器,使用户...【详细内容】
2020-06-04  Tags: DNS解析  点击:(67)  评论:(0)  加入收藏
每天都浏览的一个网站突然间打不开了,明明网络没有问题,打开其它的网站都是正常的,为什么就突然打不开了呢?电脑为什么打不开指定的网站?什么是DNS解析我要如何设置DNS这时候我们...【详细内容】
2020-03-10  Tags: DNS解析  点击:(130)  评论:(0)  加入收藏
大致就是:浏览器输入地址,然后浏览器这个进程去调操作系统某个库里的gethostbyname函数(例如,Linux GNU glibc标准库的gethostbyname函数),然后呢这个函数通过网卡给DNS服务器发UDP请求,接收结果,然后将结果给返回给浏览器...【详细内容】
2019-09-25  Tags: DNS解析  点击:(226)  评论:(0)  加入收藏
Nginx怎么做域名解析?怎么在你自己开发的模块里面使用Nginx提供的方法解析域名?它内部实现是什么样的?本文以Nginx 1.5.1为例,从nginx_mail_smtp模块如何进行域名解析出发,分析Ng...【详细内容】
2019-08-26  Tags: DNS解析  点击:(202)  评论:(0)  加入收藏
众所周知,DNS服务是用来将主机名和域名转换为IP地址的服务,在互联网中必须使用DNS服务的主要原因有以下两个:一、主机名和域名相对IP地址来说,更容易记忆;二、一个主机和域名可能...【详细内容】
2019-08-02  Tags: DNS解析  点击:(742)  评论:(0)  加入收藏
▌简易百科推荐
在上文Java自定义DNS解析器实践中,我们没有讲到org.apache.http.conn.DnsResolver具体如何实现负载均衡,今天我们就分享一下,负载均衡的具体实现。InMemoryDnsResolver被淘汰首...【详细内容】
2022-02-11  FunTester    Tags:DNS解析   点击:(0)  评论:(0)  加入收藏
Java 中的字节码,英文名为 bytecode, 是 Java 代码编译后的中间代码格式。JVM 需要读取并解析字节码才能执行相应的任务。从技术人员的角度看,Java 字节码是 JVM 的指令集。JV...【详细内容】
2022-02-10  记得要让着本宝宝    Tags:Java   点击:(4)  评论:(0)  加入收藏
日志是发现错误和调试代码的便捷工具。除了日志的功能方面,从 Java 安全的角度来看,日志也很重要, 当发生安全漏洞时,你的日志文件是寻找所发生事件线索的第一个位置。 日志的质...【详细内容】
2022-01-26  粤嵌教育    Tags:Java 日志   点击:(15)  评论:(0)  加入收藏
和软件的MACD的值进行对比过,数值是一样的,不过我这个版本小数点后面更精确,软件的是四舍五入的。这个版本支持增量更新,更加方便。使用需要单例模式,如果多并发请自行修改代码。...【详细内容】
2022-01-20  吴彬的分享    Tags:JAVA   点击:(24)  评论:(0)  加入收藏
1、阿里云DNS的SDK依赖<dependency> <groupId>com.aliyun</groupId> <artifactId>tea-openapi</artifactId> <version>0.0.19</version></dependency><dependency> <groupId...【详细内容】
2022-01-19  顶顶架构师    Tags:阿里云DNS   点击:(19)  评论:(0)  加入收藏
Maven是Java的项目配置管理工具,用来管理依赖,具体的用途就不展开说了。大部分项目,配置一个镜像仓库地址就可以了(单个mirror)。但是有的网上下载的项目需要从多个仓库查找对应...【详细内容】
2022-01-14  阿福ChrisYuan    Tags:Maven配置   点击:(19)  评论:(0)  加入收藏
闰年闰年(Leap Year)是为了弥补因人为历法规定造成的年度天数与地球实际公转周期的时间差而设立的。补上时间差的年份为闰年。闰年共有366天(1月~12月分别为31天、29天、31天、...【详细内容】
2022-01-11  3班的黄同学    Tags:   点击:(24)  评论:(0)  加入收藏
目录4、TCP网络传输的基本流程二、网络编程套接字(socket)5、cookie和session的用法6、基本实现http协议的代码四、传输层协议TCP和UDP4、TCP和UDP之间的对比六、数据链路层和...【详细内容】
2022-01-04  顶顶架构师    Tags:JAVA   点击:(33)  评论:(0)  加入收藏
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  顶顶架构师    Tags:面向对象   点击:(37)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(56)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条