4 분 소요


Actuator를 사용했을 때는, 구성 정보가 변경되었을 때
각각의 서비스(어플리케이션)에 대해서 매번 /actuator/refresh를 호출해야하는 불편함이 있었다.

이번에는 Spring Cloud Bus라는 기능을 이용해 일괄적으로 업데이트 해보자.

개요

configuration 서버에 어떠한 값을 변경시켰을 때 각각의 마이크로 서비스들이 그 내용을 가져가는 방법에는 세 가지가 있다고 했다.

  • 서버 재기동
  • Actuator refresh
  • Spring cloud bus 사용

이 중 서버를 재기동하는 방식은 좋지 않은 선택이고,
Actuator의 refresh를 사용하면 서버를 재기동하지는 않아도 되지만, 각각의 마이크로 서비스에서 수동으로 refresh 해주어야한다는 단점이 있었다.
이러한 번거로움을 해결해 줄 수 있는게 Spring cloud bus 이다.

Spring Cloud Bus란?

  • 분산 시스템의 노드(= 마이크로 서비스)를 경량 “메시지 브로커”와 연결
    - 이번에는 메세지 브로커로 RabbitMQ를 사용할 것이다.
  • 상태 및 구성에 대한 변경 사항을 연결된 노드에게 전달 (Broadcast)

기존에 사용하고 있던 Spring Cloud Config Server는 그대로 사용하고,
여기에 Spring Cloud Bus를 더해 Configuration Server를 완벽하게 구축할 것이다.

우선, Spring Cloud Config Server에 각각의 마이크로 서비스들이 연결되어 있을 것이다.
데이터의 변경이 생길 때, Spring Cloud Config Server에 연결된 Spring Cloud Bus가 각각의 마이크로 서비스들에게 push 방식으로 알릴건데,
이 때 AMQP(Advanced Message Queuing Protocol)라는 프로토콜을 이용할 것이다.

AMQP(Advanced Message Queuing Protocol)

AMQP란 메시지 지향 미들웨어를 위한 개방형 표준 응용 계층 프로토콜이다.

  • 메시지 지향, 큐잉, 라우팅(P2P, Publisher-Subscriber), 신뢰성, 보안
  • Erlang, RabbitMQ에서 사용

Kafka 프로젝트

  • Apache Software Foundation이 Scalar 언어로 개발한 오픈 소스 메시지 브로커 프로젝트
  • 분산형 스트리밍 플랫폼
  • 대용량의 데이터를 처리 가능한 메시징 시스템

RabbitMQ vs Kafka

  • RabbitMQ
    - 메시지 브로커
    - 초당 20+ 메시지를 소비자(= 메시지를 받는 시스템)에게 전달
    - 메시지 전달 보장, 시스템 간 메시지 전달
    - 브로커, 소비자 중심
  • Kafka
    - 초당 100k+ 이상의 이벤트 처리
    - Pub/Sub, Topic에 메시지 전달
    - Ack을 기다리지 않고 전달 가능 -> 속도가 빠르다
    - 생상자 중심

스크린샷 2022-10-05 오후 4 53 38
간단히 말해, Kafka는 대용량의 데이터를 빠른 시간에 처리하고자 할 때 많이 선택되는 솔루션이고,
RabbitMQ는 비교적 적은 데이터를 안전하게 전달하는 것을 보장해주는 메시지 브로커이다.

Spring Cloud Bus는 어떤 방식으로 작동할까?

외부에서 클라이언트가 POST 방식으로 /busrefresh를 호출한다.
이는 Spring Cloud Bus와 연결된 어떤 마이크로 서비스에 호출하던지 상관 없다.

예를 들어 user-service에 요청을 했다면, user-service가 변경사항을 Cloud Bus에게 전달하고,
Cloud Bus가 다른 마이크로 서비스들에게 변경 사항을 전달하기 때문이다.

이러한 작업을 하기 위해 RabbitMQ를 설치해보자.

RabbitMQ

설치 (MacOS)

$ brew update
$ brew install rabbitmq

참고
혹시 아래와 같은 에러가 발생한다면, xcode-select --install 커맨드를 추가로 입력하자.
스크린샷 2022-10-05 오후 5 11 23

RabbitMQ 서버 기동

$ export PATH=$PATH:/usr/local/sbin
$ rabbitmq-server

스크린샷 2022-10-05 오후 5 29 50
이런식으로 나오면 정상적으로 실행된 것이다. (백그라운드로 실행하고 싶으면 rabbitmq-server -detached 커맨드를 이용하자.)

서버가 기동되었다면 localhost:15672로 접속할 수 있다.
스크린샷 2022-10-05 오후 5 31 11
이 때, default로 설정된 username과 password는 모두 guest이다.
입력하고 나면 아래와 같은 대시보드 화면을 볼 수 있다.
스크린샷 2022-10-05 오후 5 32 28

AMQP 사용

config-service

pom.xml

<!-- Spring Boot Actuator -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- RabbitMQ -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

<!-- Bootstrap -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

application.yml

...
spring:
  application:
    name: config-service
  rabbitmq: # 🌟 rabbitmq 추가
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
...
management:
  endpoints:
    web:
      exposure:
        include: health, busrefresh # 🌟 busrefresh

이 때, actuator에 busrefresh 엔드포인트를 추가함으로써,
configuration server에 변경 요청사항이 들어오게 되면,
우선 rabbitmq에 요청사항을 받았음을 통보한다.

그리고 rabbitmq에 등록되어진 또다른 마이크로 서비스들에게 일괄적으로 변경사항 정보를 push 한다.

config-service도 클라이언트 역할을 같이 한다고 볼 수 있다.

user-service

pom.xml

<!-- RabbitMQ -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

application.yml

...
spring:
  application:
    name: user-service
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
...
management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans, busrefresh # 🌟 busrefresh

gateway-service

pom.xml

<!-- RabbitMQ -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

application.yml

...
spring:
  application:
    name: gateway-service
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
...
management:
  endpoints:
    web:
      exposure:
        include: refresh, health, beans, httptrace, busrefresh # 🌟 busrefresh

서버 기동

아래 순서대로 서버를 기동한다.

  1. RabbitMQ Server
  2. Spring Cloud Config Service (config-service)
  3. Eureka Discovery Service (service-discovery)
  4. Spring Cloud Gateway Service (gateway-service)
  5. Users Microservice (user-service)

서버 기동 후, config-servicegateway-service, user-service에서 아래와 같은 로그가 찍힌다면 성공적으로 실행된 것이다.
스크린샷 2022-10-05 오후 6 01 10

현재, rabbitmq라는 메시지 큐잉 서버에
config-service, gateway-service, user-service 세 개의 마이크로 서비스가 연결되어있는 구조라고 볼 수 있다.

테스트

0) 프로파일(profile) 확인

포스트맨을 이용해 테스트를 하기 전에, user-servicegateway-service에서 어떤 프로파일을 가지고 데이터를 읽어오는지 확인해보자.

user-servicebootstrap.yml을 확인하자.
스크린샷 2022-10-05 오후 6 07 38
유저 서비스에서는 config-service라는 어플리케이션 네임을 사용해 8888번 포트에서 불러온다.

스크린샷 2022-10-05 오후 6 10 02
현재 config-service에서 프로파일이 native이기 때문에, 유저 서비스에서는 native-file-repo안에 있는 application.yml 파일의 정보를 불러오는 것이다.
스크린샷 2022-10-05 오후 6 11 05

gateway-servicebootstrap.yml도 유저 서비스와 동일하게 되어있기 때문에,
게이트웨이 서비스도 native-file-repo안에 있는 application.yml 파일의 정보를 불러올 것이다.

gateway-service 콘솔에 찍힌 로그로도 알 수 있다.
스크린샷 2022-10-05 오후 6 14 36

1) 변경 전 토큰 값 확인

게이트웨이 서비스에 전달되는 토큰을 확인하기 위해 break point를 찍고 디버깅 모드로 실행하자.

회원가입 -> 로그인 -> health-check를 진행하면 아래와 같이 토큰 정보를 확인할 수 있다.
스크린샷 2022-10-05 오후 6 19 27
게이트웨이 서비스에서 읽어온 토큰은 user_token_native_application이다.

2) 구성 정보 변경 (토큰 값 변경)

이제 configuration server의 정보를 변경해보자.
스크린샷 2022-10-05 오후 6 23 07
git을 사용하는게 아니니까 별도의 commmit 과정 없이 저장(cmd+s)만 하면 된다.

웹 브라우저를 이용해 저장된 정보가 잘 반영되었는지 확인하자.
http://localhost:8888/config-service/default로 접속하자.
스크린샷 2022-10-05 오후 6 25 03
위 사진과 같이 잘 반영되었음을 확인할 수 있다.

3) 게이트웨이 서비스 - 토큰 값 확인

그리고 다시 한번, 게이트웨이 서비스에 전달되는 토큰을 확인하기 위해 break point를 찍고 디버깅 모드로 실행하자.
이전과 마찬가지로, 회원가입 -> 로그인 -> health-check를 진행하면 아래와 같이 토큰 정보를 확인할 수 있다.
스크린샷 2022-10-05 오후 6 27 08
토큰 정보가 변경되지 않았음을 확인할 수 있다. (예전 값을 계속 읽어오고 있다.)

4) busrefresh 🌟

이제 리프레시를 해주자.
POST 방식으로 http://localhost:8000/user-service/actuator/busrefresh에 요청한다.
스크린샷 2022-10-05 오후 6 40 28
204 No Content로 반환받았다. 204번은 200번대로, 성공은 성공인데 204번은 서버로부터 특별한 메시지를 받은 게 없다는 의미이다.

우리는 분명 user-service 하나만 리프레시를 했다.
유저 서비스의 로그를 보게되면 아래와 같이 갱신되었다고 나타나있다.
스크린샷 2022-10-05 오후 6 42 36
이 메시지는 게이트웨이 서비스에도 찍힌다.
스크린샷 2022-10-05 오후 6 46 52
다시 말해, 유저 마이크로 서비스에 busrefresh를 통해 변경되었음을 알리면, rabbitmq가 자신과 연결되어있는 다른 모든 마이크로 서비스에게 변경사항을 push 기능을 통해 전달한 것이다.

5) 유저 서비스 - 토큰 값 확인

그렇다면, 변경된 토큰값을 사용하고 있는지 break point를 걸어서 확인해보자.
다시 회원가입 -> 로그인을 진행하면 아래와 같이 토큰 정보를 확인할 수 있다.
스크린샷 2022-10-05 오후 6 51 45
user-service는 변경된 정보를 사용하고 있다!

6) 게이트웨이 서비스 - 토큰 값 확인

이제 gateway-service도 변경된 정보를 사용하는지 테스트 해보자.
로그인 후 받아온 토큰을 사용해 health-check를 호출해보자.
스크린샷 2022-10-05 오후 7 00 14
gateway-service에서도 변경된 정보를 사용하고 있음을 확인할 수 있다!

지금은 리프레시 요청을 유저 서비스에게 했는데, 게이트웨이 서비스에게 해도 마찬가지로 모든 마이크로 서비스에 적용된다.
게이트웨이 서비스에 요청하려면 아래 경로로 POST 요청하면 된다.
http://localhost:8000/actuator/busrefresh



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기