시그마 삽질==six 시그마

Spring Cloud Netflix 본문

프로그래밍/Spring

Spring Cloud Netflix

Ethan Matthew Hunt 2020. 10. 18. 14:49

 

11번가의 MSA 전환 관련 동영상을 보고 정리한 내용입니다

 

https://www.youtube.com/watch?v=J-VP0WFEQsY&list=WL&index=100&t=3419s

 

https://www.youtube.com/watch?v=D6drzNZWs-Y&list=PL9mhQYIlKEhdtYdxxZ6hZeb0va2Gm17A5

 

spring.io/projects/spring-cloud-netflix

 

Spring Cloud Netflix

Spring Cloud Netflix features: Service Discovery: Eureka instances can be registered and clients can discover the instances using Spring-managed beans Service Discovery: an embedded Eureka server can be created with declarative Java configuration Circuit B

spring.io

 

모노리틱 구조에서 

업무 도메인 별로 (혹은 잘게) 서버 분리

-Legacy 코드에서는 한개씩 새로운 api 서버들을 호출(한개씩 옮김)

-기존 코드와 새로운 API 호출은 DB flag를 통해서 switchable하게 롤백가능하게

 

netflix OSS  --> Spring Cloud Netflix

 

 

1. Hystrix 

 

Netflix가 만든 Fault Toerance Library

-장애 전파 방재 & Resilience

 

기능적 관점에서 본 Hytrix의 주요 4가지 기능

-Circuit Breaker

-Fallback

-Thread Isolation

-Timeout

 

HystrixCommand호출시 벌어지는일

 

 

1)이 메소드를 Intercept하여 대신 실행한다 -Thread Isolation

 

두가지 Isolation 방식을 Circuit Breaker 별로 지정 가능

 

(1)Semaphore(optional)

-Circuit Breaker 1개당 1개의 Semaphore 생성

- Semaphore별로 최대 동시 요청개수 지정

-최대 개수 초과시 Semaphore Rejection 발생 -Fallback 실행

-Command를 호출한 Caller Thread에서 메소드 실행

*Timeout이 제 시간에 발생하지 못함

 

(2)Thread(default)

 

-Circuit Breaker별로 사용할 Thread Pool을 지정(ThreadPoolKey)

-Circuit Breaker: Thread Pool = N:1 관계 가능

- 최대 개수 초과시 Thread Pool rejection 발생 -Fallback 실행

-Command를 호출한 Caller  Thread가 아닌 Thread Pool에서 메소드 실행

 

 

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

 

 

 

 

2)  메소드의 실행 결과 성공 혹은 실패(exception) 발생 여부를 기록하고 통계를 낸다.

통계에 따라 Circuit Open여부를 결정한다 -Circuit Breaker

 

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

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

 

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

-, Circuit Breaker는 CommandKey 단위로 생성된다.  같은 서비스를 호출하는 로직이면 키를 똑같에 만들어서 같이 통계를 내고 서킷브레이크 되게 만든다.

 

“동일한 Dependency를 갖는 Hystrix Command를 동일한 CommandKey로 묶는것을 고려해야함”

 

hystrix:
  command:
    productInfo:    # command key. use 'default' for global setting.
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000
      circuitBreaker:
        requestVolumeThreshold: 20   # Minimum number of request to calculate circuit breaker's health. default 20
        errorThresholdPercentage: 50 # Error percentage to open circuit. default 50
        sleepWindowInMilliseconds: 5

 

10초간 20개 이상의 호출이 발생한경우 50% 이상의 에러가 발생하면 5초간 circuit open

 

    @Override

    @HystrixCommand(commandKey = "productInfo", fallbackMethod = "getProductInfoFallback")

    public String getProductInfo(String productId) {

        return this.restTemplate.getForObject(url + productId, String.class);

    }



    public String getProductInfoFallback(String productId, Throwable t) {

        System.out.println("t = " + t);

        return "[ this product is sold out ]";

    }

 

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

ex) 추천상품 뻑나면 고정상품 보여주기

 

 

Fallback으로 지정된 메소드는 다음의 경우에 원본 메소드 대신 실행된다

-Circuit Open

-Any Exception(HystrixBadRequestException 제외)

-Semaphore/ ThreadPoolRejection(나중 Isolation에서 설명)

