(二)优化方案的验证

一、设置正向缓存过期时间

(待续)

二、添加自定义的NameService

先上实现步骤:

1. 在META-INF/services下创建一个sun.net.spi.nameservice.NameServiceDescriptor文件
2. 在文件内的一行写上自定义dns服务发现类的全限定类名
3. 自定义dns实现类重写NameServiceDescriptor接口的三个方法,getProviderName返回"XYDnsNameService",getType返回"dns",createNameService返回一个自定义NameService实现类的实例。
4. 自定义XYDnsNameService实现NameService接口的三个方法。
5. 通过代码设置自定义nameservice为默认dns解析器:System.setProperty("sun.net.spi.nameservice.provider.1", "dns,XYDnsNameService");

下面通过源码分析:
当我们使用默认dns解析百度的域名时,方法调用栈如下

 InetAddress.getByName(“www.baidu.com”)
 InetAddress.getAllByName(“www.baidu.com”)[0]
 InetAddress.getAllByName(“www.baidu.com”, null)
 InetAddress.getAllByName0(“www.baidu.com”, null, true)
 InetAddress.getCachedAddress(“www.baidu.com”)

InetAddress.getCachedAddress(“www.baidu.com”) 从缓存中查不到对应的数据时,就会执行下面的语句

private static InetAddress[] getAllByName0 (String host, InetAddress reqAddr, boolean check)
        throws UnknownHostException  {
    ........
        if (addresses == null) {
            addresses = getAddressesFromNameService(host, reqAddr);
        }
    .........
  
        return addresses.clone();

再看看getAddressesFromNameService方法

 /* Used to store the name service provider */
    private static List nameServices = null;

for (NameService nameService : nameServices) {
    try {
 
        addresses = nameService.lookupAllHostAddr(host);
        success = true;
        break;
    } catch (UnknownHostException uhe) {
        if (host.equalsIgnoreCase("localhost")) {
            InetAddress[] local = new InetAddress[] { impl.loopbackAddress() };
            addresses = local;
            success = true;
            break;
        }
        else {
            addresses = unknown_array;
            success = false;
            ex = uhe;
        }
    }
}

getAddressesFromNameService方法遍历nameService(类静态属性),调用nameService的lookupAllHostAddr对域名进行解析。这意味着我们可以通过添加自定义的nameService并重写其lookupAllHostAddr方法来实现自定义的dns解析逻辑

那么,现在的问题就是向类属性nameService添加自定义nameService

下面的静态代码块的作用是初始化nameService

  static {
        // create the impl
        impl = InetAddressImplFactory.create();

        // get name service if provided and requested
        String provider = null;;
        String propPrefix = "sun.net.spi.nameservice.provider.";
        int n = 1;
        nameServices = new ArrayList();
       // 从系统属性属性中获取key为sun.net.spi.nameservice.provider.x的配置的值
        provider = AccessController.doPrivileged(
                new GetPropertyAction(propPrefix + n));
      //值不为空
        while (provider != null) {
            
            NameService ns = createNSProvider(provider);
            if (ns != null)
                nameServices.add(ns);

            n++;
            provider = AccessController.doPrivileged(
                    new GetPropertyAction(propPrefix + n));
        }

        // if not designate any name services provider,
        // create a default one
        if (nameServices.size() == 0) {
            NameService ns = createNSProvider("default");
            nameServices.add(ns);
        }
    }

可以看出,如何系统配置sun.net.spi.nameservice.provider.x(x >= 1)不为空的话,就会执行下面的createNSProvider方法

//createNSProvider方法中
 final String providerName = provider;
try {
                nameService = java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedExceptionAction() {
                        public NameService run() {
                            Iterator itr =
                                // spi加载 META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor里写的全限定包名的类的接口实现类
                                ServiceLoader.load(NameServiceDescriptor.class的实现类.class)
                                    .iterator();
                            while (itr.hasNext()) {
                                // 如果providerName和实现类的getType+,+getProviderName拼接的字符串一致时,调用该实现类的createNameService方法创建一个NameService实例。
                                NameServiceDescriptor nsd = itr.next();
                                if (providerName.
                                    equalsIgnoreCase(nsd.getType()+","
                                        +nsd.getProviderName())) {
                                    try {
                                        return nsd.createNameService();
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        System.err.println(
                                            "Cannot create name service:"
                                             +providerName+": " + e);
                                    }
                                }
                            }

                            return null;
                        }
                    }

所以我们在设置System.setProperty("sun.net.spi.nameservice.provider.1", "dns,xxxDnsNameService")后,xxxNameServiceDescriptor的getProviderName方法返回"xxxDnsNameService",getType方法返回"dns"。createNameService方法放回一个自定义的NameService实例,这样就实现了自定义dns NameService

你可能感兴趣的:(java-dns)