From 1310e2d41bdad59128abad3edc91f673e457f2ce Mon Sep 17 00:00:00 2001 From: Woonduk Kang Date: Tue, 8 Jan 2013 01:48:30 +0000 Subject: [PATCH] =?UTF-8?q?[=EA=B0=95=EC=9A=B4=EB=8D=95]=20[LUCYSUS-1744]?= =?UTF-8?q?=20sql=EC=9D=98=20meta=20data=EB=A5=BC=20=EB=94=B0=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=9D=BC=EB=B6=80=20util=EB=B0=8F=20agent?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=BD=94=EB=93=9C=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-tomcat-profiler/trunk@1081 84d0f5b1-2673-498c-a247-62c4ff18d310 --- src/main/java/com/profiler/Agent.java | 17 +++- .../java/com/profiler/TomcatProfiler.java | 7 +- .../com/profiler/metadata/SqlCacheTable.java | 78 +++++++++++++++++++ .../java/com/profiler/metadata/SqlObject.java | 40 ++++++++++ .../db/mysql/MySQLStatementModifier.java | 7 +- .../profiler/metadata/SqlCacheTableTest.java | 50 ++++++++++++ 6 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/profiler/metadata/SqlCacheTable.java create mode 100644 src/main/java/com/profiler/metadata/SqlObject.java create mode 100644 src/test/java/com/profiler/metadata/SqlCacheTableTest.java diff --git a/src/main/java/com/profiler/Agent.java b/src/main/java/com/profiler/Agent.java index 20f209c7f..ac2c76025 100644 --- a/src/main/java/com/profiler/Agent.java +++ b/src/main/java/com/profiler/Agent.java @@ -33,6 +33,7 @@ public class Agent { private final String agentId; private final String nodeName; private final String applicationName; + private final long startTime; public Agent(ProfilerConfig profilerConfig) { Assert.notNull(profilerConfig, "profilerConfig must not be null"); @@ -44,10 +45,12 @@ public class Agent { // 일단 임시로 호환성을 위해 agentid에 머신name을넣도록 하자 String machineName = NetworkUtils.getMachineName(); this.agentId = getId("hippo.agentId", machineName); + // TODO node name의 string limit 제한을 해결해야 된다. this.nodeName = getId("hippo.nodeName", machineName); this.applicationName = getId("hippo.applicationName", "UnknownApplicationName"); this.dataSender = createDataSender(); + this.startTime = System.currentTimeMillis(); initializeTraceContext(); @@ -93,7 +96,7 @@ public class Agent { logger.warning(idName + " is too long(1~24). value=" + id); } // validate = false; - // TODO 이거 후처리를 어떻게 해야 될지. agent를 시작 시키지 않아야 될거 같은데. lifecycle이 이쪽저쪽에 퍼져 있어서 일관된 stop에 문제가 있음.. + // TODO 이제 그냥 exception을 던지면 됨 agent 생성 타이밍이 최초 vm스타트와 동일하다. } catch (UnsupportedEncodingException e) { logger.log(Level.WARNING, "invalid agentId. Cause:" + e.getMessage(), e); } @@ -125,6 +128,10 @@ public class Agent { return agentId; } + public long getStartTime() { + return startTime; + } + public String getApplicationName() { return applicationName; } @@ -153,8 +160,9 @@ public class Agent { agentInfo.setPorts(ports); agentInfo.setAgentId(getAgentId()); agentInfo.setApplicationName(getApplicationName()); + agentInfo.setIsAlive(true); - agentInfo.setTimestamp(System.currentTimeMillis()); + agentInfo.setTimestamp(this.startTime); this.dataSender.send(agentInfo); } @@ -177,11 +185,12 @@ public class Agent { agentInfo.setHostname(ip); agentInfo.setPorts(ports); - agentInfo.setIsAlive(false); - agentInfo.setTimestamp(System.currentTimeMillis()); agentInfo.setAgentId(getAgentId()); agentInfo.setApplicationName(getApplicationName()); + agentInfo.setIsAlive(false); + agentInfo.setTimestamp(this.startTime); + this.dataSender.send(agentInfo); // 종료 처리 필요. this.dataSender.stop(); diff --git a/src/main/java/com/profiler/TomcatProfiler.java b/src/main/java/com/profiler/TomcatProfiler.java index cac4f688f..89a1cf546 100644 --- a/src/main/java/com/profiler/TomcatProfiler.java +++ b/src/main/java/com/profiler/TomcatProfiler.java @@ -114,6 +114,11 @@ public class TomcatProfiler implements ClassFileTransformer { } String javassistClassName = className.replace('/', '.'); - return findModifier.modify(classLoader, javassistClassName, protectionDomain, classFileBuffer); + try { + return findModifier.modify(classLoader, javassistClassName, protectionDomain, classFileBuffer); + } catch (Exception e) { + logger.log(Level.SEVERE, "Modifier:" + findModifier.getTargetClass() + " modify fail. Cause:" + e.getMessage(), e); + return null; + } } } diff --git a/src/main/java/com/profiler/metadata/SqlCacheTable.java b/src/main/java/com/profiler/metadata/SqlCacheTable.java new file mode 100644 index 000000000..edc671ced --- /dev/null +++ b/src/main/java/com/profiler/metadata/SqlCacheTable.java @@ -0,0 +1,78 @@ +package com.profiler.metadata; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * approximate concurrent lru cache + */ +public class SqlCacheTable { + + private static final Object V = new Object(); + + private int concurrentLevel = 16; + private Map[] entry; + + private AtomicInteger cacheSize = new AtomicInteger(); + private int maxCacheSize = 500; + + + public SqlCacheTable(int maxCacheSize) { + this.maxCacheSize = maxCacheSize; + initialize(); + } + + public SqlCacheTable() { + initialize(); + } + + private void initialize() { + this.entry = new Map[concurrentLevel]; + for (int i = 0; i < concurrentLevel; i++) { + this.entry[i] = createLinkedHashMap(); + } + } + + private Map createLinkedHashMap() { + + LinkedHashMap map = new LinkedHashMap(200, .75F, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + // 해당 cache는 구현은 매우 정확하게 max사이를 가지고 있지 않음, 어느정도 오차가 있는 범위에서 동작한다.. + boolean remove = cacheSize.get() + 1 > maxCacheSize; + // + 1의 경우 성능을 좀더 높이기 위해서 put이후 정상적으로 들어갔을 경우 count를 increment시키기 때문에 먼저 +1해서 봄 + // +-대략 concurrentLevel 정도의 오차가 생길수 있을것으로 추정함. + if (remove) { + cacheSize.getAndDecrement(); + } + return remove; + } + }; + return Collections.synchronizedMap(map); + } + + + public boolean put(T value) { + Map cacheMap = getHashEntry(value); + Object oldValue = cacheMap.put(value, V); + if (oldValue == null) { + cacheSize.incrementAndGet(); + return true; + } + return false; + } + + private Map getHashEntry(T key) { + + int entryNumber = Math.abs(key.hashCode()) % concurrentLevel; + + return this.entry[entryNumber]; + } + + public int getSize() { + return cacheSize.get(); + } + +} diff --git a/src/main/java/com/profiler/metadata/SqlObject.java b/src/main/java/com/profiler/metadata/SqlObject.java new file mode 100644 index 000000000..17a806ea6 --- /dev/null +++ b/src/main/java/com/profiler/metadata/SqlObject.java @@ -0,0 +1,40 @@ +package com.profiler.metadata; + +import com.profiler.util.Assert; + +/** + * 없애도 될듯하다. + */ +public class SqlObject { + private String parsedSql; + + public SqlObject(String parsedSql) { + Assert.notNull(parsedSql, "parsedSql is not null"); + this.parsedSql = parsedSql; + } + + public String getParsedSql() { + return parsedSql; + } + + public int getParsedSqlHashCode() { + return parsedSql.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SqlObject sqlObject = (SqlObject) o; + + if (parsedSql != null ? !parsedSql.equals(sqlObject.parsedSql) : sqlObject.parsedSql != null) return false; + + return true; + } + + @Override + public int hashCode() { + return parsedSql != null ? parsedSql.hashCode() : 0; + } +} diff --git a/src/main/java/com/profiler/modifier/db/mysql/MySQLStatementModifier.java b/src/main/java/com/profiler/modifier/db/mysql/MySQLStatementModifier.java index 0935209a0..183b9bf53 100644 --- a/src/main/java/com/profiler/modifier/db/mysql/MySQLStatementModifier.java +++ b/src/main/java/com/profiler/modifier/db/mysql/MySQLStatementModifier.java @@ -41,16 +41,19 @@ public class MySQLStatementModifier extends AbstractModifier { Interceptor executeUpdate1 = byteCodeInstrumentor.newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.interceptor.StatementExecuteUpdateInterceptor"); statementClass.addInterceptor("executeUpdate", new String[]{"java.lang.String"}, executeUpdate1); Interceptor executeUpdate2 = byteCodeInstrumentor.newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.interceptor.StatementExecuteUpdateInterceptor"); - statementClass.addInterceptor("executeUpdate", new String[]{"java.lang.String", "boolean"}, executeUpdate2); + statementClass.addInterceptor("executeUpdate", new String[]{"java.lang.String", "int"}, executeUpdate2); Interceptor executeUpdate3 = byteCodeInstrumentor.newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.interceptor.StatementExecuteUpdateInterceptor"); statementClass.addInterceptor("execute", new String[]{"java.lang.String"}, executeUpdate3); Interceptor executeUpdate4 = byteCodeInstrumentor.newInterceptor(classLoader, protectedDomain, "com.profiler.modifier.db.interceptor.StatementExecuteUpdateInterceptor"); - statementClass.addInterceptor("execute", new String[]{"java.lang.String", "boolean"}, executeUpdate4); + statementClass.addInterceptor("execute", new String[]{"java.lang.String", "int"}, executeUpdate4); statementClass.addTraceVariable("__url", "__setUrl", "__getUrl", "java.lang.Object"); return statementClass.toBytecode(); } catch (InstrumentException e) { + if (logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, this.getClass().getSimpleName() + " modify fail. Cause:" + e.getMessage(), e); + } return null; } } diff --git a/src/test/java/com/profiler/metadata/SqlCacheTableTest.java b/src/test/java/com/profiler/metadata/SqlCacheTableTest.java new file mode 100644 index 000000000..595443bdd --- /dev/null +++ b/src/test/java/com/profiler/metadata/SqlCacheTableTest.java @@ -0,0 +1,50 @@ +package com.profiler.metadata; + +import junit.framework.Assert; +import org.junit.Test; + +import java.util.Random; + +/** + * + */ +public class SqlCacheTableTest { + @Test + public void testPut() throws Exception { + int cacheSize = 100; + SqlCacheTable sqlCacheTable = new SqlCacheTable(cacheSize); + Random random = new Random(); + for (int i = 0; i < 1000; i++) { + sqlCacheTable.put(new SqlObject(String.valueOf(random.nextInt(100000)))); + } + + int size = sqlCacheTable.getSize(); + Assert.assertEquals(size, cacheSize); + + } + + @Test + public void testGetSize() throws Exception { + SqlCacheTable sqlCacheTable = new SqlCacheTable(2); + Assert.assertEquals(sqlCacheTable.getSize(), 0); + + SqlObject sqlObject = new SqlObject("test"); + + boolean hit = sqlCacheTable.put(sqlObject); + Assert.assertTrue(hit); + Assert.assertEquals(sqlCacheTable.getSize(), 1); + + boolean hit2 = sqlCacheTable.put(sqlObject); + Assert.assertFalse(hit2); + Assert.assertEquals(sqlCacheTable.getSize(), 1); +// "23 123"; +// "DCArMlhwQO 7" + sqlCacheTable.put(new SqlObject("23 123")); + sqlCacheTable.put(new SqlObject("DCArMlhwQO 7")); + sqlCacheTable.put(new SqlObject("3")); + sqlCacheTable.put(new SqlObject("4")); + Assert.assertEquals(sqlCacheTable.getSize(), 2); + + + } +}