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가지
- Circuit Breaker
- Fallback
- Thread Isolation
- 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 호출을 하기로 결정"
- @LoadBalanced RestTemplate : RestTemplate이 Bean으로 선언된 것만 가능
- 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
'MicroService' 카테고리의 다른 글
마이크로서비스 아키텍처 구축 - 7장.테스팅 (0) | 2020.03.05 |
---|---|
Message-Driven 마이크로서비스 (0) | 2018.12.03 |
Sleuth란? (0) | 2018.12.03 |
도커 컨테이너와 마이크로서비스 (1) | 2018.11.28 |
스프링 클라우드 - 마이크로서비스간 통신이란 (Ribbon) (5) | 2018.08.09 |