오늘은 Spring boot 2.0의 actuator에 대해서 살펴보자. 아직 마일스톤 버전이라 바뀔 가능성은 있지만 크게 바뀌지 않을 것 같다. 기존(2.0 이전)의 actuator 와는 구조가 많이 변경되었다. 구조가 변경되었다고 하더라도 우리가 사용하는 것에 대해서는 많은 변화는 없다. 약간의 변화? 일단 spring-boot-actuator 모듈이 분리 되었다. 원래는 spring-boot-actuator 모듈 하나만 있었지만 autoconfigure 모듈이 새로 추가되었다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>

하지만 우리는 기존과 동일하게 spring-boot-starter-actuator 만 디펜더시 받으면 된다. 그럼 자동으로 actuatoractuator-autoconfigure이 디펜더시 받아진다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

일단 필자가 아는 것만큼 이야기를 해보자.

Endpoint

새로운 @Endpoint 어노테이션이 추가되었다. 기존에는 인터페이스로 되어있던 걸로 기억하는데 어노테이션으로 변경되었다. 새로운 @Endpoint 속성에는 세가지 속성이 있다. id, exposure, defaultEnablement 속성인데 id는 말그대로 id를 뜻하며 endpoint 도 id와 동일하게 지정된다. exposure 은 노출 속성을 의미한다. 두가지 속성이 있는데 JMXWEB이 있다. 아무 설정하지 않았을 경우에는 모두 다 노출되며 특정한(JMX 혹은 WEB) 경우에 노출 시키고 싶다면 JMX 또는 WEB을 지정해주면 된다. defaultEnablement 속성은 기본적으로 enable 시킬 것인가 아니면 disable 시킬것인가 속성이다.

일단 한번 코드로 살펴보자.

@Endpoint(id = "hello")
@Component
public class HelloEndpoint {

  @ReadOperation
  public String hello(String name)  {
    return "hello " + name;
  }

  @WriteOperation
  public String foo(String name) {
    return name;
  }
}

나머지는 위에서 설명을 했으니 넘어가고 여기서 조금 익숙하지 않은 어노테이션이 눈에 띈다. @ReadOperation, @WriteOperation 어노테이션이다.
이 어노테이션은 말그대로 읽기 오퍼레이션이냐 쓰기 오퍼레이션이냐를 정하는 것이다. 그럼 무엇이 다를까? ReadOperation은 http method의 GET에 해당하고 WriteOperation 어노테이션은 POST에 해당한다. 이외에도 @DeleteOperation 어노테이션도 존재한다. 아마도 DELETE 메서드에 해당하지 않을까 생각된다. 아직 테스트는 해보지 않았지만 그러지 않을까?

한번 요청을 해보자.

http http://localhost:8080/application/hello name==wonwoo

hello wonwoo

위와 같이 요청을 했을 경우 hello wonwoo를 볼 수 있을 것이다. 아니다 볼 수 없다. 왜냐하면 기본적으로 몇가지를 제외하고는 모두 disable 되어 있기 때문이다. 루트와 info, status를 제외하고는 모두 비활성화 되어있다. enable 시키기 위해서는 프로퍼티에 다음과 같이 작성하면 된다.

endpoints.hello.web.enabled=true

만약 web을 활성화 시키고 싶다면 위와 같이 작성하면 된다. JMX를 활성화 시키고 싶다면 동일하게 endpoints.hello.jmx.enabled=true 를 작성해주면 된다. 하지만 지금 버그인지 원래그런건지 아니면 필자가 M5 버전이라 그런건지 web만 설정하고 싶어서 저렇게 했는데 jmx도 보인다. 하지만 jmx만 설정하면 web은 동작하지 않는다. 뭐 나중에 다시 확인해봐서 안되면 한번 물어나보지 머..

위와 같이 작성을 했다면 다시 요청을 해보자. 그럼 원하는 대로 hello wonwoo를 볼수 있을 것이다.