-Timeout

 

써킷이 실행되야 실행되는거 x

fallback을 잘못사용하면 우리 장애나 비지니스로직의 에러를 감추게된다

익셉션이 fallback으로 감쳐짐.

 

 

MSA는 모노리틱보다 빠를순 없다.

 

사용자가 잘못넘긴 데이터는 illigalArgu...이런 익셉션 말고 HystrixBadRequestException 로 던져라!!!

 

“사용자의 코드에서 HystrixBadRequestException을 발생시키면

이오류는 Fallback을 실행하지 않으며 Circuit breaker를위한 통계에도 집계되지 않는다.

 

Method Caller의 실수(ex)잘못된 파라미터 전달)의 경우임.

HystrixBadRequestException을 Throw하게 작성할 필요가 있다.

 

 

만약 Caller의 실수를 다른 Exception으로 던지면

-Circuit Breaker의 통계에 집계되어 Caller의 잘못으로 Circuit이 Open

-Fallback이 실행되어 개발과정에 오류를 인지하지 못할 수 있다.

 

 

 

4) 특정시간 동안 메소드가 종료되지 않은 경우 Exception 발생시킨다 -timeout

 

jdbc 타입아웃,소켓타임아웃은 언제 타임아웃될지 몰라서   서비스레이어에 이런 타임아웃두기 적당함

 

Hystrix에서는 Circuit Breaker 단위로 (CommandKey 단위로) Timeout을 설정할 수 있다.

 

hystrix.command.<commandKey>.

execution.isolation.thread.timeoutInMilliseconds -default 1초

 

-semaphore Isolation인 경우 - 제 시간에 Timeout이 발생하지 않는 경우가 대부분이다.

-default 값이 매우 짧다 1초

 

 

 

 

 

 

 

2. Ribbon

https://www.youtube.com/watch?v=J-VP0WFEQsY

 

Netflix 만든 Solfware  Load Balancer 내장한 RPC(REST) Library

-Client Load Balancer with HTTP Client

 

Server(caller)(Ribbon) ->server1, server2,server3…

 

 

Spring Cloud 에서는 Ribbon 클라이언트를 사용자가 직접 사용하지 않음

-Spring cloud의 HTTP 통신이 필요한 요소에 내장되어 있음

* Zuul API Gateway

* RestTemplate(@LoadBalanced)

* Spring Cloud Feign- 선언적 http Client

 

 

Ribbon이 기존 Load Balancer와 다른점

Ribbon은 대부분의 동작은 Programmable함! 기존 하드웨어 로드밸런싱 갈증해소

 

Spring Cloud에서는 아래와 같은 BeanType으로

 

IRule- 주어진 서버 목록에서 어떤 서버를 선택할 것인가(1/n..트래픽..)

Iping -각 서버가 살아있는가 검사

ServerList<Server>-대상 서버 목록 제공

ServerListFilter<Server> -대상 서버들 중 호출할 대상 filter

ServerListUpdater

IClientConfig

ILoadBalancer

 

 

 

 

 

3. Eureka 

 

https://www.youtube.com/watch?v=J-VP0WFEQsY

 

Netflix가 만든 Dynamic Service Discovery

-등록 : 서버가 자신의 서비스 이름(종류)와 IP주소, 포트를 등록

-조회: 서비스 이름(종류)를 갖고 서버 목록을 조회

 

 

Eureka Server  

ServiceA: ip1:8081   <———등록————Service A (Eureka Client)

ServiceB: ip2:8080  ip3:8080— 조회——>    Service B(Eureka Client)

ServiceC: ip4:8081 ip5:8080 ip6:8080

ServiceD: ip7:8081

 

 

Eureka Enable Spring Cloud Application 

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

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

-Server 종료시 Eureka 서버에 자신의 상태 변경(DOWN) 혹은 자신의 목록 삭제

-Eureka 상에 등록된 이름은 ’spring.application.name’

 

 

Eureka+Ribbon in Spring Cloud

하나의 서버에 Eureka Client Riboon Client가 함께 설정되면 Spring cloud는 다음의 Ribbon Bean을 대체

 

1.ServerList<Server>

-기본 : ConfigurationBasedServerList

-변경:DiscoveryEnabledNIWSServerList

 

