forked from riemann/riemann-java-client
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
try to add a better localhost resolver. See issue riemann#44
- Loading branch information
Showing
5 changed files
with
249 additions
and
21 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
91 changes: 91 additions & 0 deletions
91
src/main/java/com/aphyr/riemann/client/LocalhostResolver.java
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package com.aphyr.riemann.client; | ||
|
||
import java.net.UnknownHostException; | ||
|
||
/** | ||
* A "Smarter" localhost resolver | ||
* see issue: https://github.com/aphyr/riemann-java-client/issues/44 | ||
* Trying to avoid a lot of calls to java.net.InetAddress.getLocalHost() | ||
* which under AWS trigger DNS resolving and have relatively high latency *per event* | ||
* usually, the hostname doesn't change so often to warrant a real query. | ||
* | ||
* A real call to java.net.InetAddress.getLocalHost().getHostName() | ||
* is made only if: | ||
* 1) the refresh interval has passed (=result is stale) | ||
* AND | ||
* 2) no env vars that identify the hostname are found | ||
*/ | ||
public class LocalhostResolver { | ||
|
||
// default hostname env var names on Win/Nix | ||
public static final String COMPUTERNAME = "COMPUTERNAME"; // Windows | ||
public static final String HOSTNAME = "HOSTNAME"; // Nix | ||
|
||
// how often should we refresh the cached hostname | ||
public static long RefreshIntervalMillis = 60 * 1000; | ||
// enables setting a custom env var used for resolving | ||
public static String CustomEnvVarName = null; | ||
|
||
// cached hostname result | ||
private static String hostname; | ||
|
||
// update (refresh) time management | ||
private static long lastUpdate = 0; | ||
private static long lastNetUpdate = 0; | ||
public static long getLastUpdateTime() { return lastUpdate; } | ||
public static long getLastNetUpdateTime() { return lastNetUpdate; } | ||
public static void resetUpdateTimes() { | ||
lastUpdate = 0; | ||
lastNetUpdate = 0; | ||
} | ||
|
||
/** | ||
* get resolved hostname. | ||
* encapsulates all lookup and caching logic. | ||
* | ||
* @return the hostname | ||
*/ | ||
public static String getResolvedHostname() { | ||
long now = System.currentTimeMillis(); | ||
if(now - RefreshIntervalMillis > lastUpdate) { | ||
refreshResolve(); | ||
} | ||
|
||
return hostname; | ||
} | ||
|
||
/** | ||
* forces a new resolve even if refresh interval has not passed yet | ||
*/ | ||
public static void refreshResolve() { | ||
try { | ||
hostname = resolveByEnv(); | ||
if(hostname == null || hostname.isEmpty()) { | ||
hostname = java.net.InetAddress.getLocalHost().getHostName(); | ||
lastNetUpdate = System.currentTimeMillis(); | ||
} | ||
} catch (UnknownHostException e) { | ||
//e.printStackTrace(); | ||
} | ||
finally { | ||
lastUpdate = System.currentTimeMillis(); | ||
} | ||
} | ||
|
||
/** | ||
* try to resolve the hostname by env vars | ||
* | ||
* @return | ||
*/ | ||
private static String resolveByEnv() { | ||
if(CustomEnvVarName != null) { | ||
return System.getenv(CustomEnvVarName); | ||
} | ||
|
||
if(System.getProperty("os.name").startsWith("Windows")) { | ||
return System.getenv(COMPUTERNAME); | ||
} | ||
|
||
return System.getenv(HOSTNAME); | ||
} | ||
} |
146 changes: 146 additions & 0 deletions
146
src/test/java/riemann/java/client/tests/LocalhostResolveTest.java
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package riemann.java.client.tests; | ||
|
||
import com.aphyr.riemann.client.LocalhostResolver; | ||
import junit.framework.Assert; | ||
import org.junit.Before; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
|
||
import java.lang.reflect.Field; | ||
import java.net.UnknownHostException; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class LocalhostResolveTest { | ||
|
||
protected Map<String, String> env = new HashMap<String, String>(); | ||
|
||
protected static final String OS_NAME_PROP = "os.name"; | ||
protected static final String ExpectedWinHostname = "LocalHostResolverTestWin"; | ||
protected static final String ExpectedNixHostname = "LocalHostResolverTestNix"; | ||
|
||
@BeforeClass | ||
public static void oneTimeSetUp() { | ||
LocalhostResolver.RefreshIntervalMillis = 1000; | ||
} | ||
|
||
@Before | ||
public void setup() { | ||
env = new HashMap<String, String>(System.getenv()); | ||
env.put(LocalhostResolver.HOSTNAME, ExpectedNixHostname); | ||
env.put(LocalhostResolver.COMPUTERNAME, ExpectedWinHostname); | ||
setEnv(env); | ||
|
||
LocalhostResolver.CustomEnvVarName = null; | ||
LocalhostResolver.resetUpdateTimes(); | ||
Assert.assertEquals(0, LocalhostResolver.getLastUpdateTime()); | ||
Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime()); | ||
} | ||
|
||
@Test | ||
public void testUpdateInterval() { | ||
Assert.assertEquals(0, LocalhostResolver.getLastUpdateTime()); | ||
String hostname = LocalhostResolver.getResolvedHostname(); | ||
long lastUpdateTime = LocalhostResolver.getLastUpdateTime(); | ||
Assert.assertNotNull(hostname); | ||
try { | ||
Thread.sleep(1500); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
Assert.assertNotSame(lastUpdateTime, LocalhostResolver.getLastUpdateTime()); | ||
} | ||
|
||
@Test | ||
public void testNoEnvVars() throws UnknownHostException { | ||
env.remove(LocalhostResolver.HOSTNAME); | ||
env.remove(LocalhostResolver.COMPUTERNAME); | ||
setEnv(env); | ||
|
||
String hostname = LocalhostResolver.getResolvedHostname(); | ||
Assert.assertNotNull(hostname); | ||
|
||
try { | ||
Thread.sleep(1500); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
// ensure queried hostname without env vars | ||
Assert.assertEquals(LocalhostResolver.getLastUpdateTime(), LocalhostResolver.getLastNetUpdateTime()); | ||
Assert.assertEquals(java.net.InetAddress.getLocalHost().getHostName(), hostname); | ||
} | ||
|
||
@Test | ||
public void testEnvVarWindows() { | ||
System.getProperties().put(OS_NAME_PROP, "Windows7"); | ||
|
||
String hostname = LocalhostResolver.getResolvedHostname(); | ||
Assert.assertEquals(ExpectedWinHostname, hostname); | ||
Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime()); | ||
} | ||
|
||
@Test | ||
public void testEnvVarNix() { | ||
System.getProperties().put(OS_NAME_PROP, "Linux"); | ||
String hostname = LocalhostResolver.getResolvedHostname(); | ||
Assert.assertEquals(ExpectedNixHostname, hostname); | ||
Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime()); | ||
} | ||
|
||
@Test | ||
public void testCustomEnvVar() { | ||
final String customHostnameEnvVar = "AWS_HOST"; | ||
final String customHostname = "EC2-LocalHostResolverTest"; | ||
env.put(customHostnameEnvVar, customHostname); | ||
setEnv(env); | ||
|
||
LocalhostResolver.CustomEnvVarName = customHostnameEnvVar; | ||
String hostname = LocalhostResolver.getResolvedHostname(); | ||
Assert.assertEquals(customHostname, hostname); | ||
Assert.assertEquals(0, LocalhostResolver.getLastNetUpdateTime()); | ||
} | ||
|
||
|
||
/** | ||
* evil hack for testing (only!) with env var in-memory modification | ||
* see: http://stackoverflow.com/a/7201825/1469004 | ||
* | ||
* @param newEnv - to set in memory | ||
*/ | ||
protected static void setEnv(Map<String, String> newEnv) { | ||
try { | ||
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); | ||
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment"); | ||
theEnvironmentField.setAccessible(true); | ||
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null); | ||
env.putAll(newEnv); | ||
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment"); | ||
theCaseInsensitiveEnvironmentField.setAccessible(true); | ||
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null); | ||
cienv.putAll(newEnv); | ||
} | ||
catch (NoSuchFieldException e) { | ||
try { | ||
Class[] classes = Collections.class.getDeclaredClasses(); | ||
Map<String, String> env = System.getenv(); | ||
for(Class cl : classes) { | ||
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { | ||
Field field = cl.getDeclaredField("m"); | ||
field.setAccessible(true); | ||
Object obj = field.get(env); | ||
Map<String, String> map = (Map<String, String>) obj; | ||
map.clear(); | ||
map.putAll(newEnv); | ||
} | ||
} | ||
} catch (Exception e2) { | ||
e2.printStackTrace(); | ||
} | ||
} catch (Exception e1) { | ||
e1.printStackTrace(); | ||
} | ||
} | ||
} |