Cache Type
DNS的缓存类型分为两种:Positive和Negative,Positive用于缓存可以正常解析的域名,Negative用于缓存无法解析的域名。
enum Type {Positive, Negative};
InetAddressCachePolicy
InetAddressCachePolicy(sun.net.InetAddressCachePolicy),表示缓存策略,这里特指缓存过期时间,
private static int cachePolicy = -1;
private static int negativeCachePolicy = 0;
public static synchronized int get() {
return cachePolicy;
}
public static synchronized int getNegative() {
return negativeCachePolicy;
}
- cachePolicy,表示Positive缓存的过期时间,通过方法get()获取;
- negativeCachePolicy,表示Negative缓存的过期时间,通过getNegative()获取;
cachePolicy和negativeCachePolicy都是静态变量,它们的数值也是通过静态代码块初始化的。
cachePolicy
Integer var0 = (Integer)AccessController.doPrivileged(new PrivilegedAction<Integer>() {
public Integer run() {
String var1;
try {
var1 = Security.getProperty("networkaddress.cache.ttl");
if (var1 != null) {
return Integer.valueOf(var1);
}
} catch (NumberFormatException var3) {
;
}
try {
var1 = System.getProperty("sun.net.inetaddr.ttl");
if (var1 != null) {
return Integer.decode(var1);
}
} catch (NumberFormatException var2) {
;
}
return null;
}
});
if (var0 != null) {
cachePolicy = var0;
if (cachePolicy < 0) {
cachePolicy = -1;
}
propertySet = true;
} else if (System.getSecurityManager() == null) {
cachePolicy = 30;
}
cachePolicy取值来源于两个配置属性:
- networkaddress.cache.ttl(优先级高)
- sun.net.inetaddr.ttl(优先级低)
如果配置属性的值小于0,则cachePolicy为-1;如果配置属性没有被设置,则cachePolicy为30。也就是说,对于正常解析的域名,默认的缓存时间为30s。
注:-1为永久缓存。
negativeCachePolicy
var0 = (Integer)AccessController.doPrivileged(new PrivilegedAction<Integer>() {
public Integer run() {
String var1;
try {
var1 = Security.getProperty("networkaddress.cache.negative.ttl");
if (var1 != null) {
return Integer.valueOf(var1);
}
} catch (NumberFormatException var3) {
;
}
try {
var1 = System.getProperty("sun.net.inetaddr.negative.ttl");
if (var1 != null) {
return Integer.decode(var1);
}
} catch (NumberFormatException var2) {
;
}
return null;
}
});
if (var0 != null) {
negativeCachePolicy = var0;
if (negativeCachePolicy < 0) {
negativeCachePolicy = -1;
}
propertyNegativeSet = true;
}
negativeCachePolicy取值来源于两个配置属性:
- networkaddress.cache.negative.ttl(优先级高)
- sun.net.inetaddr.negative.ttl(优先级低)
如果配置属性的值小于0,则negativeCachePolicy为-1;如果配置属性的值没有被设置,则negativeCachePolicy为0。也就是说,对于无法解析的域名,默认的缓存时间为0s,即不缓存。
CacheEntry
CacheEntry(java.net.InetAddress.CacheEntry),表示一个缓存对象,包含两个实例属性:
- addresses ,IP地址,一个域名可以对应着多个IP地址)
- expiration,过期时间;
/**
* Represents a cache entry
*/
static final class CacheEntry {
CacheEntry(InetAddress[] addresses, long expiration) {
this.addresses = addresses;
this.expiration = expiration;
}
InetAddress[] addresses;
long expiration;
}
Cache
Cache(java.net.InetAddress.Cache),表示一个缓存实例,包含两个实例属性:
- cache,缓存的具体实现,实际就是一个HashMap(LinkedHashMap);
- type,缓存类型,Positive或Negative;
private LinkedHashMap<String, CacheEntry> cache;
private Type type;
put
- 根据缓存类型,获取相应的缓存时间;如果缓存时间为0s,则put直接退出即可;
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<String> expired = new LinkedList<>();
long now = System.currentTimeMillis();
for (String key : cache.keySet()) {
CacheEntry entry = cache.get(key);
if (entry.expiration >= 0 && entry.expiration < now) {
expired.add(key);
} else {
break;
}
}
for (String key : expired) {
cache.remove(key);
}
逐个遍历缓存中的对象(entry),判断是否过期;如果已过期(entry.expiration < now),则将域名加入到过期列表(expired);遍历完成之后,将过期列表中的域名统一从缓存中清除。
- 计算对象对象过期时间(expiration),将对象存入缓存;
long expiration;
if (policy == InetAddressCachePolicy.FOREVER) {
expiration = -1;
} else {
expiration = System.currentTimeMillis() + (policy * 1000);
}
CacheEntry entry = new CacheEntry(addresses, expiration);
cache.put(host, entry);
get
- 根据缓存类型,获取相应的缓存时间;如果缓存时间为0s,则get直接返回null即可;
int policy = getPolicy();
if (policy == InetAddressCachePolicy.NEVER) {
return null;
}
- 通过域名(host)获取相应的缓存对象(IP地址);
CacheEntry entry = cache.get(host);
- 判断缓存对象是否过期;如果已过期,需要从缓存中移出(域名指向可能已发生变化),并返回null;
// check if entry has expired
if (entry != null && policy != InetAddressCachePolicy.FOREVER) {
if (entry.expiration >= 0 &&
entry.expiration < System.currentTimeMillis()) {
cache.remove(host);
entry = null;
}
}
InetAddress getByName
InetAddress.getByName底层域名解析实际调用的方法为InetAddress.getAllByName0,主要包括两个核心逻辑:
- getCachedAddresses,从缓存中获取指定域名的IP地址;
- getAddressesFromNameService,如果缓存中没有获取到指定域名的IP地址,则通过DNS服务来解析;
通过DNS服务解析域名之后,无论是否能够正常解析,均需要更新缓存:
// Cache the address.
cacheAddresses(host, addresses, success);
/*
* 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);
}
}
}
注:无法正常解析的域名,IP地址被缓存为unknown_array。
网友评论