2.IPing

-기본 : DummyPing

-변경: NIWSDiscoverPing

 

서버의 목록을 설정으로 명시하는 대신 Eureka를 통해서 Look up해오는 구현

 

 

 

 

 

 

 

 

 

 

4. Spring Cloud Zuul

https://www.youtube.com/watch?v=J-VP0WFEQsY

API Gateway

MSA 환경에서 API Gateway의 필요성

-Single Endpoint 제공

*API 를 사용할 Client들은 API Gateway 주소만 인지

 

-API의 공통 로직 구현

*Logging,Authentication,Authorization

-Traffic Control

*API Quota, Throttling

 

 

Spring Cloud Zuul API Routing을 Hystrix,Ribbon, Eureka를 통해서 구현

-Spring Cloud와 가장 잘 Intergration 되어있는 API Gateway

 

Spring Cloud Zuul

Product——>Product Api Server(API Server,Api Server…)

Display——>Display Api Server(API Server,Api Server…)

Member——>Member Api Server(API Server,Api Server…)

 

zuul을 사용하면 알아서 엔드포인트 매칭 관리해준다

“API 요청들은 각각 HystrixCommand를 통해서 실행되며 각 API 의 Routing은, Ribbon&Eureka의 조합으로 수행”

Hystricx Command->Ribbon Client->Eureka Client, Http Client

 

 

 

-Hystrix Circuit Breaker는 ComandKey 이름 단위로 동작하는 반면

Spring Cloud Zuul에서 Hystrix CommandKey는 각 서버군 이름(Zuul용어로 Service Id, Eureka에 등록된 서버군 이름)이 사용됨

앞선 그림의 예를 들면 3개의 commandKey가 사용

hystrix.command.[product|display|member].xxxx

 

Hystrix 는 Isolation 기본이 Thread Isolation모드가 기본인반면 Spring Cloud Zuul에서 Hystrix Isolation은 -Semaphore Isolation을 기본으로 한다. 그러면 단점이 타임아웃을 컨트롤할 수 없다

 

 

so Spring Cloud Zuul에서 Hystrix Isolation을  Thread Isolation으로 변경하는 방법

zuul:

ribbon-isolation-strategy: thread 

이것만하면 서버전체 한개의 Thread Pool이 생겨버림. 

so 하단 추가해야 서비스별로 zuul 쓰레드 풀 생김(11번가 요청으로 수정됨)

zuul:

  thread-pool:

    use-separate-thread-pools: true

    thread-pool-key-prefix: zuul-

 

https://www.youtube.com/watch?v=J-VP0WFEQsY

 

Server to Server 호출 in MSA

 

“MSA 플랫폼 내부의 API Server 간의 호출은 어떻게 할 것인가?”

일반적으로 MSA 플랫폼 외부의 호출은 API Gateway를 단일 창구로 사용.but api gateway에 심각한 의존(single point of failure)

 

api서버들끼리 직접호춟 방식

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

 

Spring Cloud 에서는 다음의 Ribbon+Eureka 기반 Http호출 방법을 제공

-@LoadBalanced RestTemplate

*@LoadBalanced 주석을 붙이는 경우 RestTemplate이 Ribbon+Eureka 기능을 갖게 됨

*RestTemplate이 Bean으로 선언된 것만 적용가능

-Spring Cloud Feign

 

 

 

 

 

 

 

 

 

 

5. Feign

 

Declarative Http Client

Java Interface+Spring MVC Annotaion 선언으로 Http 호출이 가능한 Spring Bean을 자동생성

-OpenFeign 기반의 Spring Cloud 확장

-Hystrix+Ribbon+Eureka와 연동되어 있음

 

//Ribbon+Eureka 없이 사용하기  -url 직접명시

@FeignClient(name = "product", url = "http://localhost:8082/")

public interface FeignProductRemoteService {

    @RequestMapping(path = "/products/{productId}")

    String getProductInfo(@PathVariable("productId") String productId);

}



//Ribbon+Eureka연동 -url 생략

@FeignClient(name = "product", fallbackFactory = FeignProductRemoteServiceFallbackFactory.class)

public interface FeignProductRemoteService {

    @RequestMapping(path = "/products/{productId}")

