JDK中DNS缓存的分析

在JAVA中使用InetAddress.getByName(String host) 方法来获取给定hostname的IP地址。为了减少DNS解析的请求次数,提高解析效率,InetAddress中提供cache来缓存解析结果。

下面就此cache进行简单的分析:

该缓存实现比较简单,巧妙的利用LinkedHashMap的特性来进行过期条目的检测和移除。

    /**
     * Represents a cache entry
     */
    static final class CacheEntry {

        CacheEntry(Object address, long expiration) {
            this.address = address;
            this.expiration = expiration;
        }

        Object address;
        long expiration;
    }


//CacheEntry实例代表一个缓存记录,一个记录中包括address(IP 地址) 和 expiration

    /**
     * A cache that manages entries based on a policy specified
     * at creation time.
     */
    static final class Cache {
    private LinkedHashMap cache;
    private Type type;

    enum Type {Positive, Negative};//此缓存只提供两种缓存类型 Positive: DNS解析成功    Negative:DNS解析失败

    /**
     * Create cache
     */
    public Cache(Type type) {
        this.type = type;
        cache = new LinkedHashMap();//LinkedHashMap 保存了记录的插入顺序,当遍历LindedHashMap时得到的数据是最先插入的数据,此特性很重要在put方法中有所体现
    }

    private int getPolicy() {//获取配置的缓存策略   0: 不缓存  -1: 代表永久缓存  >=1:代表缓存的时间(unit: second)
        if (type == Type.Positive) {
        return InetAddressCachePolicy.get();
        } else {
        return InetAddressCachePolicy.getNegative();
        }
    }

    /**
     * Add an entry to the cache. If there's already an
     * entry then for this host then the entry will be
     * replaced.
     */
    public Cache put(String host, Object address) {
        int policy = getPolicy();
        if (policy == InetAddressCachePolicy.NEVER) {
                return this;
        }

        // purge any expired entries

        if (policy != InetAddressCachePolicy.FOREVER) {

        // As we iterate in insertion order we can
        // terminate when a non-expired entry is found.
                LinkedList expired = new LinkedList();
                Iterator i = cache.keySet().iterator();//每次put的时候都对缓存记录做一个清理,由于每个条目的过期时间是一样的,所以先插入的记录就先到期
        long now = System.currentTimeMillis();
                while (i.hasNext()) {
                    String key = (String)i.next();
                    CacheEntry entry = (CacheEntry)cache.get(key);

                    if (entry.expiration >= 0 && entry.expiration < now) {
                        expired.add(key);
                    } else {
                        break;
                    }
                }

                i = expired.iterator();
                while (i.hasNext()) {
                    cache.remove(i.next());
        }
            }

        // create new entry and add it to the cache
        // -- as a HashMap replaces existing entries we
        //    don't need to explicitly check if there is
        //    already an entry for this host.
        long expiration;
        if (policy == InetAddressCachePolicy.FOREVER) {
        expiration = -1;
        } else {
        expiration = System.currentTimeMillis() + (policy * 1000);
        }
        CacheEntry entry = new CacheEntry(address, expiration);
        cache.put(host, entry);
        return this;
    }

    /**
     * Query the cache for the specific host. If found then
     * return its CacheEntry, or null if not found.
     */
    public CacheEntry get(String host) {
        int policy = getPolicy();
        if (policy == InetAddressCachePolicy.NEVER) {
        return null;
        }
        CacheEntry entry = (CacheEntry)cache.get(host);

        // check if entry has expired
        if (entry != null && policy != InetAddressCachePolicy.FOREVER) {//命中缓存条目后先判断是否过期
        if (entry.expiration >= 0 &&
            entry.expiration < System.currentTimeMillis()) {
            cache.remove(host);
            entry = null;
        }
        }

        return entry;
    }
    }

 

posted @ 2014-05-04 15:08  cruze_lee  阅读(1422)  评论(0编辑  收藏  举报