Skip to content

Instantly share code, notes, and snippets.

@pfmiles
Created November 14, 2013 09:04
Show Gist options
  • Save pfmiles/7463710 to your computer and use it in GitHub Desktop.
Save pfmiles/7463710 to your computer and use it in GitHub Desktop.
Java应用本地内存dns cache更改、恢复示例程序; 用于参考实现java进程内部范围内的dns绑定功能(无需更改部署机器的dns绑定或dns服务器的配置)
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
public class GatewayUrgentDnsSwitchServiceImpl implements GatewayUrgentDnsSwitchService {
private static final String DNS_BIND_META_KEY = "gateway.local.dns.bindings";
private static Map<?, ?> dnsInnerCache;
private static int cacheRefreshTime;
private static Method bindDnsMethod;
private SystemMetaRegistry systemMetaRegistry;
private Thread switchingThread = null;
private boolean switched;
static {
try {
Field addrCache = InetAddress.class.getDeclaredField("addressCache");
addrCache.setAccessible(true);
Object addrCache0 = addrCache.get(null);
Field cache = addrCache0.getClass().getDeclaredField("cache");
cache.setAccessible(true);
dnsInnerCache = (Map<?, ?>) cache.get(addrCache0);
Class<?> cls = Class.forName("sun.net.InetAddressCachePolicy");
Method m = cls.getDeclaredMethod("get");
cacheRefreshTime = (Integer) m.invoke(null);
bindDnsMethod = InetAddress.class.getDeclaredMethod("cacheAddress", String.class, Object.class,
boolean.class);
bindDnsMethod.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean doSwitch() {
// 1.从系统元数据中获取dns绑定信息
String bind = this.systemMetaRegistry.getGlobalMeta(DNS_BIND_META_KEY);
if (StringUtil.isBlank(bind)) return false;
// 2.调用InetAddress的私有api完成绑定, 绑定信息格式为"domain1:ip1,domain2:ip2,...,domainN:ipN"
List<Pair<String, String>> bindings = extractBindings(bind);
return doBinding(bindings);
}
private synchronized boolean doBinding(List<Pair<String, String>> bindings) {
if (0 == cacheRefreshTime) {
// dns cache is disabled
OceanLog.system.error("DNS caching in java is disabled, could not do gateway local dns binding.");
return false;
} else if (-1 == cacheRefreshTime) {
// dns cache is permanent
for (Pair<String, String> b : bindings)
try {
bindDnsMethod.invoke(null, b.getLeft(), new InetAddress[] { resolveAddr(b) }, true);
} catch (Exception e) {
OceanLog.system.error("Bind local dns error.", e);
continue;
}
this.switched = true;
return true;
} else {
// amount of time, in seconds
if (this.switchingThread != null) return true;
switchingThread = new Thread(new DnsSwitchingTask(bindings, cacheRefreshTime * 1000 - 10),
"gateway-local-dns-switching-thread");
switchingThread.start();
this.switched = true;
return true;
}
}
private static InetAddress resolveAddr(Pair<String, String> nameAndIp) throws NumberFormatException,
UnknownHostException {
String[] ps = nameAndIp.getRight().split("\\.");
return InetAddress.getByAddress(nameAndIp.getLeft(), new byte[] { Byte.parseByte(ps[0]), Byte.parseByte(ps[1]),
Byte.parseByte(ps[2]), Byte.parseByte(ps[3]) });
}
private List<Pair<String, String>> extractBindings(String bind) {
// 格式: domain1:ip1,domain2:ip2,...,domainN:ipN
List<Pair<String, String>> ret = new ArrayList<Pair<String, String>>();
for (String p : bind.split(",")) {
String[] pp = p.split(":");
ret.add(new Pair<String, String>(pp[0].trim(), pp[1].trim()));
}
return ret;
}
public synchronized boolean doRecover() {
// 停止switchingThread
if (this.switchingThread != null) {
this.switchingThread.interrupt();
this.switchingThread = null;
}
// 清空dns cache, 策略:将所有binding entry的超时时间设置为当前已超时
long expiredTime = System.currentTimeMillis() - 1;
for (int i = 0; i < 5; i++) {// ConcurrentModificationException最多尝试5次
try {
if (-1 == cacheRefreshTime) {
dnsInnerCache.clear();
} else {
for (Object val : dnsInnerCache.values()) {
Field f = val.getClass().getDeclaredField("expiration");
f.setAccessible(true);
f.set(val, expiredTime);
}
}
this.switched = false;
return true;
} catch (ConcurrentModificationException ex) {
// try again
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
continue;
} catch (Exception e) {
OceanLog.system.error("Gateway local dns cache recover failed.", e);
return false;
}
}
return false;
}
public boolean checkIfSwitched() {
return this.switched;
}
public void setSystemMetaRegistry(SystemMetaRegistry systemMetaRegistry) {
this.systemMetaRegistry = systemMetaRegistry;
}
private static final class DnsSwitchingTask implements Runnable {
private List<Pair<String, String>> bindings;
private int interval;
public DnsSwitchingTask(List<Pair<String, String>> bindings, int interval){
this.bindings = bindings;
this.interval = interval;
}
public void run() {
while (!Thread.interrupted()) {
for (Pair<String, String> b : bindings)
try {
bindDnsMethod.invoke(null, b.getLeft(), new InetAddress[] { resolveAddr(b) }, true);
} catch (Exception e) {
OceanLog.system.error("Bind local dns error.", e);
continue;
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
break;
}
}
}
}
}
@pfmiles
Copy link
Author

pfmiles commented Nov 14, 2013

经实测,java1.6和1.7版本均适用

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment