前回書いた Spring BootアプリのメトリクスをMicrometerで収集する の続きです。

この記事では、java.util.concurrent.ExecutorServiceのメトリクスを収集するためのExecutorServiceMetricsを紹介します。

なお、この記事で使用しているバージョンは以下です。
(2018/05/28現在の最新)

  • Spring Boot: 2.0.2 or 1.5.13
    • Spring Boot
    • spring-boot-starter-actuator
  • Micrometer: 1.0.4
    • micrometer-core
    • micrometer-spring-legacy ※Spring Boot 1.5の場合は必須
    • micrometer-registry-prometheus ※僕はモニタリングにPrometheusを使っています

ExecutorServiceに関するメトリクスは自動で収集されないので、自分で設定を行う必要がありますが、設定は簡単です。
自分で作成したExecutorServiceを引数にしてExecutorServiceMetrics#monitorを呼ぶと、メトリクス収集処理が仕込まれたExecutorServiceが取得できるので、それ使うだけでメトリクスが収集されます。

コード例

@Service
public class AsyncService {

    private final ExecutorService instrumentedExecutor;

    public AsyncService(MeterRegistry registry) {
        ExecutorService executor = Executors.newFixedThreadPool(10);  // ここはお好みで
        instrumentedExecutor = ExecutorServiceMetrics.monitor(registry, executor, "MyThreadPool");
    }

    public void doSomething() {
        // ExecutorServiceとして使うだけでメトリクスが収集される
        instrumentedExecutor.execute(() -> {
            log.info("Thread name = {}", Thread.currentThread().getName());
        });
    }
}

AsyncConfigurerSupportの場合

また、Spring MVCには@Asyncアノテーションを付けたクラス・メソッドを非同期に処理する機能があり、
AsyncConfigurerSupportを継承したクラスを作ると、@Asyncで使用されるThreadPoolをカスタマイズする事ができます。

@AsyncAsyncConfigurerSupportに関しては以下の記事がわかりやすいです。

コード例

AsyncConfigurerSupportを継承したクラスで、上記の例と同じようにExecutorServiceMetrics#monitorでメトリクス収集処理が仕込まれたExecutorServiceを取得して、getAsyncExecutorでreturnします。

@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {

    private final ExecutorService asyncExecutor;

    public AsyncConfig() {
        final ExecutorService executor = Executors.newFixedThreadPool(10);  // ここはお好みで

        // see:
        // https://github.com/micrometer-metrics/micrometer/blob/v1.0.4/micrometer-spring-legacy/src/test/java/io/micrometer/spring/async/ThreadPoolTaskExecutorMetricsIntegrationTest.java#L61-L62
        asyncExecutor = ExecutorServiceMetrics.monitor(Metrics.globalRegistry, executor, "MyThreadPool");
    }

    @Override
    public Executor getAsyncExecutor() {
        return asyncExecutor;
    }
}

※注意点

AsyncConfigurerSupportの場合、ExecutorServiceMetrics#monitorに渡すMeterRegistryはDIで受け取ったインスタンスではなく、Metrics.globalRegistryを使わないといけません。

ドキュメントに記載は見当たらなかったのですが、テストコードに以下のようにコメントが書いてあります。

Injecting the registry instead would cause early evaluation of the registry and the registry wouldn’t be discovered by MeterRegistryPostProcessor

Spring内部の処理順序の関係で、Metrics.globalRegistryを使わないとExecutorServiceMetricsが正しく動作しません。



収集されるメトリクス

  • ThreadPoolのメトリクス
    • Queued
    • Active
    • Poolサイズ
  • タスクのメトリクス
    • Completed

などが収集されます。

# HELP executor_queued_threads The approximate number of threads that are queued for execution
# TYPE executor_queued_threads gauge
executor_queued_threads{name="MyThreadPool",} 0.0
# HELP executor_active_threads The approximate number of threads that are actively executing tasks
# TYPE executor_active_threads gauge
executor_active_threads{name="MyThreadPool",} 10.0
# HELP executor_pool_size_threads The current number of threads in the pool
# TYPE executor_pool_size_threads gauge
executor_pool_size_threads{name="MyThreadPool",} 10.0
# HELP executor_completed_tasks_total The approximate total number of tasks that have completed execution
# TYPE executor_completed_tasks_total counter
executor_completed_tasks_total{name="MyThreadPool",} 40.0

以上です。