본문 바로가기

MicroService

11번가 Spring Cloud 기반 MSA로의 전환 - 발표정리

11번가 Monolitic일때 상황 ( 16년 말 )

  • 공통 모듈만 200만 라인
  • 빠르고 다양한 비즈니스 발전으로 인한 if문 추가추가.....
  • 코드 양이 많다보니 IDE에 띄우는것도 힘들다
  • 새로운 기술 접하기가 힘들다 ( dependency가 엉켜있으니까 )
  • 많은 개발자가 한번에 배포해야 함

잘 돌아가는데 MSA 가야하나?

  • 나의 과감한 수정은 전사 장애다 => 개발 조직에 좋지 않은 문화

어떻게 MSA를 도입할 것인가?

  • 업무 도메인별 ( 또는 더 자세하게 ) 별로 서버 분리
  • Legacy 코드에서는 새로운 API 서버 호출
  • DB Flag를 통해서 기존코드와 새로운 코드 switchable하게 해서 안정성 확보

어떤 솔루션을 사용할 것인가?

NETFLIX OSS => Spring Cloud를 사용

 

Hystrix

Netflix가 만든 Fault Tolerance Library : 장애전파방지 & Resilience

 

기능 4가지

  1. Circuit Breaker
  2. Fallback
  3. Thread Isolation
  4. Timeout

 

Hystrix Command를 호출할 때 벌어지는 일

1. 이 메소드를 intercept하여 대신 실행한다. - Thread isolation

 

2. 메소드의 실행 결과 성공 혹은 실패 발생 여부를 기록하고 통계를 낸다. 통계에 따라 Circuit Open 여부를 결정한다.

 - Circuit Breaker

 

3. 실패한 경우(Exception) 사용자가 제공한 메소드를 대신 실행한다.

 ex : 상품 추천 서버를 호출 했는데 응답이 없는 경우 미리 정의해놓은 정해진 상품을 추천해준다. ( UX 관점 )

-Fallback

 

4. 특정시간 동안 메소드가 종료되지 않는 경우 Exception을 발생 시킨다.

- Timeout

 

Circuit Breaker

(1) 일정 시간 동안 (2) 일정 개수 이상의 호출이 발생한 경우, (3) 일정 비율 이상의 에러가 발생하면 ==> Circuit Open ( 호출 차단 )

 

누군가 가로채서 Exception을 던져버림 -> 해당기능이 안정성 확보

 

A -> B(장애) 때문에 다른 C,D 서버 호출을 위한 thread를 할당받지 못한다면 문제가 생김

 

 

(4) 일정 시간 경과 후에 단 한개의 요청에 대해서 호출을 허용하며(Half open), 이 호출이 성공하면 --> Circuit Close ( 호출허용 )

 

" 10초간 20번 이상의 호출이 발생한 경우 50% 이상의 에러가 발생하면 5초간 Circuit Open"

 

Hystrix Circuit Breaker는 한 프로세스 내에서 주어진 CommandKey 단위로 통계를 내고 동작한다.

- 같은 서버를 호출하거나 같은 로직을 사용하고 있다면 같은 CommandKey 단위로 묶는게 좋다.

- 이렇게 묶음을 주는게 중요하다! 주지 않는다면 한 인스턴스에 같은 단위로 묶여 있다. 또는 너무 작은 단위로 묶는다면 10초에 20번 이하를 호출하기 때문에 50% 이상의 에러가 발생해도 잡지 못할 확률이 높다.

 

Hystrix - Fallback

  • Circuit Open
  • Any Exception ( HystrixBadRequestException 제외 )
  • Semaphore / ThreadPool Rejection 
  • Timeout

잘못 사용하면 exception을 개발자들이 파악하지 못하는 경우가 있음 => 모니터링 환경을 잘 구축해야 함

 