하지만 spring boot 에서는 web을 사용할 때 다른 것을 권장하고 있다. @WebEndpointExtension 어노테이션을 사용해서 웹에 특화되게 확장한 어노테이션이라고 생각하면 된다. 다른 건없다. 사용법도 간단하다. 아래와 같이 해당 엔드포인트만 연결해주면 된다.

@WebEndpointExtension(endpoint = HelloEndpoint.class)
@Component
public class HelloWebEndpointExtension {

  private final HelloEndpoint delegate;

  public HelloWebEndpointExtension(HelloEndpoint delegate) {
    this.delegate = delegate;
  }

  @ReadOperation
  public WebEndpointResponse<String> hello(@Nullable String name) {
    return new WebEndpointResponse<>(delegate.hello(name));
  }
}

어렵지 않다. 딱 보기에도 쉽다. 참고로 spring5부터는 Nullable을 지원한다. 옵션값일 경우 spring 에서 제공해주는 Nullable 어노테이션을 사용하면 된다. DI를 받을 때에도 옵션 값이면 Nullable을 사용하면 된다.

private final SomeClass some;

public HelloEndpoint(@Nullable SomeClass some) {
  this.some = some;
}

그리고 Spring boot 만의 Conditional 어노테이션인 ConditionalOnEnabledEndpoint 어노테이션이 추가 되었다. 이것은 endpoints.hello.enabled=false 일 때는 빈으로 동작 시키지 않는 어노테이션이다.

@Bean
@ConditionalOnEnabledEndpoint
public HelloEndpoint helloEndpoint() {
  return new HelloEndpoint();
}

위와 같이 설정 했을 경우 endpoints.hello.enabled=false 이라면 helloEndpoint는 빈으로 등록 시키지 않는다. 이외에도 ConditionalOnEnabledHealthIndicator 어노테이션도 추가 되었다. HealthIndicator는 설정 하는 것 이외에는 변경된 부분은 없다. 기존과 동일하게 HealthIndicator 인터페이스 혹은 AbstractHealthIndicator 추상 클래스를 구현하면 된다.

@Component
public class DummyHealthIndicator implements HealthIndicator {

  @Override
  public Health health() {
    return Health.up().build();
  }

}

HealthIndicator는 우리가 사용하는 것에 대한 변화는 없으므로 넘어가고 마지막으로 Endpoint에 새로 추가된 어노테이션이 하나 더 있다. 그건 @Selector라는 어노테이션인데 간단히 말해서 PathVariable 어노테이션과 동일하다.

@ReadOperation
public WebEndpointResponse<String> selector(@Selector String name) {
  return new WebEndpointResponse<>(delegate.hello(name));
}

위와 같이 설정 했을 경우에 다음과 같이 요청이 가능하다.

http http://localhost:8080/application/hello/wonwoo

hello wonwoo

그럼 동일하게 hello wonwoo라고 응답 받을 수 있다.

안타깝게 현재는 Post로 받을때 바디를 Object로 받을 수 는 없다. 나중에 가능할지는 모르겠지만 현재로는 String, Integer, Long 기타 enum 등으로만 받을 수 있다.

@WriteOperation
public WebEndpointResponse<Person> person(String name) {
  return new WebEndpointResponse<>(new Person(delegate.hello(name)));
}

위와 같이 POST로 설정했을 경우 body로 요청 할 수 있다.

http POST http://localhost:8080/application/hello name=wonwoo

{
    "name": "hello wonwoo"
}

이렇게 actuator 의 Endpoint에 변화에 대해서 알아봤다. 다른 것들은 우리가 사용하는데 있어서 많은 변화는 없었지만 내부적으로 많은 변화가 있었다. 특히 reactive가 추가 됨으로써 구조적으로 많은 변화가 있었다. 우리는 잘 가져다 쓰면 된다. 물론 내부를 아는 것이 휠씬 좋지만 일단 먼저 어떻게 사용하지는 아는 것도 중요하니 잘 가져다 쓰자.

오늘은 이렇게 Spring boot 2.0 actuator에 대해서 살펴봤다. reactive stream 도 공부하는 중이라 조만간 한번 언급을 할 예정이다. 하지만 너무 어렵..