Mjerni podaci za vaš Spring REST API

OSTALO Vrh

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ

1. Pregled

U ovom ćemo se vodiču integrirati osnovne metrike u Spring REST API.

Prvo ćemo izraditi metričku funkcionalnost pomoću jednostavnih filtera servleta, a zatim pomoću pokretača proljetnog pokretanja.

2. The web.xml

Počnimo s registracijom filtra - “MetricFilter" - u web.xml naše aplikacije:

 metricFilter org.baeldung.web.metric.MetricFilter metricFilter / * 

Imajte na umu kako mapiramo filtar da pokrije sve pristigle zahtjeve - “/*” - što je naravno u potpunosti podesivo.

3. Servlet filtar

Sada - izradimo naš prilagođeni filtar:

javna klasa MetricFilter provodi Filter {private MetricService metricService; @Override javna void init (FilterConfig config) baca ServletException {metricService = (MetricService) WebApplicationContextUtils .getRequiredWebApplicationContext (config.getServletContext ()) .getBean ("metricService"); } @Override public void doFilter (zahtjev ServletRequest, odgovor ServletResponse, lanac FilterChain) baca java.io.IOException, ServletException {HttpServletRequest httpRequest = ((HttpServletRequest) zahtjev); Niz req = httpRequest.getMethod () + "" + httpRequest.getRequestURI (); chain.doFilter (zahtjev, odgovor); int status = ((HttpServletResponse) odgovor) .getStatus (); metricService.increaseCount (zahtjev, status); }}

Budući da filtar nije standardni grah, nećemo ubrizgati metricService ali umjesto toga dohvatite ga ručno - putem ServletContext.

Također imajte na umu da nastavljamo izvršavanje lanca filtra pozivanjem doFilter API ovdje.

4. Metrika - Broji se statusni kod

Dalje - pogledajmo naš jednostavan MetricService:

@Service javna klasa MetricService {private ConcurrentMap statusMetric; javna MetricService () {statusMetric = new ConcurrentHashMap (); } javna void povećanjeCount (zahtjev za nizom, int status) {Integer statusCount = statusMetric.get (status); if (statusCount == null) {statusMetric.put (status, 1); } else {statusMetric.put (status, statusCount + 1); }} javna karta getStatusMetric () {return statusMetric; }}

Koristimo memoriju Istodobna karta da zadrži brojeve za svaku vrstu HTTP statusnog koda.

Sada ćemo - kako bismo prikazali ovaj osnovni mjerni podatak - preslikati ga u Kontroler metoda:

@RequestMapping (value = "/ status-metric", method = RequestMethod.GET) @ResponseBody javna karta getStatusMetric () {return metricService.getStatusMetric (); }

I evo primjera odgovora:

{ "404":1, "200":6, "409":1 }

5. Metrika - statusni kodovi prema zahtjevu

Sljedeći - zabilježimo mjerne podatke za Counts prema Zahtjevu:

@Service javna klasa MetricService {private ConcurrentMap metricMap; javna praznina povećanjeCount (zahtjev za nizom, int status) {ConcurrentHashMap statusMap = metricMap.get (zahtjev); if (statusMap == null) {statusMap = new ConcurrentHashMap (); } Cjelobrojni broj = statusMap.get (status); if (count == null) {count = 1; } ostalo {count ++; } statusMap.put (status, count); metricMap.put (zahtjev, statusMap); } javna karta getFullMetric () {return metricMap; }}

Rezultate mjernih podataka prikazat ćemo putem API-ja:

@RequestMapping (value = "/ metric", method = RequestMethod.GET) @ResponseBody javna karta getMetric () {return metricService.getFullMetric (); }

Evo kako izgledaju ove mjerne vrijednosti:

{"GET / users": {"200": 6, "409": 1}, "GET / users / 1": {"404": 1}}

Prema gornjem primjeru API je imao slijedeću aktivnost:

  • „7“ zahtjeva za „GET / korisnici
  • Njih 6 rezultiralo je odgovorima statusnog koda „200“, a samo jedan odgovor „409“

6. Metrika - Podaci vremenskih serija

Sveukupno brojanje donekle je korisno u aplikaciji, ali ako je sustav pokrenut značajno vrijeme - teško je reći što ove metrike zapravo znače.

Potreban vam je kontekst vremena kako bi podaci imali smisla i bili lako protumačeni.

Izgradimo sada jednostavnu vremensku metriku; vodit ćemo evidenciju brojanja statusnih kodova u minuti - kako slijedi:

@Service javna klasa MetricService {private ConcurrentMap karta vremena; privatni statički SimpleDateFormat dateFormat = novi SimpleDateFormat ("yyyy-MM-dd HH: mm"); public void увеличениеCount (zahtjev za nizom, int status) {Vrijeme niza = dateFormat.format (novi datum ()); ConcurrentHashMap statusMap = timeMap.get (vrijeme); if (statusMap == null) {statusMap = new ConcurrentHashMap (); } Cjelobrojni broj = statusMap.get (status); if (count == null) {count = 1; } ostalo {count ++; } statusMap.put (status, count); timeMap.put (vrijeme, statusMap); }}

I getGraphData ():

javni objekt [] [] getGraphData () {int colCount = statusMetric.keySet (). size () + 1; Postavi allStatus = statusMetric.keySet (); int rowCount = timeMap.keySet (). size () + 1; Object [] [] rezultat = novi Object [rowCount] [colCount]; rezultat [0] [0] = "Vrijeme"; int j = 1; za (int status: allStatus) {rezultat [0] [j] = status; j ++; } int i = 1; ConcurrentMap tempMap; za (Unos entry: timeMap.entrySet ()) {result [i] [0] = entry.getKey (); tempMap = entry.getValue (); za (j = 1; j <colCount; j ++) {rezultat [i] [j] = tempMap.get (rezultat [0] [j]); if (rezultat [i] [j] == null) {rezultat [i] [j] = 0; }} i ++; } vratiti rezultat; }

Sada ćemo ovo preslikati na API:

@RequestMapping (value = "/ metric-graph-data", method = RequestMethod.GET) @ResponseBody javni objekt [] [] getMetricData () {return metricService.getGraphData (); }

I na kraju - pružit ćemo ga pomoću Google Charts:

  Metrički grafikon google.load ("vizualizacija", "1", {paketi: ["corechart"]}); function drawChart () {$ .get ("/ metric-graph-data", function (mydata) {var data = google.visualization.arrayToDataTable (mydata); var options = {title: 'Metrika web lokacije', hAxis: {title : 'Time', titleTextStyle: {color: '# 333'}}, vAxis: {minValue: 0}}; var chart = new google.visualization.AreaChart (document.getElementById ('chart_div')); chart.draw ( podaci, opcije);}); } 

7. Korištenje aktuatora Spring Boot 1.x

U sljedećih nekoliko odjeljaka pridružit ćemo se aktuatoru u Spring Boot-u kako bismo predstavili naše mjerne podatke.

Prvo - morat ćemo dodati ovisnost aktuatora u našu pom.xml:

 org.springframework.boot opruga-pokretač-pokretač-pokretač 

7.1. The MetricFilter

Dalje - možemo okrenuti MetricFilter - u stvarni proljetni grah:

@Component javna klasa MetricFilter implementira Filter {@Autowired private MetricService metricService; @Override public void doFilter (ServletRequest zahtjev, ServletResponse odgovor, FilterChain lanac) baca java.io.IOException, ServletException {chain.doFilter (zahtjev, odgovor); int status = ((HttpServletResponse) odgovor) .getStatus (); metricService.increaseCount (status); }}

To je, naravno, malo pojednostavljenje - ali ono koje vrijedi učiniti da biste se riješili prethodno ručnog ožičenja ovisnosti.

7.2. Koristeći CounterService

Koristimo sada CounterService za brojanje događaja za svaki statusni kod:

@Service javna klasa MetricService {@Autowired private CounterService counter; privatni popis statusList; povećanje javne praznineCount (int status) {counter.increment ("status." + status); if (! statusList.contens ("counter.status." + status)) {statusList.add ("counter.status." + status); }}}

7.3. Izvoz mjernih podataka pomoću MetricRepository

Dalje - moramo izvesti mjerne podatke - pomoću MetricRepository:

@Service javna klasa MetricService {@Autowired private MetricRepository repo; privatni Popis statusMetric; privatni popis statusList; @Scheduled (fixedDelay = 60000) private void exportMetrics () {Metrička metrika; ArrayList statusCount = novi ArrayList (); za (status niza: statusList) {metric = repo.findOne (status); if (metric! = null) {statusCount.add (metric.getValue (). intValue ()); repo.reset (status); } else {statusCount.add (0); }} statusMetric.add (statusCount); }}

Imajte na umu da pohranjujemo brojeve od kodovi stanja u minuti.

7.4. Proljetni čizme PublicMetrics

Također možemo koristiti Spring Boot PublicMetrics izvesti mjerne podatke umjesto da koristimo vlastite filtre - kako slijedi:

Prvo, imamo zakazanu zadaću izvoz podataka u minuti:

@Autowired private MetricReaderPublicMetrics publicMetrics; privatni Popis statusMetricsByMinute; privatni popis statusList; privatni statički konačni SimpleDateFormat dateFormat = novi SimpleDateFormat ("gggg-MM-dd VH: mm"); @Scheduled (fixedDelay = 60000) private void exportMetrics () {ArrayList lastMinuteStatuses = initializeStatuses (statusList.size ()); za (Metric counterMetric: publicMetrics.metrics ()) {updateMetrics (counterMetric, lastMinuteStatuses); } statusMetricsByMinute.add (lastMinuteStatuses); }

Moramo, naravno, inicijalizirati popis HTTP statusnih kodova:

privatni ArrayList initializeStatuses (int veličina) {ArrayList counterList = novi ArrayList (); za (int i = 0; i <veličina; i ++) {counterList.add (0); } return counterList; }

A onda ćemo zapravo ažurirati mjerne podatke pomoću broj statusnog koda:

privatna praznina updateMetrics (Metric counterMetric, ArrayList statusCount) {Status niza = ""; indeks int = -1; int oldCount = 0; if (counterMetric.getName (). contains ("counter.status.")) {status = counterMetric.getName (). substring (15, 18); // primjer 404, 200 appendStatusIfNotExist (status, statusCount); indeks = statusList.indexOf (status); oldCount = statusCount.get (index) == null? 0: statusCount.get (indeks); statusCount.set (index, counterMetric.getValue (). intValue () + oldCount); }} private void appendStatusIfNotExist (Status niza, ArrayList statusCount) {if (! statusList.contens (status)) {statusList.add (status); statusCount.add (0); }}

Imajte na umu da:

  • PublicMetics naziv brojača statusa započinje s “brojač.status" na primjer "brojač.status.200.korijen
  • Na našem popisu evidentiramo brojanje statusa po minuti statusMetricsByMinute

Prikupljene podatke možemo izvesti kako bismo ih nacrtali u grafikonu - kako slijedi:

javni objekt [] [] getGraphData () {Datum trenutni = novi datum (); int colCount = statusList.size () + 1; int rowCount = statusMetricsByMinute.size () + 1; Object [] [] rezultat = novi Object [rowCount] [colCount]; rezultat [0] [0] = "Vrijeme"; int j = 1; za (status niza: statusList) {rezultat [0] [j] = status; j ++; } for (int i = 1; i <rowCount; i ++) {rezultat [i] [0] = dateFormat.format (novi Datum (current.getTime () - (60000 * (rowCount - i)))); } Popis minuteStatuses; Popis zadnji = novi ArrayList (); for (int i = 1; i <rowCount; i ++) {minuteOfStatuses = statusMetricsByMinute.get (i - 1); za (j = 1; j = j? last.get (j - 1): 0); } while (j <colCount) {rezultat [i] [j] = 0; j ++; } last = minuteStatuses; } vratiti rezultat; }

7.5. Crtanje grafikona pomoću metrike

Konačno - predstavimo ove mjerne podatke putem dvodimenzionalnog niza - kako bismo ih mogli grafički prikazati:

javni objekt [] [] getGraphData () {Datum trenutni = novi datum (); int colCount = statusList.size () + 1; int rowCount = statusMetric.size () + 1; Object [] [] rezultat = novi Object [rowCount] [colCount]; rezultat [0] [0] = "Vrijeme"; int j = 1; za (status niza: statusList) {rezultat [0] [j] = status; j ++; } ArrayList temp; for (int i = 1; i <rowCount; i ++) {temp = statusMetric.get (i - 1); rezultat [i] [0] = dateFormat.format (novi datum (current.getTime () - (60000 * (rowCount - i)))); za (j = 1; j <= temp.size (); j ++) {rezultat [i] [j] = temp.get (j - 1); } while (j <colCount) {rezultat [i] [j] = 0; j ++; }} vratiti rezultat; }

I evo naše metode Controller getMetricData ():

@RequestMapping (value = "/ metric-graph-data", method = RequestMethod.GET) @ResponseBody javni objekt [] [] getMetricData () {return metricService.getGraphData (); }

I evo primjera odgovora:

[["Vrijeme", "counter.status.302", "counter.status.200", "counter.status.304"], ["26.03.2015 19:59", 3,12,7], ["26. 03. 2015 20:00", 0,4,1]]

8. Korištenje aktuatora Spring Boot 2.x

U Spring Boot 2, API-ji Spring Actuatora svjedočili su velikoj promjeni. Proljetne vlastite metrike zamijenjene su s Mikrometar. Pa napišimo isti primjer metrike gore sa Mikrometar.

8.1. Zamjena CounterService S MeterRegistry

Kako naša aplikacija Spring Boot već ovisi o pokretaču pokretača, Micrometer je već automatski konfiguriran. Možemo ubrizgati MeterRegistry umjesto CounterService. Možemo koristiti različite vrste Metar za hvatanje metrike. The Brojač je jedan od mjerača:

@Automobilski privatni MeterRegistry registar; privatni popis statusList; @Preuzmi javnu porast praznineCount (konačni int status) {String counterName = "counter.status." + status; registry.counter (counterName) .increment (1); if (! statusList.contens (counterName)) {statusList.add (counterName); }}

8.2. Izvoz brojeva pomoću MeterRegistry

U mikrometar možemo izvesti Brojač vrijednosti pomoću MeterRegistry:

@Scheduled (fixedDelay = 60000) private void exportMetrics () {ArrayList statusCount = new ArrayList (); for (String status: statusList) {Search search = registry.find (status); if (search! = null) {Brojač brojača = search.counter (); statusCount.add (brojač! = null? ((int) brojač.broj ()): 0); registry.remove (brojač); } else {statusCount.add (0); }} statusMetricsByMinute.add (statusCount); }

8.3. Objavljivanje metrika pomoću Brojila

Sada također možemo objavljivati ​​Metrics koristeći Brojila MeterRegistry:

@Scheduled (fixedDelay = 60000) private void exportMetrics () {ArrayList lastMinuteStatuses = initializeStatuses (statusList.size ()); za (Meter counterMetric: publicMetrics.getMeters ()) {updateMetrics (counterMetric, lastMinuteStatuses); } statusMetricsByMinute.add (lastMinuteStatuses); } private void updateMetrics (konačni brojač brojača, konačni ArrayList statusCount) {Status niza = ""; indeks int = -1; int oldCount = 0; if (counterMetric.getId (). getName (). contains ("counter.status.")) {status = counterMetric.getId (). getName (). substring (15, 18); // primjer 404, 200 appendStatusIfNotExist (status, statusCount); indeks = statusList.indexOf (status); oldCount = statusCount.get (index) == null? 0: statusCount.get (indeks); statusCount.set (index, (int) ((Counter) counterMetric) .count () + oldCount); }}

9. Zaključak

U ovom smo članku istražili nekoliko jednostavnih načina za izgradnju nekih osnovnih mjernih mogućnosti u web aplikaciju Spring.

Imajte na umu da su brojači nisu zaštićeni nitima - pa možda neće biti točni bez korištenja nečega poput atomskih brojeva. To je bilo namjerno samo zato što bi delta trebala biti mala i 100% preciznost nije cilj - već je rano uočavanje trendova.

Postoje naravno zreliji načini za bilježenje HTTP mjernih podataka u aplikaciji, ali ovo je jednostavan, lagan i nadasve koristan način da to učinite bez dodatne složenosti punopravnog alata.

Potpuna provedba ovog članka može se naći u projektu GitHub.

OSTALO dno

Upravo sam najavio novo Uči proljeće tečaj, usredotočen na osnove Spring 5 i Spring Boot 2:

>> PROVJERITE TEČAJ