Netflixサーボの紹介

Netflix Servoの概要

1. 概要

Netflix Servoは、Javaアプリケーション用のメトリックツールです。 サーボはDropwizard Metricsに似ていますが、はるかに単純です。 JMXを活用して、アプリケーションメトリックを公開および公開するためのシンプルなインターフェイスを提供します。

この記事では、Servoが提供するものと、Servoを使用してアプリケーションメトリックを収集および公開する方法を紹介します。

2. Mavenの依存関係

実際の実装に飛び込む前に、Servo依存関係をpom.xmlファイルに追加しましょう。


    com.netflix.servo
    servo-core
    0.12.16

その上、Servo-ApacheServo-AWSなどの多くの拡張機能が利用可能です。 後で必要になる場合があります。 これらの拡張機能の最新バージョンは、Maven Centralにもあります。

3. 指標を収集する

まず、アプリケーションから指標を収集する方法を見てみましょう。

サーボは、CounterGaugeTimer,、およびInformationalの4つの主要なメトリックタイプを提供します。

3.1. メトリックタイプ– カウンタ

Countersは、インクリメントを記録するために使用されます。 一般的に使用される実装は、BasicCounterStepCounter、およびPeakRateCounterです。

BasicCounterは、カウンターが行うべきことを単純明快に実行します。

Counter counter = new BasicCounter(MonitorConfig.builder("test").build());
assertEquals("counter should start with 0", 0, counter.getValue().intValue());

counter.increment();

assertEquals("counter should have increased by 1", 1, counter.getValue().intValue());

counter.increment(-1);

assertEquals("counter should have decreased by 1", 0, counter.getValue().intValue());

PeakRateCounterは、ポーリング間隔中の特定の秒の最大カウントを返します。

Counter counter = new PeakRateCounter(MonitorConfig.builder("test").build());
assertEquals(
  "counter should start with 0",
  0, counter.getValue().intValue());

counter.increment();
SECONDS.sleep(1);

counter.increment();
counter.increment();

assertEquals("peak rate should have be 2", 2, counter.getValue().intValue());

他のカウンターとは異なり、StepCounterは、前のポーリング間隔の1秒あたりのレートを記録します。

System.setProperty("servo.pollers", "1000");
Counter counter = new StepCounter(MonitorConfig.builder("test").build());

assertEquals("counter should start with rate 0.0", 0.0, counter.getValue());

counter.increment();
SECONDS.sleep(1);

assertEquals(
  "counter rate should have increased to 1.0",
  1.0, counter.getValue());

上記のコードでservo.pollers1000に設定していることに注意してください。 これは、デフォルトで60秒と10秒の間隔ではなく、ポーリング間隔を1秒に設定することでした。 これについては後で詳しく説明します。

3.2. メトリックタイプ– ゲージ

Gaugeは、現在の値を返す単純なモニターです。 BasicGaugeMinGaugeMaxGauge、およびNumberGaugesが提供されます。

BasicGaugeは、Callableを呼び出して現在の値を取得します。 コレクションのサイズ、BlockingQueueの最新の値、または小さな計算を必要とする任意の値を取得できます。

Gauge gauge = new BasicGauge<>(MonitorConfig.builder("test")
  .build(), () -> 2.32);

assertEquals(2.32, gauge.getValue(), 0.01);

MaxGaugeMinGaugeは、それぞれ最大値と最小値を追跡するために使用されます。

MaxGauge gauge = new MaxGauge(MonitorConfig.builder("test").build());
assertEquals(0, gauge.getValue().intValue());

gauge.update(4);
assertEquals(4, gauge.getCurrentValue(0));

gauge.update(1);
assertEquals(4, gauge.getCurrentValue(0));

NumberGaugeLongGaugeDoubleGauge)は、指定されたNumberLongDouble)をラップします。 これらのゲージを使用してメトリックを収集するには、Numberがスレッドセーフであることを確認する必要があります。

3.3. メトリックタイプ– タイマー

Timersは、特定のイベントの期間を測定するのに役立ちます。 デフォルトの実装はBasicTimerStatsTimerBucketTimerである。

BasicTimerは、合計時間、カウント、およびその他の単純な統計を記録します。

BasicTimer timer = new BasicTimer(MonitorConfig.builder("test").build(), SECONDS);
Stopwatch stopwatch = timer.start();

SECONDS.sleep(1);
timer.record(2, SECONDS);
stopwatch.stop();

assertEquals("timer should count 1 second", 1, timer.getValue().intValue());
assertEquals("timer should count 3 seconds in total",
  3.0, timer.getTotalTime(), 0.01);
assertEquals("timer should record 2 updates", 2, timer.getCount().intValue());
assertEquals("timer should have max 2", 2, timer.getMax(), 0.01);

StatsTimerは、ポーリング間隔間でサンプリングすることにより、はるかに豊富な統計を提供します。

