Monitoring

Amikor az alkalmazásunkat szeretnénk élesíteni (production mód), akkor több eszköz is segítségünkre lehet. Az egyik ilyen eszköz a Spring által biztosított actuator. Az actuator használatához a következő függőséget kell megadnunk a pom.xml-ben:

1
2
3
4
5
6
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

Végpontok

Az actuator, amikor a classpath-ba kerül, akkor alapból biztosít néhány végpontot, amelyeken a rendszerre vonatkozó információkat monitorozhatjuk (például a health végponton). Ezeken a built-in végpontokon felül mi magunk is adhatunk hozzá extra végpontokat az egyedi igényeinknek megfelelően.

A végpontokat akár egyesével engedélyezhetjük és letilthatjuk. Az actuator végpontjait HTTP vagy JMX segítségével vezethetjük ki a felhasználók számára.

Ha már emlegettük a JMX-et, de valójában mi is az?

A JMX (Java Management Extensions) a Java 1.5-ös verziójával látta meg a napvilágot és azóta is széles körben támogatott. Lényegében egy infrastruktúrát ad Java alkalmazások monitorozásához és menedzseléséhez, mely infrastruktúra konfigurálható, skálázható, megbízható és többé kevésbé user-friendly. A JMX egy központi fogalma az úgynevezett MBean (Managed Bean).

A JMX architektúra 3 rétegből áll:

  • Agent réteg: A core komponens (MBeanServer), mely egy registry-t tart számon, ahova a manadzselt MBean-ek kerülnek regisztrálásra. Egy interfészt is biztosít ezen MBean-ekhez történő hozzáférésére.
  • Instrumentation vagy Probe réteg: A monitorozásban résztvevő erőforrások instrumentálását teszi lehetővé.
  • Remote Management réteg: Távoli hozzáférést biztosít (adapterek és connectorok) segítségével az MBeanServer-hez. A connectorok segítségével változatos formában férhetünk hozzá az MBeanServer-hez (RMI, IIOP, JMS, WS-*, ...), míg az adapterek segítségével változatos protokollokat is használhatunk (HTML/HTTP, WML/HTTP, ...)

A JMX-et változatos formában használhatjuk, de általában a JConsole-t szokták alkalmazni.

A továbbiakban is inkább a HTTP alapú monitorozásra szorítkozunk, így mélyebben nem foglalkozunk a JMX-el.

HTTP esetében alapból a /actuator prefixszel vannak ellátva a HTTP végpontok (Pl.: /actuator/health).

Megjegyzés

Az alap /actuator prefix-et megváltoztathatjuk az application.properties-ben a következőképpen:

1
management.endpoints.web.base-path=/admin

A következő alap végpontok érhetőek el (csak a számunkra fontosabbakat listázzuk):

  • beans: Az alkalmazásban szereplő összes bean-t kilistázza
  • caches: Az elérhető cache-elt objektumokat listázza
  • conditions: Az alkalmazás konfigurációihoz tartozó feltételek kiértékelését mutatja meg, illetve az auto-konfigurált osztályokat (azt is megadja, hogy miért vagy miért nem volt egyezés rájuk).
  • configprops: Egy teljes (összefésült) listát ad az összes @ConfigurationProperties-ről.
  • env: A Spring ConfigurableEnvironment által tartalmazott property-k listáját adja meg.
  • health: Health infot ad az alkalmazásról
  • httptrace: HTTP trace info (alapból az utolsó 100 HTTP request-response párt adja meg). Szükséges hozzá egy HttpTraceRepository bean.
  • info: Tetszőleges információ az alkalmazásról
  • loggers: Loggerek konfigurációjának mutatása, illetve módosítása is.
  • metrics: Az alkalmazás metrikáit mutatja meg.
  • mappings: Minden egyes @RequestMapping útvonalat listáz
  • scheduledtasks: Beütemezett feladatok listáját mutatja meg
  • sessions: A felhasználói session-ök lekérdezése és törlése. Servlet alapú web alkalmazásnak kell lennie, mely a Spring-es Session-t használja.
  • shutdown: Az alkalmazás normális leállítását teszi lehetővé. Alapból le van tiltva.
  • threaddump: Thread dump végrehajtása