HystrixBadRequestException : 로직 상에서 생긴 문제가 아니라 호출한 caller가 잘못한 일 ( 잘못된 시점 호출, 파라미터 잘못 넘김)인 경우 => 에러 통계에서 집계되지 않음

 

Hystrix - Timeout

Circuit Breaker 단위로 Timeout 설정을 할 수 있다. 하지만 default가 1초이므로 적절히 조정해야 한다.

execution.isolation.thread.timeoutInMilliseconds 

Hystrix - Isolation

Thread(default) / Semaphore 옵션 2가지가 있음

 

  • Circuit Breaker마다 Semaphore가 붙음 
  • 각각의 뒤 시스템에 적절한 요청을 정의함 ( 최대 동시 요청 개수 )
  • Command를 호출한 Caller Thread에서 메소드 실행
  • Timeout이 제 시간에 발생하지 않음
    • Java 1.x 시대에는 thread.start(), thread.stop()을 통해 남의 thread를 멈출 수 있었음
    • 하지만 시스템 안전성을 위해 이것이 사라짐
    • timeout이 지났다고 중단하라고 명령하여 thread를 return 할 수 없음

  • Circuit Breaker 별로 사용할 Thread pool을 지정 ( ThreadPoolKey )
  • N:1 관계 가능
  • Command를 호출한 Thread가 아닌 Thread Pool에서 메소드 실행

실제 메소드의 실행은 다른 Thread에서 실행되므로 Thread Local 사용 시 주의 필요

 

Ribbon

Client Load Balancer with HTTP Client

 

로드밸런스를 기존 하드웨어에서 하던걸 애플리케이션 영역으로 넘김

 

Spring Cloud에서는 Ribbon 클라이언트를 사용자가 직접 사용하지는 않음 -> 옵션 설정

  • Zull API Gateway에 내장이 되어 있음
  • RestTemplate ( @LoadBalanced ) 
  • Spring Cloud Feign - 선언전 Http Client

서버 IP,Port 호출대신 서버 군의 이름을 넣으면 됨 ( Eureka )

 

Ribbon이 기존 Load Balancer와 다른점

 

AWS ELB나 infra 이미 잘 구축되어 있는데 굳이 왜 써야함??

 

  • Ribbon은 대부분의 동작을 프로그래밍화 할 수 있음
  • IRule - 주어진 서버 목록에서 어떤 서버를 선택할 것인가 
  • IPing - 각 서버가 살아 있는 가를 검사
  • ServerList<Server> - 대상 서버 목록 제공
  • ServerListFilter<Server> - 대상 서버 중 필터할 목록 제공

 

Eureka

Netflix가 만든 Dynamic Service Discovery

 

  • 등록 : 서버가 자신의 서비스 이름와 IP 주소, 포트를 등록
  • 조회 : 서비스 이름을 갖고 서버 목록을 조회

Server 시작 시 Eureka 서버에 자동으로 자신의 상태 등록 ( UP )

주기적인 HeartBeat으로 Eureka Server에 자신이 살아 있음을 알림

Server 종료시 Eureka 서버에 자신의 상태 변경 ( DOWN )

 

Eureka + Ribbon in Spring Cloud

두개가 둘다 설정되어 있다면, Spring Cloud는 다음의 Ribbon Bean을 대체함 => 서버 목록을 설정으로 명시하는 대신 Eureka를 통해서 Look up 해오는 구현 ( 복잡한 절차 없이 단지 Spring application을 하나 더 띄우면 됨 )

 

API Gateway

  • API의 공통 로직 구현 : Logging, Authentication, Authorization
  • Traffic Control : API Quota, Throttling
  • Single Endpoint 제공 : API 사용할 Client 들은 API Gateway 주소만 인지

11번가에서는 기존 개발한 훌륭한 자체 API Gateway가 있는데 왜 바꿨을까?

=> Hystrix, Ribbon, Eureka 개념이 잘 녹아 있음

 

