Panduan Pantas untuk Mikrometer

1. Pengenalan

Mikrometer memberikan fasad sederhana kepada klien instrumentasi untuk sejumlah sistem pemantauan yang popular. Pada masa ini, ia menyokong sistem pemantauan berikut: Atlas, Datadog, Graphite, Ganglia, Influx, JMX dan Prometheus.

Dalam artikel ini, kami akan memperkenalkan penggunaan asas Mikrometer dan penyatuannya dengan Spring.

Demi kesederhanaan, kami akan mengambil Micrometer Atlas sebagai contoh untuk menunjukkan kebanyakan kes penggunaan kami.

2. Ketergantungan Maven

Sebagai permulaan, mari tambahkan kebergantungan berikut ke pom.xml :

 io.micrometer micrometer-registry-atlas 0.12.0.RELEASE 

Versi terbaru boleh didapati di sini.

3. Pendaftaran Meter

Dalam Mikrometer, MeterRegistry adalah komponen teras yang digunakan untuk mendaftarkan meter. Kita boleh melakukan iterasi melalui registri dan selanjutnya metrik setiap meter, untuk menghasilkan siri masa di backend dengan kombinasi metrik dan nilai dimensi mereka.

Bentuk pendaftaran yang paling mudah ialah SimpleMeterRegistry . Tetapi dalam kebanyakan kes, kita harus menggunakan MeterRegistry yang dirancang secara eksplisit untuk sistem pemantauan kita; untuk Atlas, ia adalah AtlasMeterRegistry .

CompositeMeterRegistry membolehkan beberapa pendaftar ditambahkan. Ini memberikan penyelesaian untuk menerbitkan metrik aplikasi ke pelbagai sistem pemantauan yang disokong secara serentak.

Kami dapat menambahkan MeterRegistry yang diperlukan untuk memuat naik data ke pelbagai platform:

CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry(); SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry(); AtlasMeterRegistry atlasMeterRegistry = new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM); compositeRegistry.add(oneSimpleMeter); compositeRegistry.add(atlasMeterRegistry);

Terdapat sokongan pendaftaran global yang statik di Micrometer: Metrics.globalRegistry . Juga, satu set pembangun statik berdasarkan pendaftaran global ini disediakan untuk menghasilkan meter dalam Metrik :

@Test public void givenGlobalRegistry_whenIncrementAnywhere_thenCounted() { class CountedObject { private CountedObject() { Metrics.counter("objects.instance").increment(1.0); } } Metrics.addRegistry(new SimpleMeterRegistry()); Metrics.counter("objects.instance").increment(); new CountedObject(); Optional counterOptional = Metrics.globalRegistry .find("objects.instance").counter(); assertTrue(counterOptional.isPresent()); assertTrue(counterOptional.get().count() == 2.0); }

4. Tag dan Meter

4.1. Teg

Pengecam Meter terdiri daripada nama dan tanda nama. Sebaiknya kita mengikuti konvensyen penamaan yang memisahkan kata dengan titik, untuk membantu menjamin mudah alih nama metrik di pelbagai sistem pemantauan.

Counter counter = registry.counter("page.visitors", "age", "20s");

Teg boleh digunakan untuk memotong metrik untuk memberi alasan mengenai nilainya. Dalam kod di atas, page.visitors adalah nama meter, dengan umur = 20-an sebagai tagnya . Dalam kes ini, kaunter dimaksudkan untuk menghitung pengunjung halaman dengan usia antara 20 dan 30 tahun.

Untuk sistem yang besar, kita dapat menambahkan tag biasa ke registri, katakan metriknya berasal dari wilayah tertentu:

registry.config().commonTags("region", "ua-east");

4.2. Kaunter

A Counter melaporkan hanya kiraan atas harta yang ditetapkan permohonan. Kami boleh membina kaunter khusus dengan fasih builder atau kaedah pembantu mana-mana MetricRegistry :

Counter counter = Counter .builder("instance") .description("indicates instance count of the object") .tags("dev", "performance") .register(registry); counter.increment(2.0); assertTrue(counter.count() == 2); counter.increment(-1); assertTrue(counter.count() == 2);

Seperti yang dapat dilihat dari coretan di atas, kami cuba menurunkan pembilang dengan satu tetapi kami hanya dapat menambahkan pembilang secara monoton dengan jumlah positif yang tetap.

4.3. Pemasa

Untuk mengukur kependaman atau kekerapan peristiwa dalam sistem kami, kami dapat menggunakan Pemasa . A Timer akan melaporkan sekurang-kurangnya jumlah masa dan acara mengira siri masa tertentu.

Sebagai contoh, kita dapat merakam peristiwa aplikasi yang mungkin berlangsung beberapa saat:

SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = registry.timer("app.event"); timer.record(() -> { try { TimeUnit.MILLISECONDS.sleep(1500); } catch (InterruptedException ignored) { } }); timer.record(3000, MILLISECONDS); assertTrue(2 == timer.count()); assertTrue(4510 > timer.totalTime(MILLISECONDS) && 4500 <= timer.totalTime(MILLISECONDS));

Untuk merakam peristiwa yang berjalan lama, kami menggunakan LongTaskTimer :

SimpleMeterRegistry registry = new SimpleMeterRegistry(); LongTaskTimer longTaskTimer = LongTaskTimer .builder("3rdPartyService") .register(registry); long currentTaskId = longTaskTimer.start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException ignored) { } long timeElapsed = longTaskTimer.stop(currentTaskId); assertTrue(timeElapsed / (int) 1e9 == 2);

4.4. Tolok

Tolok menunjukkan nilai meter semasa.

Berlainan dengan meter lain, Tolok hanya boleh melaporkan data apabila diperhatikan. Tolok boleh berguna semasa memantau statistik cache, koleksi, dan lain-lain:

SimpleMeterRegistry registry = new SimpleMeterRegistry(); List list = new ArrayList(4); Gauge gauge = Gauge .builder("cache.size", list, List::size) .register(registry); assertTrue(gauge.value() == 0.0); list.add("1"); assertTrue(gauge.value() == 1.0);

4.5. Edaran Ringkas

Pengedaran acara dan ringkasan ringkas disediakan oleh DistributionSummary :

SimpleMeterRegistry registry = new SimpleMeterRegistry(); DistributionSummary distributionSummary = DistributionSummary .builder("request.size") .baseUnit("bytes") .register(registry); distributionSummary.record(3); distributionSummary.record(4); distributionSummary.record(5); assertTrue(3 == distributionSummary.count()); assertTrue(12 == distributionSummary.totalAmount());

Selain itu, DistributionSummary dan Timers dapat diperkaya dengan kuantil:

SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = Timer.builder("test.timer") .quantiles(WindowSketchQuantiles .quantiles(0.3, 0.5, 0.95) .create()) .register(registry);

Dalam coretan di atas, tiga alat pengukur dengan tag quantile = 0,3 , quantile = 0,5 dan quantile = 0,95 akan tersedia dalam daftar, menunjukkan nilai di bawah yang masing-masing 95%, 50% dan 30% jatuh.

Untuk melihat kuantil ini beraksi, mari tambahkan rekod berikut:

timer.record(2, TimeUnit.SECONDS); timer.record(2, TimeUnit.SECONDS); timer.record(3, TimeUnit.SECONDS); timer.record(4, TimeUnit.SECONDS); timer.record(8, TimeUnit.SECONDS); timer.record(13, TimeUnit.SECONDS);

Kemudian kita dapat mengesahkan dengan mengekstrak nilai dalam ketiga-tiga Tolok kuantitatif :

List quantileGauges = registry.getMeters().stream() .filter(m -> m.getType().name().equals("Gauge")) .map(meter -> (Gauge) meter) .collect(Collectors.toList()); assertTrue(3 == quantileGauges.size()); Map quantileMap = extractTagValueMap(registry, Type.Gauge, 1e9); assertThat(quantileMap, allOf( hasEntry("quantile=0.3",2), hasEntry("quantile=0.5", 3), hasEntry("quantile=0.95", 8)));

Selain itu, Mikrometer juga menyokong histogram:

DistributionSummary hist = DistributionSummary .builder("summary") .histogram(Histogram.linear(0, 10, 5)) .register(registry);

Sama seperti kuantil, setelah menambahkan beberapa rekod, kita dapat melihat bahawa histogram menangani pengiraan dengan cukup baik:

Map histograms = extractTagValueMap(registry, Type.Counter, 1.0); assertThat(histograms, allOf( hasEntry("bucket=0.0", 0), hasEntry("bucket=10.0", 2), hasEntry("bucket=20.0", 2), hasEntry("bucket=30.0", 1), hasEntry("bucket=40.0", 1), hasEntry("bucket=Infinity", 0)));

Generally, histograms can help illustrate a direct comparison in separate buckets. Histograms can also be time scaled, which is quite useful for analyzing backend service response time:

SimpleMeterRegistry registry = new SimpleMeterRegistry(); Timer timer = Timer .builder("timer") .histogram(Histogram.linearTime(TimeUnit.MILLISECONDS, 0, 200, 3)) .register(registry); //... assertThat(histograms, allOf( hasEntry("bucket=0.0", 0), hasEntry("bucket=2.0E8", 1), hasEntry("bucket=4.0E8", 1), hasEntry("bucket=Infinity", 3)));

5. Binders

The Micrometer has multiple built-in binders to monitor the JVM, caches, ExecutorService and logging services.

When it comes to JVM and system monitoring, we can monitor class loader metrics (ClassLoaderMetrics), JVM memory pool (JvmMemoryMetrics) and GC metrics (JvmGcMetrics), thread and CPU utilization (JvmThreadMetrics, ProcessorMetrics).

Cache monitoring (currently, only Guava, EhCache, Hazelcast, and Caffeine are supported) is supported by instrumenting with GuavaCacheMetrics, EhCache2Metrics, HazelcastCacheMetrics, and CaffeineCacheMetrics. And to monitor log back service, we can bind LogbackMetrics to any valid registry:

new LogbackMetrics().bind(registry);

The usage of above binders are quite similar to LogbackMetrics and are all rather simple, so we won’t dive into further details here.

6. Spring Integration

Spring Boot Actuator provides dependency management and auto-configuration for Micrometer. Now it's supported in Spring Boot 2.0/1.x and Spring Framework 5.0/4.x.

We'll need the following dependency (the latest version can be found here):

 io.micrometer micrometer-spring-legacy 0.12.0.RELEASE 

Without any further change to existing code, we have enabled Spring support with the Micrometer. JVM memory metrics of our Spring application will be automatically registered in the global registry and published to the default atlas endpoint: //localhost:7101/api/v1/publish.

There're several configurable properties available to control metrics exporting behaviors, starting with spring.metrics.atlas.*. Check AtlasConfig to see a full list of configuration properties for Atlas publishing.

If we need to bind more metrics, only add them as @Bean to the application context.

Say we need the JvmThreadMetrics:

@Bean JvmThreadMetrics threadMetrics(){ return new JvmThreadMetrics(); }

As for web monitoring, it's auto-configured for every endpoint in our application, yet manageable via a configuration property: spring.metrics.web.autoTimeServerRequests.

The default implementation provides four dimensions of metrics for endpoints: HTTP request method, HTTP response code, endpoint URI, and exception information.

When requests are responded, metrics relating to request method (GET, POST, etc.) will be published in Atlas.

With Atlas Graph API, we can generate a graph to compare the response time for different methods:

By default, response codes of 20x, 30x, 40x, 50x will also be reported:

We can also compare different URIs :

or check exception metrics:

Note that we can also use @Timed on the controller class or specific endpoint methods to customize tags, long task, quantiles, and percentiles of the metrics:

@RestController @Timed("people") public class PeopleController { @GetMapping("/people") @Timed(value = "people.all", longTask = true) public List listPeople() { //... } }

Based on the code above, we can see the following tags by checking Atlas endpoint //localhost:7101/api/v1/tags/name:

["people", "people.all", "jvmBufferCount", ... ]

Micrometer also works in the function web framework introduced in Spring Boot 2.0. Metrics can be enabled by filtering the RouterFunction:

RouterFunctionMetrics metrics = new RouterFunctionMetrics(registry); RouterFunctions.route(...) .filter(metrics.timer("server.requests"));

Metrics from the data source and scheduled tasks can also be collected. Check the official documentation for more details.

7. Conclusion

Dalam artikel ini, kami memperkenalkan Mikrometer fasad metrik. Dengan mengaburkan dan menyokong pelbagai sistem pemantauan di bawah semantik biasa, alat ini menjadikan pertukaran antara platform pemantauan yang berbeza menjadi sangat mudah.

Seperti biasa, kod pelaksanaan lengkap artikel ini terdapat di Github.