    String getProductInfo(@PathVariable("productId") String productId);

}

 

요기서 name Client이름인 동시에 내가 찌를 서버의 이름임.

interface로 만드는게 대세

 

Spring Cloud Feign with Hystrix

Hystrix 연동하기

-Hystrix가 Classpath에 존재

-feign.hystrix.enabled=true

 

 

Spring Cloud Feign with Feign, with Hystrix,Ribbon,Eureka

-호출하는 모든 메소드는 Hystrix Command로 실행

*Circuit Breaker, Timeout,Isolation,Faalback 적용

-호출할 서버는 Eureka를 통해 얻어서, Ribbon으로 Load Balancing되어 호출

Hystricx Command->Ribbon Client->Eureka Client, Http Client

 

 

 

 

6.장애 유형별 동작

 

서버군을(3개) 구축했는데 한개 인스턴스가 장애난경우

 

1)특정 API서버의 인스턴스가 한개 down된 경우

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

(4단계거침. 파라미터 옵션값 조정으로 사라지는 시간 설정가능.기본 사라지는데 60~90초. out of service 명령으로 즉시 뺄수도 있음)

 

Ribbon- IOException이 발생한 경우 다른 인스턴스로 Retry함(같은 서버찌를건지,다른서버 찌를건지 선택가능)

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

 

2) 특정 APi가 비정상 동작하는 경우(지연,에러)

Hystrix-해당 API를 호출하는 Circuit Breaker 오픈 Fallback,Timeout 동작

 

 

7. 11st with Spring Cloud

 

Spring Cloud Zuul(Hystrix+Ribbon+Eureka)+ EurekaServer+Spring Cloud Config Server

—>전체 Api server

Api server <—>Api server

Api server 는 Spring Cloud Feign+Hystrix+Ribbon+Eureka client

 

모든 MSA Platform 내의 서버는 Eureka Client를 탑재

API Server들간의 호출도 Spring Cloud Feign을 통해 Hystrix+Ribbon+Eureka client조합으로 호출

 

Spring Cloud Config

 

Git 기반의 Config 관리

플랫폼 내의 모든 Server들은 Config Client를 탑재

서버 시작시 Config 서버가 제공하는 Config들이 PropertySource로 등록됨

 

Spring Cloud Config Server —Clone & fetch—>Git Repo(config file)

Spring Cloud Config Server <—모든 Api Server, Eureka,Zuul (3개에 Spring Cloud Config Client 전부 설치)

 

Git Repository내의 Config 파일들은 다양하게 구성 가능

-전체 서버 공통 Config

- 서버군용 Config

- 특정 서버용 Config

 

but 이걸 사용하면 system property보다 더 높은 우선순위 갖기에 콘솔에서 -D로 띄워도 변경불가함.(근데 이 두개 우선순위 조정가능함 system prop높게 설정해라)

git으로 올라가다보니 watch해서 property 실수를 예방할 수 있다.

 

 

 

8. MSA 운영 환경을 위한 전용 모니터링 도구들

 

-Zipkin, Turbine, Spring Boot Admin

 

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

서버간의 trace 정보의 전달은 사용 Portocol의 헤더를 통해 전달 필요

ex) HTTP Header

대표적인 서비스는  트위터의 Zipkin

 

but

In Process 에서 Trace 정보의 전달 문제

 

다양한 Library에 의한 Thread 변경으로 인한 Trace 정보 전달이 어려움

-단순한 Thread Local에 저장하는 방식을 사용하기 어렵다

-Hystrix,RxJava, @Async….

 

 

Spring Cloud Sleuth

Spring Cloud 에서 제공하는 Distributed Tracing 솔루션

-대부분의 내외부 호출 구간에서 Trace정보를 생성 및 전달

-Log에 남기거나 수집 서버(zipkin)에 전송하여 검색/시각화

 

Spring Cloud Sleuth가 지원되는 Component?

-Zuul,Servelt,RestTemplate,Hystrix,Feign,RxJava

 

 

디펜던씨 한줄이면 Application 로그에 Trace Id(UUID)가 함께 출력

 

서버이름,이 request의 UUID, uuid속에 소속된 단위 Id, 샘플링유무

Trace Id= 하나의 Request에 대해 서버 전체를 걸쳐 동일한 UUID

 

