Created
November 26, 2013 09:12
-
-
Save aleung/7655478 to your computer and use it in GitHub Desktop.
Invocation metric bases on codahale (Yammer) Metrics. 这是在Messaging性能测试时使用的metrics,记录了代码块调用的throughput(TPS,latency)和active count(并发数),数据每分钟写入csv格式文件。 用try + finally的方式对调用进行拦截检测,代码有侵入性。当时是测试使用,这样写起来最快。
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
public interface ActiveCountMetric { | |
void inc(); | |
void dec(); | |
} |
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
public interface ExecutionMetric { | |
void begin(); | |
void end(); | |
} |
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
package leoliang.foundation.metrics; | |
import java.util.concurrent.TimeUnit; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
import com.codahale.metrics.Counter; | |
import com.codahale.metrics.Histogram; | |
import com.codahale.metrics.Metric; | |
import com.codahale.metrics.MetricRegistry; | |
import com.codahale.metrics.SharedMetricRegistries; | |
import com.codahale.metrics.SlidingTimeWindowReservoir; | |
import com.codahale.metrics.Timer; | |
public class GlobalMetricRegistry { | |
interface Creator { | |
Metric create(); | |
} | |
public static String name(Class<?> klass, String... names) { | |
return replaceInvalidChars(MetricRegistry.name(klass, names)); | |
} | |
public static String name(String name, String... names) { | |
return replaceInvalidChars(MetricRegistry.name(name, names)); | |
} | |
private static String replaceInvalidChars(String name) { | |
return name.replaceAll("[^A-Za-z0-9\\.]", "_"); | |
} | |
/** | |
* InvocationMetric is a combination of ThroughputMetric and ActiveCountMetric | |
* | |
* @param registryName | |
* @param metricNamePrefix | |
* @return | |
*/ | |
public static ExecutionMetric getOrCreateInvocationMetric(String registryName, String metricNamePrefix) { | |
if (!isEnabled(registryName, metricNamePrefix)) | |
return new DummyExecutionMetric(); | |
return new InvocationMetricImpl(getOrCreateActiveCountMetric(registryName, metricNamePrefix), | |
getOrCreateThroughputMetric(registryName, metricNamePrefix)); | |
} | |
/** | |
* ThroughputMetric measures the throughput and the distribution of execution time in last one minute. The | |
* throughput is counted when execution ends. | |
* | |
* @param registryName | |
* @param metricNamePrefix | |
* @return | |
*/ | |
public static ExecutionMetric getOrCreateThroughputMetric(String registryName, String metricNamePrefix) { | |
if (!isEnabled(registryName, metricNamePrefix)) | |
return new DummyExecutionMetric(); | |
Metric metric = getOrCreate(registryName, metricNamePrefix + ".throughput", new TimerCreator()); | |
return new ThroughputMetricImpl((Timer) metric); | |
} | |
/** | |
* ActiveCountMetric counts the number of items, and also keep track of the max and min value in last one minute. | |
* <p> | |
* Two metrics will be created: <i>[metricNamePrefix].now</i> and <i>[metricNamePrefix].history_1min</i> | |
* </p> | |
* | |
* @param registryName | |
* @param metricNamePrefix | |
* @return | |
*/ | |
public static ActiveCountMetric getOrCreateActiveCountMetric(String registryName, String metricNamePrefix) { | |
if (!isEnabled(registryName, metricNamePrefix)) | |
return new DummyActiveCountMetric(); | |
Metric counter = getOrCreate(registryName, metricNamePrefix + ".active.now", new CounterCreator()); | |
Metric histogram = getOrCreate(registryName, metricNamePrefix + ".active.history_1min", new HistogramCreator()); | |
return new ActiveCountMetricImpl((Counter) counter, (Histogram) histogram); | |
} | |
private static boolean isEnabled(String registryName, String metricNamePrefix) { | |
// TODO: make it configurable | |
return Logger.getLogger("metrics").isLoggable(Level.INFO); | |
} | |
private static Metric getOrCreate(String registryName, String metricName, Creator creator) { | |
MetricRegistry registry = SharedMetricRegistries.getOrCreate(registryName); | |
Metric metric = registry.getMetrics().get(metricName); | |
if (metric == null) { | |
try { | |
registry.register(metricName, creator.create()); | |
} catch (IllegalArgumentException e) { | |
// ignore if metric already exists | |
} | |
metric = registry.getMetrics().get(metricName); | |
} | |
return metric; | |
} | |
private static class TimerCreator implements Creator { | |
@Override | |
public Metric create() { | |
return new Timer(new SlidingTimeWindowReservoir(1, TimeUnit.MINUTES)); | |
} | |
} | |
private static class CounterCreator implements Creator { | |
@Override | |
public Metric create() { | |
return new Counter(); | |
} | |
} | |
private static class HistogramCreator implements Creator { | |
@Override | |
public Metric create() { | |
return new Histogram(new SlidingTimeWindowReservoir(1, TimeUnit.MINUTES)); | |
} | |
} | |
private static class ThroughputMetricImpl implements ExecutionMetric { | |
private Timer timer; | |
private Timer.Context context; | |
private ThroughputMetricImpl(Timer timer) { | |
this.timer = timer; | |
} | |
@Override | |
public void begin() { | |
context = timer.time(); | |
} | |
@Override | |
public void end() { | |
context.close(); | |
context = null; // force to thrown exception on next call to end() without begin() | |
} | |
} | |
private static class DummyActiveCountMetric implements ActiveCountMetric { | |
@Override | |
public void inc() { | |
// do nothing | |
} | |
@Override | |
public void dec() { | |
// do nothing | |
} | |
} | |
private static class ActiveCountMetricImpl implements ActiveCountMetric { | |
private final Counter counter; | |
private final Histogram histogram; | |
public ActiveCountMetricImpl(Counter counter, Histogram histogram) { | |
this.counter = counter; | |
this.histogram = histogram; | |
} | |
@Override | |
public void inc() { | |
counter.inc(); | |
updateHistory(); | |
} | |
private void updateHistory() { | |
// no lock, it isn't important to get precise value | |
histogram.update(counter.getCount()); | |
} | |
@Override | |
public void dec() { | |
counter.dec(); | |
updateHistory(); | |
} | |
} | |
private static class DummyExecutionMetric implements ExecutionMetric { | |
@Override | |
public void begin() { | |
// do nothing | |
} | |
@Override | |
public void end() { | |
// do nothing | |
} | |
} | |
private static class InvocationMetricImpl implements ExecutionMetric { | |
private final ActiveCountMetric counterMetric; | |
private final ExecutionMetric throughputMetric; | |
private InvocationMetricImpl(ActiveCountMetric counterMetric, ExecutionMetric throughputMetric) { | |
this.counterMetric = counterMetric; | |
this.throughputMetric = throughputMetric; | |
} | |
@Override | |
public void begin() { | |
counterMetric.inc(); | |
throughputMetric.begin(); | |
} | |
@Override | |
public void end() { | |
counterMetric.dec(); | |
throughputMetric.end(); | |
} | |
} | |
} |
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
package leoliang.foundation.metrics; | |
import java.io.File; | |
import java.util.concurrent.TimeUnit; | |
import com.codahale.metrics.CsvReporter; | |
import com.codahale.metrics.MetricRegistry; | |
import com.codahale.metrics.ScheduledReporter; | |
import com.codahale.metrics.SharedMetricRegistries; | |
import leoliang.lifecycle.WSFApplicationLifecycleIF; | |
public class MetricsApplicationLifecycle implements WSFApplicationLifecycleIF { | |
private ScheduledReporter reporter; | |
@Override | |
public void startup() { | |
// FIXME: tmp solution, hard coded to "Messaging" | |
MetricRegistry registry = SharedMetricRegistries.getOrCreate("Messaging"); | |
reporter = CsvReporter.forRegistry(registry) | |
.convertRatesTo(TimeUnit.SECONDS) | |
.convertDurationsTo(TimeUnit.MILLISECONDS).build(new File("/var/exposure/metrics/")); | |
reporter.start(1, TimeUnit.MINUTES); | |
} | |
@Override | |
public void shutdown() { | |
reporter.stop(); | |
} | |
} |
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
private String httpSend(String url, PostMethod method, HttpClient httpClient, HostConfiguration hostConfiguration) | |
throws IOException { | |
ExecutionMetric metric = GlobalMetricRegistry.getOrCreateInvocationMetric("Messaging", | |
GlobalMetricRegistry.name(this.getClass(), "httpSend", url)); | |
metric.begin(); | |
try { | |
int aStatusCode = send(httpClient, hostConfiguration, method); | |
if (aStatusCode != HttpStatus.SC_OK) { | |
if ((logger != null) && logger.isWarnEnabled()) { | |
logger.warn("HTTP error, object will not be read"); | |
} | |
throw new RetryableException("HTTP Error: " + aStatusCode + " (" | |
+ HttpStatus.getStatusText(aStatusCode) + ")"); | |
} | |
return method.getResponseBodyAsString(); | |
} finally { | |
metric.end(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment