Inleiding tot Netflix Servo

1. Overzicht

Netflix Servo is een metrische tool voor Java-applicaties. Servo is vergelijkbaar met Dropwizard Metrics, maar is veel eenvoudiger. Het maakt alleen gebruik van JMX om een ​​eenvoudige interface te bieden voor het blootleggen en publiceren van toepassingsstatistieken.

In dit artikel zullen we introduceren wat Servo biedt en hoe we het kunnen gebruiken om applicatiestatistieken te verzamelen en te publiceren.

2. Maven afhankelijkheden

Voordat we ingaan op de daadwerkelijke implementatie, laten we de Servo-afhankelijkheid toevoegen aan het pom.xml het dossier:

 com.netflix.servo servo-core 0.12.16 

Bovendien zijn er veel extensies beschikbaar, zoals Servo-Apache, Servo-AWS, enz. Wellicht hebben we ze later nodig. De nieuwste versies van deze extensies zijn ook te vinden op Maven Central.

3. Verzamel statistieken

Laten we eerst eens kijken hoe we metrische gegevens van onze applicatie kunnen verzamelen.

Servo biedt vier primaire metrische typen: Teller, Meter, Timer, en Informatief.

3.1. Metrische typen - Teller

Tellers worden gebruikt om incrementering vast te leggen. Veelgebruikte implementaties zijn BasicCounter, Stappenteller, en PeakRateCounter.

BasicCounter doet wat een loket moet doen, duidelijk en ongecompliceerd:

Teller counter = nieuwe BasicCounter (MonitorConfig.builder ("test"). Build ()); assertEquals ("teller moet beginnen met 0", 0, counter.getValue (). intValue ()); counter.increment (); assertEquals ("teller had verhoogd moeten zijn met 1", 1, counter.getValue (). intValue ()); counter.increment (-1); assertEquals ("teller had moeten afnemen met 1", 0, counter.getValue (). intValue ());

PeakRateCounter geeft het maximale aantal voor een gegeven seconde terug tijdens het polling-interval:

Teller counter = nieuwe PeakRateCounter (MonitorConfig.builder ("test"). Build ()); assertEquals ("teller moet beginnen met 0", 0, counter.getValue (). intValue ()); counter.increment (); SECONDEN. Slaap (1); counter.increment (); counter.increment (); assertEquals ("pieksnelheid moet 2 zijn", 2, counter.getValue (). intValue ());

In tegenstelling tot andere tellers, Stappenteller opnamesnelheid per seconde van het vorige polling-interval:

System.setProperty ("servo.pollers", "1000"); Teller counter = nieuwe StepCounter (MonitorConfig.builder ("test"). Build ()); assertEquals ("teller moet beginnen met tarief 0.0", 0.0, counter.getValue ()); counter.increment (); SECONDEN. Slaap (1); assertEquals ("counter rate had verhoogd moeten zijn naar 1,0", 1,0, counter.getValue ());

Merk op dat we de servo.pollers naar 1000 in de bovenstaande code. Dat was om het polling-interval in te stellen op 1 seconde in plaats van standaard intervallen van 60 seconden en 10 seconden. We zullen hier later meer op terugkomen.

3.2. Metrische typen - Meter

Meter is een eenvoudige monitor die de huidige waarde retourneert. Basismeter, MinGauge, MaxGauge, en NumberGauges voorzien.

Basismeter roept een Oproepbaar om de huidige waarde te krijgen. We kunnen de grootte van een verzameling krijgen, de laatste waarde van een BlockingQueue of elke waarde die kleine berekeningen vereist.

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

MaxGauge en MinGauge worden gebruikt om respectievelijk de maximale en minimale waarden bij te houden:

MaxGauge meter = nieuwe 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));

Cijfermaat (Langspoor, DoubleGauge) wraps een voorzien Aantal (Lang, Dubbele). Om metrische gegevens te verzamelen met behulp van deze meters, moeten we ervoor zorgen dat de Aantal is draadveilig.