Például a beans végpontra történő kérés a következő felépítésű válasszal tér vissza:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "contexts" : {
    "application" : {
      "beans" : {
        "defaultServletHandlerMapping" : {
          "aliases" : [ ],
          "scope" : "singleton",
          "type" : "org.springframework.web.servlet.HandlerMapping",
          "resource" : "class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]",
          "dependencies" : [ ]
        },
        "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration" : {
          "aliases" : [ ],
          "scope" : "singleton",
          "type" : "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration",
          "dependencies" : [ ]
        },
        "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration" : {
          "aliases" : [ ],
          "scope" : "singleton",
          "type" : "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration",
          "dependencies" : [ ]
        }
      }
    }
  }
}

A végpontok API dokumentációja megtalálható itt.

Végpontok engedélyezése

Alapból minden végpont engedélyezve van kivéve a shutdown-t. A végpontok engedélyezéséhez a következő property-t kell megadnunk:

1
management.endpoint.<id>.enabled=true

, ahol az <id> a megfelelő végpont azonosítója (például: shutdown).

Arra is lehetőség van, hogy alapból kikapcsoljunk minden végpontot és csak a számunkra hasznosakat engedélyezzük, melyhez a következőt kell megadnunk az application.properties állományban:

1
2
management.endpoints.enabled-by-default=false
management.endpoint.info.enabled=true

A fenti részlet az info végpontot engedélyezi, a többit viszont alapból kikapcsolt állapotba rakja.

Az engedélyezés mellett a végpontokat expose-olni is kell, hogy a végpont hívható legyen.

A két koncepció között fontos különbség van. Az engedélyezés során a megfelelő végponthoz tartozó bean-ek regisztrálásra kerülnek, de a végpont maga még nem lesz kívülről látható, ehhez kell az expose.

Alapértelmezetten csak a health és az info végpontok vannak kivezetve, melyet rögtön láthatunk ha kiadunk egy GET kérést a http://localhost:8080/actuator címre. A visszakapott JSON formátuma HATEOAS alapú, mint azt az alábbi részlet is mutatja.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    "_links":
    {
        "self":
        {
            "href":"http://localhost:8080/actuator",
            "templated":false
        },
        "health":
        {
            "href":"http://localhost:8080/actuator/health"
            ,"templated":false
        },
        "health-path":
        {
            "href":"http://localhost:8080/actuator/health/{*path}",
            "templated":true
        },
        "info":
        {
            "href":"http://localhost:8080/actuator/info",
            "templated":false
        }
    }
}

Ez a korlátozás nyilvánvalóan az érzékeny adatok védelme érdekében történik. Ennek megváltoztatására az include és exclude használatosak (mind JMX és HTTP környezetben is):

1
2
3
4
management.endpoints.jmx.exposure.exclude
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.exclude
management.endpoints.web.exposure.include=info,health

A JMX alapból mindent kienged, míg HTTP alapon csak az info és a health engedélyezettek.

Visszatérve az expose-hoz, akár saját stratégiákat is implementálhatunk, hogy mikor szeretnénk egy-egy végpontot kiengedni, melyhez egy EndpointFilter bean-t kell regisztrálnunk a konténerben.

Az actuator nyújtotta lehetőségeket további platformokon hasznosíthatjuk, mint például CloudFoundry vagy Spring Boot Admin.

Security és az Actuator

Azt már láttuk, hogy biztonsági megfontolások miatt bizonyos végpontokat nem enged ki a rendszer. Alapvetően a health végpontot viszont elérhetjük, de az csak annyit árul el a rendszerről, hogy az fut-e vagy sem. A health további információkat is képes szolgáltatni, amennyiben a következő property-t megfelelően állítjuk be:

1
management.endpoint.health.show-details=never

A never helyett használhatjuk az always és a when_authorized értékeket. A when_authorized beállításkor szoros együttműködést figyelhetünk meg a Spring Security-vel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().requestMatchers(EndpointRequest.to(HealthEndpoint.class))
            .permitAll()
            .requestMatchers(EndPointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
            .and().httpBasic();
    }

