Spring Bean 새로고침

Soonwoo Kwon·2023년 9월 24일
post-thumbnail

Spring Boot 에서의 설정값 관리

Spring Boot 에서는 설정값을 관리하기 위한 Configuration Bean 객체들을 생성하여 이용한다.

보통 해당 설정값들을 하드코딩하여 관리하기 보단 Spring Cloud Config, Spring Valult와 같은 외부 저장소에서 설정값을 저장하고,
서버가 실행될 때 설정값들을 읽어 Bean 객체들을 생성한다.

이렇게 외부 저장소에 설정값을 관리하면, 코드의 수정 없이 저장소의 설정값 변경 만으로 설정을 변경할 수 있다.

하지만 외부 저장소의 설정값 변경 만으론 서버가 실행될 당시 생성된 Bean 설정 객체들을 수정할 수 없다.
서버가 재기동 되어야만, 변경된 설정값을 통해 Bean 객체들이 재생성되어 설정 변경을 반영할 수 있다.

@RefreshScope

@RefreshScope란 서버의 재기동 없이 Bean 객체를 새로고침하기 적용하기 위해 사용되는 애노테이션이다.
사용 방법은 다음과 같다.

사용법

0. 의존성 추가

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

1. 새로고침을 적용할 빈에 @RefreshScope 애노테이션 적용

@Component
@RefreshScope
public class ValueRefreshConfigBean {
    private String color;

    public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) {
        this.color = color;
    } 
    //put getter here 
}

해당 애노테이션이 적용된 빈들은 빈 새로고침의 대상이 된다.

2. /actuator/refresh 활성화


management:
  endpoints:
    web:
      exposure:
        include: refresh

yaml 설정을 통해 actuator의 refresh 엔드포인트를 활성화한다.

3. POST /actuator/refresh 엔드포인트에 빈 새로고침 요청

/actuator/refresh 엔드포인트에 POST 요청을 전송하면 @RefreshScope이 적용된 모든 빈들이 새로고침되어 재생성된다.
이 때, 외부 저장소로 부터 설정값을 읽어 생성되던 빈들은 최신의 설정값을 반영할 수 있게 된다.

이처럼 서버의 재기동 없이 설정값 변경을 동적으로 적용할 수 있다.

한계점

일반적으로 k8s 환경에서 서버들은 deployment 단위로 배포되며, 여러개의 파드에서 실행된다.
즉 각 Bean 객체들은 각 파드의 서버마다 존재하며, POST /actuator/refresh 에 보낸 새로고침 요청은 특정 파드에만 적용되는 것이다.

빈 새로고침 요청을 각 파드의 서버마다 전송하면 모두 새로고침 할 수 있지만, 파드의 특성상 IP 값이 동적으로 변경되고
Spring Cloud Discover와 같은 별도 discover 서버 없이는 각 파드의 동적인 IP를 관리할 수 없다.

이를 해결하기 위해 이용되는 것이 Spring Cloud Bus 이다.

Spring Cloud Bus

Spring Cloud Bus란, MSA 환경에서 Kafka나 RabbitMQ와 같은 메시지 브로커를 이용하여 여러 인스턴스 간에 메시지를 브로드캐스팅 하는 프레임워크이다.

이 Spring Cloud Bus를 이용하여 멀티 파드에서 운영되는 서버의 Bean을 새로고침 할 수 있다.

사용법

0. 의존성 추가


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>

kafak를 메시지 브로커로 이용하는 의존성 추가(다른 메시지 브로커라면 다른 의존성)

1. 메시지 Producing Server 설정

spring:
  application:
    name: spring-cloud-bus-producer
  cloud:
    bus:
      refresh:
        enabled: true
      env:
        enabled: true
      # kafka topic 설정
      destination: {kafka-topic-name}
  # kafka 서버 설정
  kafka:
    bootstrap-servers: {kafka-server-url}
 
# busrefresh 엔드포인트 활성화
management:
  endpoints:
    web:
      exposure:
        include: busrefresh

메시지를 생성하여 kafka에 적재하는 역할을 하는 서버이다.
kafka 서버의 URL과 topic을 지정하고, actuator/busrefresh 엔드포인트를 활성화한다.

2. 메시지 Consumer Server 설정

spring:
  cloud:
    # kafka topic 설정
    bus:
      destination: {kafka-topic-name}
  # kafka 서버 설정
  kafka:
    bootstrap-servers: {kafka-server-url}

kafka의 메시지를 listening 하다가, 특정 topic의 메시지를 consuming 하여 로직을 수행하는 서버이다.

3. 새로고침을 적용할 빈에 @RefreshScope 애노테이션 적용

@Component
@RefreshScope
public class ValueRefreshConfigBean {
    private String color;

    public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) {
        this.color = color;
    } 
    //put getter here 
}

수행 과정

  1. 외부 저장소에서 설정값을 변경한다.
  2. 메시지 Producer 서버에 POST /actuator/busrefresh 요청을 전송한다.
  3. 메시지 Producer 서버는 메시지 브로커인 Kafka에 RefreshRemoteApplicationEvent 메시지를 적재한다.
  4. Kafak를 listening 하고 있던 각 인스턴스(Clinet)는 RefreshRemoteApplicationEvent 메시지를 consume 하여 @RefreshScope이 적용된 모든 빈들을 새로고침한다.(필요에 따라 외부저장소 참고)

0개의 댓글