Member API Server를 호출하면, 모든 호출이 Hystrix Command로 감싸서 돌아가고 ( 앞에본 4가지 장점 ), Ribbon Client가 존재하게 되고 이는 Eureka를 통해 서버 목록을 알 수 있다. 뒷단에 서버 목록을 관리하지 않아도 된다.

 

  • Zuul에서 Hystrix CommandKey : 서버군 이름이 사용됨 
    • hystrix.command.[ product | display | member ]
  • 기본이 Semaphore로 설정되어 있음
    • 특정 API 군의 장애 등이 발생하여도 Zuul 자체의 장애로 이어지지 않음
    • But timeout 기능을 잃게 됨....
    • timeout 기능을 살리기 위해 Thread Isolation을 사용함 ( Hystrix timeout을 통해서 특정 서버군의 장애시에도 Zuul의 Container Worker Thread의 원할한 반환 )

  • Hystrix Isolation을 Thread로 바꾸면
    • Server 전체에 한 개의 Thread Pool이 생겨버림 ( RibbonCommand라는 이름 ) => 원래 Isolation과 어울리지 않는 코드
    • 초창기에 Netflix 문서를 오역하여 Spring Cloud측에서 잘못 구현했다고 판단

  • 서버군 ( Service ID ) 별로 Thread Pool을 분리해주는 것이 필요

 

Server to Server 호출 in MSA

  • API Gateway 전체
    • API Gateway가 죽는다면?
    • 전체 트래픽을 감당해야 함
  • API Gateway + API Server V
    • Eureka ( Discovery ) 가 있기 떄문에 어렵지 않음

" Ribbon + Eureka 조합으로 Peer to Peer 호출을 하기로 결정"

  1. @LoadBalanced RestTemplate : RestTemplate이 Bean으로 선언된 것만 가능
  2. Spring Cloud Feign

Spring Cloud Feign

Java interface + Spring MVC annotation 선언으로 HTTP 호출이 가능한 Spring bean을 자동 생성

Hystrix 연동하기

- feign.hystrix.enabled = true

- method 단위로 Command 단위가 생김 ( Hystrix 단위 조절은 필요

 

 

장애 발생시?

장애 유형 1. 특정 API 서버의 인스턴스가 DOWN 된 경우

Eureka가 Heartbeat 송신이 중단 됨으로 일정 시간 후 목록에서 사라짐

( 사라질 때 까지 시간 : default 60~90초 => 줄이는게 좋음 )

 

Ribbon - IOException 발생 한 경우 다른 인스턴스로 Retry

 

Hystrix - Circuit은 오픈되지 않음 ( Error = 33% ) but, Fallback,Timeout은 동작

 

 

장애 유형 2. 특정 API가 비정상 동작하는 경우 ( 지연, 에러 )

Hystrix - 해당 API를 호출하는 Circuit Breaker 오픈

 

11번가 with Spring Cloud architecture

모니터링

분산 Tracing에서 서버간 Trace 정보의 전달

서버간의 Trace 정보의 전달은 사용 Protocol의 헤더를 통해 전달 필요 ( Zipkin )

 

그러나 Thread Change가 많이 일어나기 때문에 전달하는게 쉽지 않음

 

Spring Cloud Sleuth가 있음!

  • Log에 남기거나 수집 서버 (Zipkin)에 전송하여 검색/시각화
  • Zuul, Servlet, RestTemplate, Hystrix, Feign, RxJax를 지원함

  • Spring Cloud Sleuth로는 DB 호출 구간은 표현이 안됨
    - Spring AOP를 사용하여 Sleuth API로 trace 정보를 직접 생성할 수 있음

  • 11번가에서 실제로 있었던 문제 : API 코드는 깔끔하나 DB 호출을 1000번 해버림
    • API X API X API 이렇게 되다보니....
    • API 검수 가능

Hystrix 모니터링 with Turbine

 

참고 : https://tv.naver.com/v/3925308