Статьи

Spring Boot Actuator: пользовательская конечная точка со слоем MVC поверх него

Логотип-весна-ю

Конечные точки Spring Boot Actuator позволяют вам контролировать и взаимодействовать с вашим приложением. Spring Boot включает в себя ряд встроенных конечных точек, и вы также можете добавить свои собственные.

Добавить пользовательские конечные точки так же просто, как создать класс из org.springframework.boot.actuate.endpoint.AbstractEndpoint . Но Spring Boot Actuator также предлагает возможность декорировать конечные точки слоем MVC.

Конечная точка

Существует много встроенных конечных точек, но одна из них отсутствует — это конечная точка, которая предоставляет все конечные точки. По умолчанию конечные точки предоставляются через HTTP, где идентификатор конечной точки сопоставляется с URL-адресом. В приведенном ниже примере создается новая конечная точка с идентификаторами endpoints а ее метод invoke возвращает все доступные конечные точки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
@Component
public class EndpointsEndpoint extends AbstractEndpoint<List<Endpoint>> {
 
    private List<Endpoint> endpoints;
 
    @Autowired
    public EndpointsEndpoint(List<Endpoint> endpoints) {
        super("endpoints");
        this.endpoints = endpoints;
    }
 
 
    @Override
    public List<Endpoint> invoke() {
        return endpoints;
    }
}

Аннотация @Component добавляет конечную точку в список существующих конечных точек. URL /endpoints теперь будет отображать все конечные точки с id , enabled и sensitive свойствами:

01
02
03
04
05
06
07
08
09
10
11
12
[
    {
        "id": "trace",
        "sensitive": true,
        "enabled": true
    },
    {
        "id": "configprops",
        "sensitive": true,
        "enabled": true
    }
]

Новая конечная точка также будет зарегистрирована на сервере JMX как MBean: [org.springframework.boot:type=Endpoint,name=endpointsEndpoint]

Конечная точка MVC

Spring Boot Actuator предлагает дополнительную функцию, которая представляет собой стратегию для слоя MVC поверх org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint точки через интерфейсы org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint . MvcEndpoint может использовать @RequestMapping и другие функции Spring MVC.

Обратите внимание, что EndpointsEndpoint возвращает все доступные конечные точки. Но было бы неплохо, если бы пользователь мог фильтровать конечные точки по enabled и sensitive свойствам.

Для этого необходимо создать новую MvcEndpoint с допустимым методом @RequestMapping . Обратите внимание, что использование @Controller и @RequestMapping на уровне класса недопустимо, поэтому @Component использовался, чтобы сделать конечную точку доступной:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
@Component
public class EndpointsMvcEndpoint extends EndpointMvcAdapter {
 
    private final EndpointsEndpoint delegate;
 
    @Autowired
    public EndpointsMvcEndpoint(EndpointsEndpoint delegate) {
        super(delegate);
        this.delegate = delegate;
    }
 
    @RequestMapping(value = "/filter", method = RequestMethod.GET)
    @ResponseBody
    public Set<Endpoint> filter(@RequestParam(required = false) Boolean enabled,
                          @RequestParam(required = false) Boolean sensitive) {
 
    }
}

Новый метод будет доступен в /endpoints/filter URL. Реализация этого метода проста: он получает необязательные enabled и sensitive параметры и фильтрует результат метода invoke делегата:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@RequestMapping(value = "/filter", method = RequestMethod.GET)
@ResponseBody
public Set<Endpoint> filter(@RequestParam(required = false) Boolean enabled,
                            @RequestParam(required = false) Boolean sensitive) {
 
    Predicate<Endpoint> isEnabled =
        endpoint -> matches(endpoint::isEnabled, ofNullable(enabled));
 
    Predicate<Endpoint> isSensitive =
        endpoint -> matches(endpoint::isSensitive, ofNullable(sensitive));
 
    return this.delegate.invoke().stream()
                 .filter(isEnabled.and(isSensitive))
                 .collect(toSet());
}
 
private <T> boolean matches(Supplier<T> supplier, Optional<T> value) {
    return !value.isPresent() || supplier.get().equals(value.get());
}