System.setProperty("netflix.servo", "1000");
StatsTimer timer = new StatsTimer(MonitorConfig
  .builder("test")
  .build(), new StatsConfig.Builder()
  .withComputeFrequencyMillis(2000)
  .withPercentiles(new double[] { 99.0, 95.0, 90.0 })
  .withPublishMax(true)
  .withPublishMin(true)
  .withPublishCount(true)
  .withPublishMean(true)
  .withPublishStdDev(true)
  .withPublishVariance(true)
  .build(), SECONDS);
Stopwatch stopwatch = timer.start();

SECONDS.sleep(1);
timer.record(3, SECONDS);
stopwatch.stop();

stopwatch = timer.start();
timer.record(6, SECONDS);
SECONDS.sleep(2);
stopwatch.stop();

assertEquals("timer should count 12 seconds in total",
  12, timer.getTotalTime());
assertEquals("timer should count 12 seconds in total",
  12, timer.getTotalMeasurement());
assertEquals("timer should record 4 updates", 4, timer.getCount());
assertEquals("stats timer value time-cost/update should be 2",
  3, timer.getValue().intValue());

final Map metricMap = timer.getMonitors().stream()
  .collect(toMap(monitor -> getMonitorTagValue(monitor, "statistic"),
    monitor -> (Number) monitor.getValue()));

assertThat(metricMap.keySet(), containsInAnyOrder(
  "count", "totalTime", "max", "min", "variance", "stdDev", "avg",
  "percentile_99", "percentile_95", "percentile_90"));

BucketTimerは、値の範囲をバケット化することによってサンプルの分布を取得する方法を提供します。

BucketTimer timer = new BucketTimer(MonitorConfig
  .builder("test")
  .build(), new BucketConfig.Builder()
  .withBuckets(new long[] { 2L, 5L })
  .withTimeUnit(SECONDS)
  .build(), SECONDS);

timer.record(3);
timer.record(6);

assertEquals(
  "timer should count 9 seconds in total",
  9, timer.getTotalTime().intValue());

Map metricMap = timer.getMonitors().stream()
  .filter(monitor -> monitor.getConfig().getTags().containsKey("servo.bucket"))
  .collect(toMap(
    m -> getMonitorTagValue(m, "servo.bucket"),
    m -> (Long) m.getValue()));

assertThat(metricMap, allOf(hasEntry("bucket=2s", 0L), hasEntry("bucket=5s", 1L),
  hasEntry("bucket=overflow", 1L)));

何時間も続く可能性のある長時間の操作を追跡するために、複合モニターDurationTimerを使用できます。

3.4. メトリックタイプ– 情報提供

また、Informationalモニターを使用して、デバッグと診断に役立つ説明情報を記録することもできます。 唯一の実装はBasicInformationalであり、その使用法を単純にすることはできません。

BasicInformational informational = new BasicInformational(
  MonitorConfig.builder("test").build());
informational.setValue("information collected");

3.5. MonitorRegistry

メトリックタイプはすべてタイプMonitorであり、これはServoのまさにベースです。 各種のツールが未加工のメトリックを収集することはわかっていますが、データをレポートするには、これらのモニターを登録する必要があります。

構成された各モニターは、メトリックの正確性を確保するために一度だけ登録する必要があることに注意してください。 したがって、Singletonパターンを使用してモニターを登録できます。

ほとんどの場合、DefaultMonitorRegistryを使用してモニターを登録できます。

Gauge gauge = new BasicGauge<>(MonitorConfig.builder("test")
  .build(), () -> 2.32);
DefaultMonitorRegistry.getInstance().register(gauge);

モニターを動的に登録する場合は、DynamicTimerDynamicCounterを使用できます。

DynamicCounter.increment("monitor-name", "tag-key", "tag-value");

動的登録は、値が更新されるたびにコストのかかるルックアップ操作を引き起こすことに注意してください。

Servoは、オブジェクトで宣言されたモニターを登録するためのいくつかのヘルパーメソッドも提供します。

Monitors.registerObject("testObject", this);
assertTrue(Monitors.isObjectRegistered("testObject", this));

メソッドregisterObjectは、リフレクションを使用して、アノテーション@Monitorによって宣言されたMonitorsのすべてのインスタンスを追加し、@MonitorTagsによって宣言されたタグを追加します。

@Monitor(
  name = "integerCounter",
  type = DataSourceType.COUNTER,
  description = "Total number of update operations.")
private AtomicInteger updateCount = new AtomicInteger(0);

@MonitorTags
private TagList tags = new BasicTagList(
  newArrayList(new BasicTag("tag-key", "tag-value")));

@Test
public void givenAnnotatedMonitor_whenUpdated_thenDataCollected() throws Exception {
    System.setProperty("servo.pollers", "1000");
    Monitors.registerObject("testObject", this);
    assertTrue(Monitors.isObjectRegistered("testObject", this));

    updateCount.incrementAndGet();
    updateCount.incrementAndGet();
    SECONDS.sleep(1);

    List> metrics = observer.getObservations();

    assertThat(metrics, hasSize(greaterThanOrEqualTo(1)));

    Iterator> metricIterator = metrics.iterator();
    metricIterator.next(); //skip first empty observation

    while (metricIterator.hasNext()) {
        assertThat(metricIterator.next(), hasItem(
          hasProperty("config",
          hasProperty("name", is("integerCounter")))));
    }
}

