/*
* Copyright 2016 NAVER Corp.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.navercorp.pinpoint.collector.monitor;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
*
* refer to dropwizard metrics
* https://github.com/dropwizard/metrics/blob/3.2-development/metrics-core/src/main/java/com/codahale/metrics/InstrumentedExecutorService.java
*
* Caution : If using multiple method (like invokeAny, invokeAll), then rejected count can be incorrect.
*
* @author Taejin Koo
*/
public class MonitoredExecutorService implements ExecutorService {
private final Meter submitted;
private final Meter rejected;
private final Counter running;
private final Meter completed;
private final Timer dispatchDurationTimer;
private final Timer durationTimer;
private final ExecutorService delegate;
public MonitoredExecutorService(ExecutorService delegate, MetricRegistry registry, String name) {
this.delegate = delegate;
this.submitted = registry.meter(MetricRegistry.name(name, "submitted"));
this.rejected = registry.meter(MetricRegistry.name(name, "rejected"));
this.running = registry.counter(MetricRegistry.name(name, "running"));
this.completed = registry.meter(MetricRegistry.name(name, "completed"));
this.dispatchDurationTimer = registry.timer(MetricRegistry.name(name, "dispatchDuration"));
this.durationTimer = registry.timer(MetricRegistry.name(name, "duration"));
}
@Override
public void execute(Runnable runnable) {
submitted.mark();
try {
delegate.execute(new InstrumentedRunnable(runnable));
} catch (RejectedExecutionException ree) {
rejected.mark();
throw ree;
}
}
@Override
public Future<?> submit(Runnable runnable) {
submitted.mark();
try {
return delegate.submit(new InstrumentedRunnable(runnable));
} catch (RejectedExecutionException ree) {
rejected.mark();
throw ree;
}
}
@Override
public <T> Future<T> submit(Runnable runnable, T result) {
submitted.mark();
try {
return delegate.submit(new InstrumentedRunnable(runnable), result);
} catch (RejectedExecutionException ree) {
rejected.mark();
throw ree;
}
}
@Override
public <T> Future<T> submit(Callable<T> callable) {
submitted.mark();
try {
return delegate.submit(new InstrumentedCallable<T>(callable));
} catch (RejectedExecutionException ree) {
rejected.mark();
throw ree;
}
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> callables) throws InterruptedException {
submitted.mark(callables.size());
Collection<? extends Callable<T>> instrumented = instrument(callables);
try {
return delegate.invokeAll(instrumented);
} catch (RejectedExecutionException ree) {
rejected.mark(callables.size());
throw ree;
}
}
@Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> callables, long timeout, TimeUnit unit) throws InterruptedException {
submitted.mark(callables.size());
Collection<? extends Callable<T>> instrumented = instrument(callables);
try {
return delegate.invokeAll(instrumented, timeout, unit);
} catch (RejectedExecutionException ree) {
rejected.mark(callables.size());
throw ree;
}
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> callables) throws ExecutionException, InterruptedException {
submitted.mark(callables.size());
Collection<? extends Callable<T>> instrumented = instrument(callables);
try {
return delegate.invokeAny(instrumented);
} catch (RejectedExecutionException ree) {
rejected.mark(callables.size());
throw ree;
}
}
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> callables, long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
submitted.mark(callables.size());
Collection<? extends Callable<T>> instrumented = instrument(callables);
try {
return delegate.invokeAny(instrumented, timeout, unit);
} catch (RejectedExecutionException ree) {
rejected.mark(callables.size());
throw ree;
}
}
private <T> Collection<? extends Callable<T>> instrument(Collection<? extends Callable<T>> callables) {
final List<InstrumentedCallable<T>> instrumented = new ArrayList<InstrumentedCallable<T>>(callables.size());
for (Callable<T> callable : callables) {
instrumented.add(new InstrumentedCallable<T>(callable));
}
return instrumented;
}
@Override
public void shutdown() {
delegate.shutdown();
}
@Override
public List<Runnable> shutdownNow() {
return delegate.shutdownNow();
}
@Override
public boolean isShutdown() {
return delegate.isShutdown();
}
@Override
public boolean isTerminated() {
return delegate.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit timeUnit) throws InterruptedException {
return delegate.awaitTermination(timeout, timeUnit);
}
private class InstrumentedRunnable implements Runnable {
private final Runnable runnable;
private final Timer.Context dispatchDuration;
InstrumentedRunnable(Runnable runnable) {
this.runnable = runnable;
this.dispatchDuration = dispatchDurationTimer.time();
}
@Override
public void run() {
dispatchDuration.stop();
running.inc();
final Timer.Context context = durationTimer.time();
try {
runnable.run();
} finally {
context.stop();
running.dec();
completed.mark();
}
}
}
private class InstrumentedCallable<T> implements Callable<T> {
private final Callable<T> callable;
private final Timer.Context dispatchDuration;
InstrumentedCallable(Callable<T> callable) {
this.callable = callable;
this.dispatchDuration = dispatchDurationTimer.time();
}
@Override
public T call() throws Exception {
dispatchDuration.stop();
running.inc();
final Timer.Context context = durationTimer.time();
try {
return callable.call();
} finally {
context.stop();
running.dec();
completed.mark();
}
}
}
}