Примеры использования:

  • Все включенные конечные точки: /endpoints/filter?enabled=true
  • Все чувствительные конечные точки: /endpoints/filter?sensitive=true
  • Все включенные и чувствительные конечные точки: /endpoints/filter?enabled=true&sensitive=true

Сделайте конечные точки обнаруживаемыми

EndpointsMvcEndpoint использует возможности MVC, но все еще возвращает простые конечные объекты. Если Spring HATEOAS находится в classpath, метод filter может быть расширен для возврата org.springframework.hateoas.Resource со ссылками на конечные точки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class EndpointResource extends ResourceSupport {
 
    private final String managementContextPath;
    private final Endpoint endpoint;
 
    EndpointResource(String managementContextPath, Endpoint endpoint) {
        this.managementContextPath = managementContextPath;
        this.endpoint = endpoint;
 
        if (endpoint.isEnabled()) {
 
            UriComponentsBuilder path = fromCurrentServletMapping()
                    .path(this.managementContextPath)
                    .pathSegment(endpoint.getId());
 
            this.add(new Link(path.build().toUriString(), endpoint.getId()));   
        }
    }
 
    public Endpoint getEndpoint() {
        return endpoint;
    }
}

EndpointResource будет содержать ссылку на каждую активированную конечную точку. Обратите внимание, что конструктор принимает переменную managamentContextPath . Эта переменная содержит значение свойства Spring.Bot Actuator management.contextPath . Используется для установки префикса для конечной точки управления.

Изменения, требуемые в классе EndpointsMvcEndpoint :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
@Component
public class EndpointsMvcEndpoint extends EndpointMvcAdapter {
 
    @Value("${management.context-path:/}") // default to '/'
    private String managementContextPath;
 
    @RequestMapping(value = "/filter", method = RequestMethod.GET)
    @ResponseBody
    public Set<Endpoint> filter(@RequestParam(required = false) Boolean enabled,
                          @RequestParam(required = false) Boolean sensitive) {
 
        // predicates declarations
 
        return this.delegate.invoke().stream()
                .filter(isEnabled.and(isSensitive))
                .map(e -> new EndpointResource(managementContextPath, e))
                .collect(toSet());
    }
}

Результат в моем браузере Chrome с установленным JSON Formatter:

Акт-jsonviewer

Но почему бы не вернуть ресурс напрямую из EndpointsEnpoint ? В EndpointResource использовался UriComponentsBuilder который извлекает информацию из HttpServletRequest который будет вызывать исключение при вызове операции getData MBean (если JMX не требуется).

Управление состоянием конечной точки

Конечные точки могут использоваться не только для мониторинга, но и для управления. Уже есть встроенная ShutdownEndpoint (по умолчанию отключена), которая позволяет завершать ApplicationContext . В приведенном ниже (гипотетическом) примере пользователь может изменить состояние выбранной конечной точки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@RequestMapping(value = "/{endpointId}/state")
@ResponseBody
public EndpointResource enable(@PathVariable String endpointId) {
    Optional<Endpoint> endpointOptional = this.delegate.invoke().stream()
            .filter(e -> e.getId().equals(endpointId))
            .findFirst();
    if (!endpointOptional.isPresent()) {
        throw new RuntimeException("Endpoint not found: " + endpointId);
    }
 
    Endpoint endpoint = endpointOptional.get();       
    ((AbstractEndpoint) endpoint).setEnabled(!endpoint.isEnabled());
 
    return new EndpointResource(managementContextPath, endpoint);
}

При вызове disabled конечной точки пользователь должен получить следующий ответ:

1
2
3
{
    "message": "This endpoint is disabled"
}

Идти дальше

Следующим шагом может быть добавление пользовательского интерфейса для пользовательских (или существующих) конечных точек, но это не рассматривается в этой статье. Если вам интересно, вы можете взглянуть на Spring Boot Admin, который является простым интерфейсом администратора для приложений Spring Boot.

Резюме

Spring Boot Actuator предоставляет все готовые к работе функции Spring Boot с рядом встроенных конечных точек. С минимальными усилиями можно добавить пользовательские конечные точки, чтобы расширить возможности мониторинга и управления приложением.

использованная литература