MSA Phase 1. Springboot(2)

devty·2023년 7월 24일
0

MSA

목록 보기
2/14

본론

Gateway, Eureka Flow

  1. Client가 Request를 날려준다.
  2. Gateway에서 Eureka에 등록된 서비스인지 확인한다.
    • 매번 Request 때 마다 확인하는 것이 아니라 30초(default)에 한번씩 등록이 되어있는지 확인한다.
  3. 등록되어있는 서비스라면 Gateway에서 해당 서비스로 Reqeust를 보내고 비즈니스 로직을 처리한다.
  4. 해당 서비스에서 비즈니스 로직을 처리한 뒤 Response을 Gateway로 보내준다.
  5. 받은 Response는 다시 Client 한테 보내준다.

MSA 환경에서 Log에 중요성

  • 모놀로틱에서도 Log를 찍는 습관은 중요하지만 MSA에서는 더욱 중요하다.
  • 그 이유는 밑과 같다.
    1. 분산 시스템 디버깅
      • 마이크로서비스 아키텍처에서, 각각의 서비스는 독립적으로 실행되기 때문에 어떤 문제가 발생했을 때 그 원인을 파악하는 것이 어려다.
      • 로그는 서비스 간의 통신, 이벤트 발생 시점 등을 파악할 수 있게 해주므로, 문제의 원인을 찾고 해결하는데 도움이 됩니다.
    2. 성능 모니터링
      • 로그를 통해 각각의 서비스의 성능을 모니터링할 수 있습니다.
      • 이는 시스템 전체의 성능을 향상시키고, 특정 서비스에서 병목 현상이 발생하는지 확인하는데 도움이 됩니다.

MSA 환경에서 Log 찍기

  • 이런 부분은 Gateway에서 지원한다고 앞선 블로그에 정리해둔적이 있다.
  • Spring Cloud Gateway에서는 3가지 개념이 중요하다.
    1. Routes
    2. Predicates
    3. Filters
  • Gateway Filter Process
    • Gateway Handler Mapping 에서는 내부에 Predicates(조건)이 맞다면 Filter들을 거치게 하고 Route 한다.

Gateway Filter Code

  • Gateway → GlobalFilter.java
    @Component
    @Slf4j
    public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
        public GlobalFilter() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
            return ((exchange, chain) -> {
                ServerHttpRequest request = exchange.getRequest();
                ServerHttpResponse response = exchange.getResponse();
    
                log.info("Global Filter baseMessage: {}, {}", config.getBaseMessage(), request.getRemoteAddress());
                if (config.isPreLogger()) {
                    log.info("Global Filter Start: request id -> {}", request.getId());
                }
                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    if (config.isPostLogger()) {
                        log.info("Global Filter End: response code -> {}", response.getStatusCode());
                    }
                }));
            });
        }
    
        @Data
        public static class Config {
            private String baseMessage;
            private boolean preLogger;
            private boolean postLogger;
        }
    }
    • public GatewayFilter apply(Config config) 실제 필터의 로직을 구현하는 부분입니다. 이 메서드는 config 인스턴스에 설정된 값에 따라 로깅을 수행하고, 그 다음 필터 체인으로 요청과 응답을 전달하는 역할을 한다.
  • application.yml
    server:
      port: 8000
    
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://localhost:8761/eureka
    
    spring:
      application:
        name: ApiGateway-service
      cloud:
        gateway:
          default-filters:
            - name: GlobalFilter
              args:
                baseMessage: Spring Cloud Gateway Global Filter
                preLogger: true
                postLogger: true
          routes:
            - uri: lb://user-service
              predicates:
                - Path=/user-service/**
            - uri: lb://mate-service
              predicates:
                - Path=/mate-service/**
            - uri: lb://product-service
              predicates:
                - Path=/product-service/**
            - uri: lb://stock-service
              predicates:
                - Path=/stock-service/**
            - uri: lb://order-service
              predicates:
                - Path=/order-service/**
    • 그 전 블로그와 수정된 내용이 있다면 gateway.default-filters 부분만 추가가 됐다.
      • 이 설정은 모든 요청에 대해 'GlobalFilter' 필터를 적용하고, 각 요청 처리 전후로 로그를 남기도록 설정하고 있다. 'GlobalFilter' 클래스에서 정의한 로직에 따라 실제로 로깅이 수행될 것이다.
    • baseMessage→ 이 값은 로깅 메시지의 일부로 사용됩니다. 여기서는 "Spring Cloud Gateway Global Filter"라는 값이 설정되어 있습니다.
    • preLogger → 이 값이 true로 설정되면, 각 요청이 처리되기 전에 로깅이 수행됩니다. 즉, 요청이 들어올 때마다 로그가 남겨집니다.
    • postLogger → 이 값이 true로 설정되면, 각 요청 처리 후에 로깅이 수행됩니다. 즉, 응답이 보내질 때마다 로그가 남겨집니다.

실행결과

  • Gateway를 통해 User-service를 호출했을 때 결과이다.
    • baseMessage → application.yml 파일에서 지정한 값이 출력되었다.
    • request id → ServerHttpRequest.getId()를 가져온 값이다.
    • response code → 호출된 서비스에서 받은 response의 status code를 가져온다.

결론

후기

  • 이로써 기본적인 서비스 나누기가 완료가 되었다.
  • 여기서 점차 서비스를 늘려나가면 될 것이다.
  • 하지만, MSA은 이게 끝이 아니다. 아직 못한걸 밑에 적어보겠다.
    1. 서비스가 분리가 되어있어서 다른 서비스에 비즈니스 로직을 가져올 수 없다. → 서비스 간에 통신 불가능 → 서비스간 통신을 위한 무언가가 필요함.
    2. 모놀로틱 아키텍처에서는 하나의 서비스에서 전체를 관리하므로 트랙잭션 관리가 쉬웠다. 근데 지금은 서비스별로 서버가 다르기(Scale-Out)에 다른 서비스에 트랙잭션까지 관리해주어야한다. → 트랜잭션 관리를 위한 패턴이 필요함.
    3. 또한, 서비스가 분리되어 있어서 서비스 별로 빌드 및 배포를 따로 해주어야하는데 일일이 하긴 시간이 오래걸리니 자동화를 해주어야한다. → 각 서비스를 위한 CICD 구축이 필요함.
    4. 각 서비스를 다른 서버에서 관리하므로 모니터링이 필수적으로 작용한다. → 모니터링 툴 도입
profile
지나가는 개발자

0개의 댓글