“로그 수집/검색 시스템이 있다면 동일 요청에 해당하는 전체 서버의 로그 분석 가능”

 

위 로그를 시각화하면 할수있는데 바로 zipkin을 사용하면됨

 

Spring Cloud Sleuth with Zipkin

 

Spring cloud Sleuth로는 DB 호출 구간은 표현안됨

Spring Aop를(Repository와 mybatis mapper에 aop걸고) 사용해서 Sleuth API로  DB 관련 trace 정보를 직접생성

 

 

모든 소스들은 pull request를 거치지 못하면 머지되지 못한다.

pull Request Reviewer들은 팀 전원을 넣는다.

 

zipkin은 모든 trace를 달지만  대쉬보드 보여주는건 샘플링 1~100% 기반으로 함.

 

 

크롬 확장프로그램 Zipkin Extenstion사용시 운영화면 열고 익스텐션 사용하면 샘플링되지 않은 트래픽등 전체 다 시각화해서 볼수있는데 개발에서 slow API 검수하는 목적으로도 많이쓰임.

 

 

Hystrix 모니터링 with Turbine

 

Success 성공횟수

Short0Circuieted 차단횟수

Bad Request 사용자 입력오류(콜러가 파라미터 잘못날려서 api호출한거등)

Timeout 시간초과(지연)

Rejected isolation초과(semaphore,Thread Pool 꽉차서)

Failure 일반오류

Error 전체 에러 비율

TPS도 확인가능

 

모든 서버와 서버간의 관계, 모든 서버와 서버간의 메소드 단위로 에러비율,TPS 확인가능

아쉬운건 real time이라는것 어제의 오류사항 볼수 없음

 

그걸 보완하기 위해 InfluxDB에 일주일치의 Hystrix Metrics 보관

Grafana를 통해 Dashboard를 구성

 

 

 

Spring Boot Admin

 

Eureka에 등록되어 있는 모든 서버의 정보를 표시/제어

spring boot Actuator를 호출

 

 

Open Source를 사용한다는것

버그 부실 -> source code가 주어졌기에 무엇이든지 할 수 있다는 자신감

-bug report, 수정과 문서의 보강은 사용자의 몫

정확한 동작원리 알 수 있음..

 

 

 

Vine Admin

-운영시 필요한 추가 기능 구현

-Eureka Server 상태변경

-Zuul 내부 Eureka/Ribbon상태 확인 &강제변경

 

 

 

zuul - 오픈소스 jar 가져온게 아니라 소스 복사해 사용

eureka -eureka위에 스프링의 많은 기능 더 추가함

hystrix- jar그대로 쓰고 있음.

 

 

git에 config 변경했으니 애플리케이션에 가져가라고 refresh하는 기능이 있다.

스프링의 싱글톤이란 스콥말고 refresh라는 스콥이 하나 더 있다.

리프레시하면 내부적으로 빈 새로 만든걸 갈아낀다. 그러나 위험이 있어서 서버 재가동 없이 동적재가동 안한다.

 

 

 

MSA 트랜잭션 관리 불가 방안

 

api 에러는 DB에 update insert했는지 모른다는거다. DB업데이트 후 네트워크에러일수도 있다.

 

1. update가 필요한 api는 put으로 멱등성 있게 작성해서 최대한 retry하게 하라

1) 여러번 update 상관x 

2) one insert <---Request에 대한 처리여부를 비지니스처리와 함께 저장 

 

2. 무조건 보상 api를 함께 작성해라.

 api  업데이트할때  UUID 함께 넘겨서 에러코드가 발생했다면 DB삽입후 발생했는지 삽입전 발생했는지 상관하지 말고 200이 아니면 보상을 호출하게 해서 서버 DB 상태를 원래대로 회복시킬 수 있게 한다.

 

3. 사용자 응답 필요없는 update는 스프링 카프카로 이벤트기반으로 처리한다

 

참고

참고

'프로그래밍 > Spring' 카테고리의 다른 글

Spring webflux  (0) 2022.07.19
Spring batch 요약  (0) 2020.10.25
@TransactionalEventListener  (0) 2020.10.22
Spring Kafka Basic  (0) 2020.08.25
ObjectMapper & OrikaMapper basic  (0) 2020.08.24
Comments