Created
November 14, 2013 09:04
-
-
Save pfmiles/7463710 to your computer and use it in GitHub Desktop.
Java应用本地内存dns cache更改、恢复示例程序; 用于参考实现java进程内部范围内的dns绑定功能(无需更改部署机器的dns绑定或dns服务器的配置)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
经实测,java1.6和1.7版本均适用