4. 指標を公開する

収集されたメトリックを使用して、さまざまなデータ視覚化プラットフォームで時系列グラフをレンダリングするなど、任意の形式で公開できます。 メトリックを公開するには、モニターの観測から定期的にデータをポーリングする必要があります。

4.1. MetricPoller

MetricPollerはメトリックフェッチャーとして使用されます。 MonitorRegistriesJVMJMXのメトリックをフェッチできます。 拡張機能を使用すると、Apache server statusTomcat metricsなどのメトリックをポーリングできます。

MemoryMetricObserver observer = new MemoryMetricObserver();
PollRunnable pollRunnable = new PollRunnable(new JvmMetricPoller(),
  new BasicMetricFilter(true), observer);
PollScheduler.getInstance().start();
PollScheduler.getInstance().addPoller(pollRunnable, 1, SECONDS);

SECONDS.sleep(1);
PollScheduler.getInstance().stop();
List> metrics = observer.getObservations();

assertThat(metrics, hasSize(greaterThanOrEqualTo(1)));
List keys = extractKeys(metrics);

assertThat(keys, hasItems("loadedClassCount", "initUsage", "maxUsage", "threadCount"));

ここでは、JVMのメトリックをポーリングするためのJvmMetricPollerを作成しました。 スケジューラにポーラーを追加するとき、ポーリングタスクを毎秒実行します。 システムのデフォルトのポーラー構成はPollersで定義されていますが、システムプロパティservo.pollersで使用するポーラーを指定できます。

4.2. MetricObserver

メトリックをポーリングすると、登録されたMetricObserversの観測値が更新されます。

デフォルトで提供されるMetricObserversは、MemoryMetricObserverFileMetricObserver、およびAsyncMetricObserverです。 前のコードサンプルでMemoryMetricObserverの使用方法をすでに示しました。

現在、いくつかの便利な拡張機能が利用可能です。

カスタマイズされたMetricObserverを実装して、適切と思われる場所にアプリケーションメトリックを公開できます。 唯一気にすることは、更新されたメトリックを処理することです。

public class CustomObserver extends BaseMetricObserver {

    //...

    @Override
    public void updateImpl(List metrics) {
        //TODO
    }
}

4.3. Netflixアトラスに公開

Atlasは、Netflixのもう1つのメトリック関連ツールです。 これは、ディメンションの時系列データを管理するためのツールであり、収集した指標を公開するのに最適な場所です。

次に、指標をNetflixAtlasに公開する方法を示します。

まず、servo-atlasの依存関係をpom.xmlに追加しましょう。


      com.netflix.servo
      servo-atlas
      ${netflix.servo.ver}



    0.12.17

この依存関係には、メトリックをAtlasに公開するのに役立つAtlasMetricObserverが含まれています。

次に、Atlasサーバーをセットアップします。

$ curl -LO 'https://github.com/Netflix/atlas/releases/download/v1.4.4/atlas-1.4.4-standalone.jar'
$ curl -LO 'https://raw.githubusercontent.com/Netflix/atlas/v1.4.x/conf/memory.conf'
$ java -jar atlas-1.4.4-standalone.jar memory.conf

テストの時間を節約するために、ステップサイズをmemory.confで1秒に設定して、メトリックの十分な詳細を含む時系列グラフを生成できるようにします。

AtlasMetricObserverには、単純な構成とタグのリストが必要です。 指定されたタグのメトリックはAtlasにプッシュされます。

System.setProperty("servo.pollers", "1000");
System.setProperty("servo.atlas.batchSize", "1");
System.setProperty("servo.atlas.uri", "http://localhost:7101/api/v1/publish");
AtlasMetricObserver observer = new AtlasMetricObserver(
  new BasicAtlasConfig(), BasicTagList.of("servo", "counter"));

PollRunnable task = new PollRunnable(
  new MonitorRegistryMetricPoller(), new BasicMetricFilter(true), observer);

PollRunnableタスクでPollSchedulerを起動した後、メトリックをAtlasに自動的に公開できます。

Counter counter = new BasicCounter(MonitorConfig
  .builder("test")
  .withTag("servo", "counter")
  .build());
DefaultMonitorRegistry
  .getInstance()
  .register(counter);
assertThat(atlasValuesOfTag("servo"), not(containsString("counter")));

for (int i = 0; i < 3; i++) {
    counter.increment(RandomUtils.nextInt(10));
    SECONDS.sleep(1);
    counter.increment(-1 * RandomUtils.nextInt(10));
    SECONDS.sleep(1);
}

assertThat(atlasValuesOfTag("servo"), containsString("counter"));

メトリックに基づいて、Atlasのgraph APIを使用して折れ線グラフを生成できます。

image

5. 概要

この記事では、Netflix Servoを使用してアプリケーションメトリックを収集および公開する方法を紹介しました。

Dropwizard Metricsの概要をまだ読んでいない場合は、hereをチェックしてServoと簡単に比較してください。

いつものように、この記事の完全な実装コードはover on Githubにあります。