Introduktion til Netflix Servo

1. Oversigt

Netflix Servo er et metrics-værktøj til Java-applikationer. Servo ligner Dropwizard Metrics, men alligevel meget enklere. Det udnytter kun JMX til at give en enkel grænseflade til eksponering og offentliggørelse af applikationsmålinger.

I denne artikel introducerer vi, hvad Servo tilbyder, og hvordan kan vi bruge det til at indsamle og offentliggøre applikationsmålinger.

2. Maven-afhængigheder

Før vi dykker ned i den faktiske implementering, lad os tilføje Servo-afhængighed til pom.xml fil:

 com.netflix.servo servokerne 0.12.16 

Derudover er der mange udvidelser til rådighed, såsom Servo-Apache, Servo-AWS osv. Vi har muligvis brug for dem senere. Seneste versioner af disse udvidelser kan også findes på Maven Central.

3. Saml målinger

Lad os først se, hvordan man samler målinger fra vores ansøgning.

Servo leverer fire primære metrityper: Tæller, Målestok, Timer, og Informativt.

3.1. Metriske typer - Tæller

Tællere bruges til at registrere inkrementering. Almindeligt anvendte implementeringer er BasicCounter, StepCounterog PeakRateCounter.

BasicCounter gør hvad en tæller skal gøre, almindelig og ligetil:

Tællertæller = ny BasicCounter (MonitorConfig.builder ("test"). Build ()); assertEquals ("tælleren skal starte med 0", 0, counter.getValue (). intValue ()); counter.increment (); assertEquals ("tælleren skulle være steget med 1", 1, counter.getValue (). intValue ()); counter.increment (-1); assertEquals ("tælleren skulle være faldet med 1", 0, counter.getValue (). intValue ());

PeakRateCounter returnerer det maksimale antal i et givet sekund i løbet af afstemningsintervallet:

Tællertæller = ny PeakRateCounter (MonitorConfig.builder ("test"). Build ()); assertEquals ("tælleren skal starte med 0", 0, counter.getValue (). intValue ()); counter.increment (); SECONDS.sleep (1); counter.increment (); counter.increment (); assertEquals ("peak rate burde have været 2", 2, counter.getValue (). intValue ());

I modsætning til andre tællere StepCounter registrerer sats pr. sekund af forrige afstemningsinterval:

System.setProperty ("servo.pollers", "1000"); Tællertæller = ny StepCounter (MonitorConfig.builder ("test"). Build ()); assertEquals ("tæller skal starte med rate 0,0", 0,0, counter.getValue ()); counter.increment (); SECONDS.sleep (1); assertEquals ("modfrekvens skulle have steget til 1.0", 1.0, counter.getValue ());

Bemærk, at vi indstiller servo.pollere til 1000 i koden ovenfor. Det var at indstille afstemningsintervallet til 1 sekund i stedet for intervaller på 60 sekunder og 10 sekunder som standard. Vi vil dække mere om dette senere.

3.2. Metriske typer - Målestok

Målestok er en simpel skærm, der returnerer den aktuelle værdi. BasicGauge, MinGauge, MaxGaugeog Antal målere leveres.

BasicGauge påberåber sig en Kan kaldes for at få den aktuelle værdi. Vi kan få størrelsen på en samling, den seneste værdi af en BlockingQueue eller enhver værdi, der kræver små beregninger.

Gauge gauge = ny BasicGauge (MonitorConfig.builder ("test") .build (), () -> 2.32); assertEquals (2.32, gauge.getValue (), 0.01);

MaxGauge og MinGauge bruges til at holde styr på henholdsvis maksimum og minimumsværdier:

MaxGauge gauge = ny 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));

NumberGauge (LongGauge, DoubleGauge) indpakker en leveret Nummer (Lang, Dobbelt). For at indsamle målinger ved hjælp af disse målere skal vi sikre, at Nummer er trådsikker.

3.3. Metriske typer - Timer

Timere hjælpe med at måle varigheden af ​​en bestemt begivenhed. Standardimplementeringer er BasicTimer, StatsTimerog BucketTimer.

BasicTimer registrerer samlet tid, antal og anden simpel statistik:

BasicTimer timer = ny BasicTimer (MonitorConfig.builder ("test"). Build (), SECONDS); Stopur stopur = timer.start (); SECONDS.sleep (1); timer.record (2, SECONDS); stopwatch.stop (); assertEquals ("timer skal tælle 1 sekund", 1, timer.getValue (). intValue ()); assertEquals ("timer skal tælle 3 sekunder i alt", 3.0, timer.getTotalTime (), 0.01); assertEquals ("timer skal optage 2 opdateringer", 2, timer.getCount (). intValue ()); assertEquals ("timer skal have maks 2", 2, timer.getMax (), 0,01);

StatsTimer giver meget rigere statistikker ved at prøveudtagning mellem afstemningsintervaller:

System.setProperty ("netflix.servo", "1000"); StatsTimer timer = ny StatsTimer (MonitorConfig. Builder ("test") .build (), ny StatsConfig.Builder () .withComputeFrequencyMillis (2000) .withPercentiles (ny dobbelt [] {99.0, 95.0, 90.0}) .withPublishMax (true) .withPublishMin (true) .withPublishCount (true) .withPublishMean (true) .withPublishStdDev (true) .withPublishVariance (true) .build (), SECONDS); Stopur stopur = timer.start (); SECONDS.sleep (1); timer.record (3, SECONDS); stopwatch.stop (); stopur = timer.start (); timer.record (6, SECONDS); SECONDS.sleep (2); stopwatch.stop (); assertEquals ("timeren skal tælle 12 sekunder i alt", 12, timer.getTotalTime ()); assertEquals ("timeren skal tælle 12 sekunder i alt", 12, timer.getTotalMeasurement ()); assertEquals ("timer skal optage 4 opdateringer", 4, timer.getCount ()); assertEquals ("statistik timer værdi tid-omkostning / opdatering skal være 2", 3, timer.getValue (). intValue ()); endelig kort metricMap = timer.getMonitors (). stream () .collect (toMap (monitor -> getMonitorTagValue (monitor, "statistic"), monitor -> (Number) monitor.getValue ())); assertThat (metricMap.keySet (), containInAnyOrder ("count", "totalTime", "max", "min", "variance", "stdDev", "avg", "percentile_99", "percentile_95", "percentile_90") );

BucketTimer giver en måde at få distributionen af ​​prøver på vedhæftede værdiområder:

BucketTimer timer = ny BucketTimer (MonitorConfig .builder ("test") .build (), ny BucketConfig.Builder () .withBuckets (ny lang [] {2L, 5L}) .withTimeUnit (SECONDS) .build (), SECONDS) ; timer.record (3); timer.record (6); assertEquals ("timeren skal tælle 9 sekunder i alt", 9, timer.getTotalTime (). intValue ()); Kort metricMap = timer.getMonitors (). Stream () .filter (monitor -> monitor.getConfig (). GetTags (). ContainKey ("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)));

For at spore langvarige operationer, der kan vare i timevis, kan vi bruge den sammensatte skærm Varighedstid.

3.4. Metriske typer - Informativt

Vi kan også gøre brug af Informativt overvåge for at registrere beskrivende oplysninger for at hjælpe med fejlfinding og diagnostik. Den eneste implementering er BasicInformational, og dets anvendelse kan ikke være enklere:

BasicInformational information = ny BasicInformational (MonitorConfig.builder ("test"). Build ()); informational.setValue ("indsamlet information");

3.5. MonitorRegistry

Metriske typer er alle af typen Overvåge, som er selve basen for Servo. Vi ved nu, at slags værktøjer indsamler rå metrics, men for at rapportere dataene er vi nødt til at registrere disse skærme.

Bemærk, at hver enkelt konfigurerede skærm skal registreres en gang og kun en gang for at sikre, at metrics er korrekte. Så vi kan registrere skærmene ved hjælp af Singleton-mønster.

Det meste af tiden kan vi bruge DefaultMonitorRegistry at registrere skærme:

Gauge gauge = ny BasicGauge (MonitorConfig.builder ("test") .build (), () -> 2.32); DefaultMonitorRegistry.getInstance (). Register (gauge);

Hvis vi dynamisk vil registrere en skærm, DynamicTimerog DynamicCounter Kan bruges:

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

Bemærk, at dynamisk registrering vil medføre dyre opslag, hver gang værdien opdateres.

Servo giver også flere hjælpemetoder til at registrere skærme, der er erklæret i objekter:

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

Metode registerObject bruger refleksion til at tilføje alle forekomster af Skærme erklæret ved kommentar @Overvåge og tilføj tags, der er erklæret af @MonitorTags:

@Monitor (navn = "integerCounter", type = DataSourceType.COUNTER, beskrivelse = "Samlet antal opdateringshandlinger.") Privat AtomicInteger updateCount = nyt AtomicInteger (0); @MonitorTags private TagList tags = ny BasicTagList (newArrayList (ny BasicTag ("tag-key", "tag-value"))); @Test offentlig ugyldighed givenAnnotatedMonitor_whenUpdated_thenDataCollected () kaster undtagelse {System.setProperty ("servo.pollers", "1000"); Monitors.registerObject ("testObject", dette); assertTrue (Monitors.isObjectRegistered ("testObject", dette)); updateCount.incrementAndGet (); updateCount.incrementAndGet (); SECONDS.sleep (1); Liste metrics = observer.getObservations (); assertThat (metrics, hasSize (greaterThanOrEqualTo (1))); Iterator metricIterator = metrics.iterator (); metricIterator.next (); // spring over den første tomme observation, mens (metricIterator.hasNext ()) {assertThat (metricIterator.next (), hasItem (hasProperty ("config", hasProperty ("name", is ("integerCounter")))); }}

4. Udgiv målinger

Med de indsamlede metrics kan vi offentliggøre det i ethvert format, såsom gengivelse af tidsseriegrafer på forskellige datavisualiseringsplatforme. For at offentliggøre målingerne er vi nødt til at afstemme dataene med jævne mellemrum fra skærmobservationerne.

4.1. MetricPoller

MetricPoller bruges som metrics fetcher. Vi kan hente målinger af MonitorRegistries, JVM, JMX. Ved hjælp af udvidelser kan vi afstemme metrics som Apache-serverstatus og Tomcat-metrics.

MemoryMetricObserver-observatør = ny MemoryMetricObserver (); PollRunnable pollRunnable = ny PollRunnable (ny JvmMetricPoller (), ny BasicMetricFilter (sand), observatør); PollScheduler.getInstance (). Start (); PollScheduler.getInstance (). AddPoller (pollRunnable, 1, SECONDS); SECONDS.sleep (1); PollScheduler.getInstance (). Stop (); Liste metrics = observer.getObservations (); assertThat (metrics, hasSize (greaterThanOrEqualTo (1))); Listnøgler = extractKeys (metrics); assertThat (nøgler, hasItems ("loadedClassCount", "initUsage", "maxUsage", "threadCount"));

Her skabte vi en JvmMetricPoller til afstemningsdata for JVM. Når vi tilføjer polleren til planlæggeren, lader vi afstemningsopgaven køre hvert sekund. Systemets standard poller-konfigurationer er defineret i Pollere, men vi kan specificere pollere, der skal bruges med systemegenskab servo.pollere.

4.2. MetricObserver

Ved måling af målinger, observationer af registrerede MetricObservers vil blive opdateret.

MetricObservers leveres som standard er MemoryMetricObserver, FileMetricObserverog AsyncMetricObserver. Vi har allerede vist, hvordan man bruger MemoryMetricObserver i den foregående kodeeksempel.

I øjeblikket er der flere nyttige udvidelser tilgængelige:

  • AtlasMetricObserver: offentligg metrics til Netflix Atlas for at generere i hukommelse tidsseriedata til analyse
  • CloudWatchMetricObserver: skub metrics til Amazon CloudWatch til måling af metrics og tracking
  • GraphiteObserver: udgiv metrics til Graphite for at gemme og tegne

Vi kan implementere en tilpasset MetricObserver at offentliggøre applikationsmålinger, hvor vi finder det passende. Den eneste ting at bekymre sig om er at håndtere de opdaterede målinger:

offentlig klasse CustomObserver udvider BaseMetricObserver {// ... @Override public void updateImpl (List metrics) {// TODO}}

4.3. Udgiv til Netflix Atlas

Atlas er et andet metrics-relateret værktøj fra Netflix. Det er et værktøj til styring af dimensionelle tidsseriedata, som er et perfekt sted at offentliggøre de metrics, vi indsamlede.

Nu demonstrerer vi, hvordan vi offentliggør vores metrics til Netflix Atlas.

Lad os først tilføje servo-atlas afhængighed af pom.xml:

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

Denne afhængighed inkluderer en AtlasMetricObserver for at hjælpe os med at offentliggøre målinger til Atlas.

Derefter opretter vi en Atlas-server:

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

For at spare tid til testen, lad os indstille trinstørrelsen til 1 sekund i memory.conf, så vi kan generere en tidsseriegraf med tilstrækkelige detaljer om metrics.

Det AtlasMetricObserver kræver en simpel konfiguration og en liste over tags. Metrics for de givne tags skubbes til Atlas:

System.setProperty ("servo.pollers", "1000"); System.setProperty ("servo.atlas.batchSize", "1"); System.setProperty ("servo.atlas.uri", "// localhost: 7101 / api / v1 / publish"); AtlasMetricObserver-observatør = ny AtlasMetricObserver (ny BasicAtlasConfig (), BasicTagList.of ("servo", "tæller")); PollRunnable opgave = ny PollRunnable (ny MonitorRegistryMetricPoller (), ny BasicMetricFilter (true), observatør);

Efter opstart af en PollScheduler med PollRunnable opgave, kan vi offentliggøre metrics til Atlas automatisk:

Counter counter = new BasicCounter (MonitorConfig .builder ("test") .withTag ("servo", "counter") .build ()); DefaultMonitorRegistry .getInstance () .register (tæller); assertThat (atlasValuesOfTag ("servo"), ikke (containString ("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"), indeholderString ("tæller"));

Baseret på metrics kan vi generere en linjegraf ved hjælp af graf API af Atlas:

5. Resume

I denne artikel har vi introduceret, hvordan man bruger Netflix Servo til at indsamle og udgive applikationsmålinger.

Hvis du ikke har læst vores introduktion til Dropwizard Metrics, skal du tjekke den her for en hurtig sammenligning med Servo.

Som altid kan den fulde implementeringskode for denne artikel findes på Github.