3.3. Metrische typen - Timer

Timers helpen bij het meten van de duur van een bepaalde gebeurtenis. Standaard implementaties zijn BasicTimer, StatsTimer, en BucketTimer.

BasicTimer registreert de totale tijd, het aantal en andere eenvoudige statistieken:

BasicTimer timer = nieuwe BasicTimer (MonitorConfig.builder ("test"). Build (), SECONDS); Stopwatch stopwatch = timer.start (); SECONDEN. Slaap (1); timer.record (2, SECONDEN); stopwatch.stop (); assertEquals ("timer moet 1 seconde tellen", 1, timer.getValue (). intValue ()); assertEquals ("timer moet in totaal 3 seconden tellen", 3.0, timer.getTotalTime (), 0.01); assertEquals ("timer moet 2 updates opnemen", 2, timer.getCount (). intValue ()); assertEquals ("timer moet max 2 hebben", 2, timer.getMax (), 0.01);

StatsTimer biedt veel rijkere statistieken door steekproeven tussen polling-intervallen:

System.setProperty ("netflix.servo", "1000"); StatsTimer timer = nieuwe StatsTimer (MonitorConfig .builder ("test") .build (), nieuwe StatsConfig.Builder () .withComputeFrequencyMillis (2000) .withPercentiles (nieuwe dubbele [] {99.0, 95.0, 90.0}) .withPublishMax (true) .withPublishMin (true) .withPublishCount (true) .withPublishMean (true) .withPublishStdDev (true) .withPublishVariance (true) .build (), SECONDS); Stopwatch stopwatch = timer.start (); SECONDEN. Slaap (1); timer.record (3, SECONDEN); stopwatch.stop (); stopwatch = timer.start (); timer.record (6, SECONDEN); SECONDEN. Slaap (2); stopwatch.stop (); assertEquals ("timer moet in totaal 12 seconden tellen", 12, timer.getTotalTime ()); assertEquals ("timer moet in totaal 12 seconden tellen", 12, timer.getTotalMeasurement ()); assertEquals ("timer moet 4 updates opnemen", 4, timer.getCount ()); assertEquals ("stats timer waarde tijd-kosten / update moet 2 zijn", 3, timer.getValue (). intValue ()); laatste kaart metricMap = timer.getMonitors (). stream () .collect (toMap (monitor -> getMonitorTagValue (monitor, "statistiek"), monitor -> (nummer) monitor.getValue ())); assertThat (metricMap.keySet (), containsInAnyOrder ("count", "totalTime", "max", "min", "variance", "stdDev", "avg", "percentile_99", "percentile_95", "percentile_90") );

BucketTimer biedt een manier om de verdeling van steekproeven te krijgen door waardebereiken in te delen:

BucketTimer timer = nieuwe BucketTimer (MonitorConfig .builder ("test") .build (), nieuwe BucketConfig.Builder () .withBuckets (nieuw lang [] {2L, 5L}) .withTimeUnit (SECONDS) .build (), SECONDS) ; timer.record (3); timer.record (6); assertEquals ("timer moet in totaal 9 seconden tellen", 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)));

Om langdurige bewerkingen die uren kunnen duren bij te houden, kunnen we de composietmonitor gebruiken Duur Timer.

3.4. Metrische typen - Informatief

Ook kunnen we gebruik maken van de Informatief monitor om beschrijvende informatie vast te leggen om te helpen bij foutopsporing en diagnostiek. De enige implementatie is Basisinformatie, en het gebruik ervan kan niet eenvoudiger zijn:

BasicInformational informatief = nieuwe BasicInformational (MonitorConfig.builder ("test"). Build ()); informational.setValue ("verzamelde informatie");

3.5. MonitorRegistry

De metrische typen zijn allemaal van het type Monitor, dat is de basis van Servo. We weten nu dat soorten tools onbewerkte statistieken verzamelen, maar om de gegevens te rapporteren, moeten we deze monitoren registreren.