Saját végpontok implementálása

Amennyiben egy @Bean-t ellátunk a @Endpoint annotációval is, akkor annak @ReadOperation-el, @WriteOperation-el vagy @DeleteOperation-el ellátott metódusai automatikusan kivezetésre kerülnek (mind JMX és HTTP környezetre is igaz).

Példa:

1
2
3
4
5
6
7
@Endpoint(id = "custom")
public class CustomEndpoint{
    @ReadOperation  // -> GET request
    public CustomData getCustomData() {
        return new CustomData("test", 5);
    }
}

A fenti kód eredményeképpen a végpont megjelenik a http://localhost:8080/actuator/custom végponton.

Amennyiben csak JMX-re vagy HTTP-re szeretnénk korlátozni a kivezetést akkor rendre használhatjuk a @JmxEndpoint vagy a @WebEndpoint annotációkat.

Amennyiben a kérések inputot is várnak (mint például a http://localhost:8080/actuator/health/{*path}"), akkor ezeket a paramétereket vagy a query string-ben adhatjuk meg vagy JSON request body-val. Alapértelmezetten ezek a paraméterek kötelezőek, de opcionálissá tehetjük őket, amennyiben a paramétert ellátjuk a @org.springframework.lang.Nullable annotációval. Vegyünk például egy JSON request body-t:

1
2
3
4
{
    "name": "test",
    "counter": 42
}

Ehhez a paraméterezéshez a következő végpont metódusunk lehet adott:

1
2
3
4
@WriteOperation
public void updateCustomData(String name, int counter) {
    // injects "test" and 42
}

Megjegyzés

Mivel a fent bemutatott paraméter injektálások technológiailag függetlenek, azaz lehet JMX vagy HTTP alapú is, így a paraméterek típusa megszorított az alap beépített típusokra. Ugyanakkor a típuskonverziók automatikusan végbemennek.

A fenti példában feltéve, hogy a custom végponthoz írtuk a write operation-t, a következő címre kell a kérést küldenünk: http://localhost:8080/actuator/custom.

Amennyiben az URL-be szeretnénk lekérdezéshez szükséges elemeket ágyazni hasonlóan, mint a ReqeustMappin-nél a {param_name} megadással, itt a @Selector annotációt kell használnunk a paraméteren. Például:

1
2
3
4
5
6
7
@Endpoint(id = "custom")
public class CustomEndpoint{
    @ReadOperation  // -> GET request
    public CustomData getCustomData(@Selector String id) {
        return new CustomData(id);
    }
}

Így a kérést a következő formában küldhetem el: http://example.com/actuator/sessions/{id}

Spring Boot Admin

A Spring Boot Admin egy 3rd-party project, ami az actuator adta HTTP végpontok felhasználásával egy grafikus interface-t biztosít a számunkra. Két elsődleges komponense a szerver és a kliens. A szerver összegyűjti és megjeleníti az adott Spring Boot alkalmazás(ok) actuator végpontjairól az információt. A különböző Spring Boot alkalmazásokhoz a kliensek csatlakoznak (mindegyikhez egy-egy) és ők szolgáltatják az információt a szerver felé.

A projektünkhöz először is hozzá kell adni a Spring Boot Admin függőséget!

1
2
3
4
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

Ezután a fő konfigurációs osztályunkra el kell helyeznünk a @EnableAdminServer annotációt!

1
2
3
4
5
6
7
@SpringBootApplication
@EnableAdminServer
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Jelen pillanatban még nincs egyetlen kliens sem regisztrálva, így nincs is mit megjeleníteni. A klienseket kétféleképpen regisztrálhatjuk:

  • Minden alkalmazás saját maga regisztrálja magát a szerver felé
  • Az admin szerver feltérképezi a futó service-eket Eureka service registry segítségével

Mivel az Eureka-t nem használtuk, így az első opciót választjuk.

Referenciák


Utolsó frissítés: 2020-12-16 10:29:16