Houd er rekening mee dat elke afzonderlijke geconfigureerde monitor één keer en slechts één keer moet worden geregistreerd om de juistheid van de statistieken te garanderen. We kunnen de monitoren dus registreren met behulp van het Singleton-patroon.

Meestal kunnen we gebruiken DefaultMonitorRegistry monitoren registreren:

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

Als we een monitor dynamisch willen registreren, DynamicTimer, en DynamicCounter kan worden gebruikt:

DynamicCounter.increment ("monitornaam", "tag-key", "tag-waarde");

Merk op dat dynamische registratie elke keer dat de waarde wordt bijgewerkt, een dure opzoekbewerking zou veroorzaken.

Servo biedt ook verschillende hulpmethoden om monitors te registreren die in objecten zijn gedeclareerd:

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

Methode registerObject zal reflectie gebruiken om alle instanties van toe te voegen Monitoren verklaard door annotatie @Monitor en voeg tags toe die zijn gedeclareerd door @MonitorTags:

@Monitor (name = "integerCounter", type = DataSourceType.COUNTER, description = "Totaal aantal updatebewerkingen.") Private AtomicInteger updateCount = nieuwe AtomicInteger (0); @MonitorTags private TagList-tags = nieuwe BasicTagList (newArrayList (nieuwe BasicTag ("tag-key", "tag-value"))); @Test openbare leegte gegevenAnnotatedMonitor_whenUpdated_thenDataCollected () gooit uitzondering {System.setProperty ("servo.pollers", "1000"); Monitors.registerObject ("testObject", dit); assertTrue (Monitors.isObjectRegistered ("testObject", this)); updateCount.incrementAndGet (); updateCount.incrementAndGet (); SECONDEN. Slaap (1); Lijst metrics = observer.getObservations (); assertThat (metrics, hasSize (groterThanOrEqualTo (1))); Iterator metricIterator = metrics.iterator (); metricIterator.next (); // sla de eerste lege observatie over terwijl (metricIterator.hasNext ()) {assertThat (metricIterator.next (), hasItem (hasProperty ("config", hasProperty ("name", is ("integerCounter"))))); }}

4. Publiceer statistieken

Met de verzamelde statistieken kunnen we deze in elk formaat publiceren, zoals het weergeven van tijdreeksgrafieken op verschillende datavisualisatieplatforms. Om de metrische gegevens te publiceren, moeten we de gegevens periodiek uit de monitorwaarnemingen opvragen.

4.1. MetricPoller

MetricPoller wordt gebruikt als het ophalen van metrische gegevens. We kunnen statistieken ophalen van MonitorRegistries, JVM, JMX. Met behulp van extensies kunnen we metrische gegevens zoals Apache-serverstatus en Tomcat-metrische gegevens pollen.

MemoryMetricObserver waarnemer = nieuwe MemoryMetricObserver (); PollRunnable pollRunnable = nieuwe PollRunnable (nieuwe JvmMetricPoller (), nieuwe BasicMetricFilter (true), waarnemer); PollScheduler.getInstance (). Start (); PollScheduler.getInstance (). AddPoller (pollRunnable, 1, SECONDS); SECONDEN. Slaap (1); PollScheduler.getInstance (). Stop (); Lijst metrics = observer.getObservations (); assertThat (metrics, hasSize (groterThanOrEqualTo (1))); Lijstsleutels = extractKeys (metrische gegevens); assertThat (keys, hasItems ("loadedClassCount", "initUsage", "maxUsage", "threadCount"));

Hier hebben we een JvmMetricPoller om metrische gegevens van JVM te peilen. Bij het toevoegen van de poller aan de planner, laten we de poll-taak elke seconde uitvoeren. De standaard pollerconfiguraties van het systeem zijn gedefinieerd in Pollers, maar we kunnen pollers specificeren om te gebruiken met systeemeigenschappen servo.pollers.

4.2. MetricObserver

Bij het pollen van statistieken, observaties van geregistreerde MetricObservers zal geüpdatet worden.

MetricObservers standaard geleverd zijn MemoryMetricObserver, FileMetricObserver, en AsyncMetricObserver. We hebben al laten zien hoe te gebruiken MemoryMetricObserver in het vorige codevoorbeeld.

Momenteel zijn er verschillende handige extensies beschikbaar:

  • AtlasMetricObserver: publiceer statistieken naar Netflix Atlas om in het geheugen tijdreeksgegevens te genereren voor analyse
  • CloudWatchMetricObserver: push metrics naar Amazon CloudWatch voor het monitoren en volgen van metrics
  • GraphiteObserver: publiceer statistieken naar Graphite om op te slaan en te tekenen

We kunnen een maatwerk implementeren MetricObserver om toepassingsstatistieken te publiceren naar waar wij dat nodig achten. Het enige dat u interesseert, is het omgaan met de bijgewerkte statistieken:

public class CustomObserver breidt BaseMetricObserver uit {// ... @Override public void updateImpl (List metrics) {// TODO}}

4.3. Publiceer naar Netflix Atlas

Atlas is een andere metrics-gerelateerde tool van Netflix. Het is een tool voor het beheren van dimensionale tijdreeksgegevens, wat een perfecte plek is om de statistieken die we hebben verzameld te publiceren.

Nu laten we zien hoe we onze statistieken naar Netflix Atlas kunnen publiceren.

Laten we eerst het servo-atlas afhankelijkheid van de pom.xml:

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

Deze afhankelijkheid omvat een AtlasMetricObserver om ons te helpen statistieken te publiceren naar Atlas.

Vervolgens zullen we een Atlas-server opzetten:

$ 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

Om tijd te besparen voor de test, stellen we de stapgrootte in op 1 seconde in memory.conf, zodat we een tijdreeksgrafiek kunnen genereren met voldoende details van de statistieken.

De AtlasMetricObserver vereist een eenvoudige configuratie en een lijst met tags. Metrische gegevens van de gegeven tags worden naar Atlas gepusht:

System.setProperty ("servo.pollers", "1000"); System.setProperty ("servo.atlas.batchSize", "1"); System.setProperty ("servo.atlas.uri", "// localhost: 7101 / api / v1 / publish"); AtlasMetricObserver observer = nieuwe AtlasMetricObserver (nieuwe BasicAtlasConfig (), BasicTagList.of ("servo", "counter")); PollRunnable-taak = nieuwe PollRunnable (nieuwe MonitorRegistryMetricPoller (), nieuwe BasicMetricFilter (true), waarnemer);

Na het opstarten van een PollScheduler met de PollRunnable taak, kunnen we statistieken automatisch naar Atlas publiceren:

Teller counter = nieuwe BasicCounter (MonitorConfig .builder ("test") .withTag ("servo", "counter") .build ()); DefaultMonitorRegistry .getInstance () .register (teller); assertThat (atlasValuesOfTag ("servo"), niet (containsString ("counter"))); voor (int i = 0; i <3; i ++) {counter.increment (RandomUtils.nextInt (10)); SECONDEN. Slaap (1); counter.increment (-1 * RandomUtils.nextInt (10)); SECONDEN. Slaap (1); } assertThat (atlasValuesOfTag ("servo"), containsString ("counter"));

Op basis van de statistieken kunnen we een lijngrafiek genereren met behulp van de Graph API van Atlas:

5. Samenvatting

In dit artikel hebben we uitgelegd hoe u Netflix Servo kunt gebruiken om toepassingsstatistieken te verzamelen en te publiceren.

Als je onze inleiding tot Dropwizard Metrics nog niet hebt gelezen, bekijk deze dan hier voor een snelle vergelijking met Servo.

Zoals altijd is de volledige implementatiecode van dit artikel te vinden op Github.