Cloud

Hystrix

Jungsoomin :) 2020. 12. 8. 17:44

 

MS 와 같은 분산환경에서는 에러가 발생하거나 실패할 시점이 다양하다. 

 

MSA 에서는 빠른 실패와 장애 적응, 회복력을 중점으로 바라보며 실패를 수용하는 태도를 보인다.

 

  • 치명적인 부분은 장애 전파 시의 경우이며, 이는 대부분 의존성 발생 지점에서 나타나는 일이다.
  • Hystrix 가 하는 일은 회로 차단기와 같다.
  • 의존성이 발생하는 포인트를 분리하고, 장애 발생시 끊음으로서 장애 전파가 일어나지 않게한다.
  • FallBack 기능으로 미리 정의된 값을 Return 시킨다.
특정 서비스나 REST API 호출 결과가 비정상일 경우, 장애 전이 방지를 위해 Zuul 같은 Gateway API , 각 서비스에 있는 Hystrix 에서 호출을 자동으로 차단합니다.

기능

  • 장애 전파 방지
  • 빠른 실패와 빠른 복구
  • 실시간 모니터링, 알람, 설정변경

장애 전파와 빠른 실패 / 빠른복구

  • MSA 환경에서 서비스가 작동 중지되면 의존 서비스에 장애가 전파될 수 있음.
  • 분산 환경으로 인해 Log Trace 를 하기가 어려움
  • 장애 전파를 막기 위해 Hystrix 는 Circuit Breaker Pattern 을 따름

  • Closed : 초기 상태. 정상 실행
  • Open : 에러율이 임계치를 넘음, 모든 Connection 차단. 해당 서비스 호출 시 FallBack 실행
  • Half-Open : Open 상태 이후 시간 경과 시 Half-Open 상태,  주기적으로 접속 시도 후 성공시 Closed, 실패시 Open

Hystrix Monitoring

  • Hystrix 적용 App 은 /hystrix.stream EndPoint 를 가진다.
  • 각 App API 상태 및 Circuit 정보를 Hystrix DashBoard 에서 모니터링 가능하다.
  • Hystrix Turbin 사용시 여러 App 의 Hystrix 데이터를 집계하여 하나의 대시보드에서 볼 수 있다.

Hystrix Turbin


적용방법

  • Eureka Service , Eureka Compoent 에 적용 가능.
  • 필요 의존 : org.springframework.cloud:spring-cloud-starter-nexflix-hystrix
  • Spring Boot 2.4.0 이하로 사용해야함
plugins {
    id 'org.springframework.boot' version '2.3.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR9")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

test {
    useJUnitPlatform()
}
  • @Configuration 클래스에 @EnableCircuitBreaker 선언
@SpringBootApplication(scanBasePackages = "com.example")
@EnableCircuitBreaker
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
  • 메서드 레벨에 @HystrixCommand 선언 , fallbackMethod 속성에 메서드 이름 기입
  • fallBack 메서드 는 기존 메서드와 파라미터, 리턴 값이 반드시 동일
@GetMapping("/test/{id}")
    @HystrixCommand(fallbackMethod = "fallback")
    public ResponseEntity<Object> test(@PathVariable("id") Long id) throws Exception {
        ResponseEntity<Object> responseEntity = null;

        try{
            Account account = testService.getAccount(id);
            responseEntity = ResponseEntity.status(HttpStatus.OK).body(account);
        }catch(Exception e){
            System.out.println(e.getMessage());
            throw new RuntimeException();
        }
        return responseEntity;
    }
    
 
// fallback 메서드는 접근제어자를 제외한 리턴, 파라미터 일치시켜야함
    private ResponseEntity<Object> fallback(@PathVariable("id") Long id){
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new String("Hello Fallback Your Input is : "+id));
    }

 

application.yml

#Feign Client
feign:
  hystrix:
    enabled: true #feignClient 에서Hystrix 사용 허용
  client:
    config:
      default: #feignClient 설정 시작
        connection-timeout: 5000 #커넥션 요청 타임아웃
        read-timeout: 5000 #응답 타임아웃
        logger-level: basic # 로거 레벨설정
        
# feign client log 레벨 
# NONE : 남기지 않음, default
# BASIC : Request Method, URL , ResponseCode, 실행시간
# HEADERS : BASIC + Request/ Response Header 정보
# FULL : HEADERS + bodym metadata 정보

#Circuit Breaker
#Hystrix 는 Thread Pool 에서 관리 된다.
hystrix:
  threadpool:
    default:
      maximum-size: 10 # Thread Poll 크기 default 10
  command:
    defualt:
      execution:
        isolation:
          thread:
            timeout-in-milliseconds: 1000 # 1초 지연시 fallback, default 1000
      metrics:
        rooling-status:
          time-in-milliseconds: 100000 # 오류 감시 시간 default 10000
      circuit-breaker:
        request-volume-threshold: 5 # 감시 시간내의 요청 수 default 20
        error-threshold-percentage: 50 #  감시 시간내의 요청 수가 해당 % 를 넘으면 OPEN default 50

#Actuator
management:
  endpoints:
    web:
      exposure:
        include: ["health", "info", "hystrix.stream"]

Feign 환경에서의 Hystrix 사용

  • 의존 ,어노테이션 동일 @HystrixCommand 대신 @FeignClientfallback 속성에 구현클래스 정의.
  • 구현클래스에서 재정의한 메서드가 FallBack 메서드
  • @FeignClient 의 fallbackFactory 속성에 FallbackFactory<FeignService> 상속 클래스 지정
  • FallbackFactory<FeignService> 상속 클래스 작성 후 익명 구현 클래스 리턴함으로서 FallBack 등록
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableCircuitBreaker
public class EurekaClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApplication.class, args);
    }

}

//
@FeignClient(name = "WEB",fallback = CustomFallback.class)
public interface WebClient {
    @GetMapping("/test/{id}")
    public ResponseEntity<Object> WebClientApiTest(@PathVariable("id") Long id);
}

//
@Component
public class CustomFallback implements WebClient {
    @Override
    public ResponseEntity<Object> WebClientApiTest(Long id) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Fall Back Method Execution Your Param : "+id);
    }
}

FallBackFactory 사용 방법

@FeignClient(name = "auth-service", path = "/api/auth", fallbackFactory = AuthClientFallbackFactory.class)
public interface AuthClient {

    @PostMapping("/ip/check")
    Map getIpCheck(@RequestBody Map map);

    @PostMapping("/ip/swaggerAccess")
    List<String> swaggerAccess(@RequestBody Map map);

}

//
@Slf4j
@Component
class AuthClientFallbackFactory implements FallbackFactory<AuthClient> {

    @Override
    public AuthClient create(Throwable cause) {
        return new AuthClient() {

            @Override
            public Map getIpCheck(Map map) {
                log.error(cause.getMessage());
                return new HashMap();
            }

            @Override
            public List<String> swaggerAccess(Map map) {
                log.error(cause.getMessage());
                return new ArrayList<String>();
            }
        };
    }

}

Hystrix Monitoring => Hystrix DashBoard

 

Hystrix 를 사용하는 Service

  • Hystrix 를 사용 중인 Service 는 endpoint를 열기위해 spring-boot-starter-actuator 의존 필요.
  • Hystrix 를 사용하는 Service 에서 /actuator 로 접근하면 /actuator/hystrix.stream url 존재.
  • doli0413.tistory.com/601?category=934911
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
    
# application.yml

#Actuator
management:
  endpoints:
    web:
      exposure:
        include: ["health", "info", "hystrix.stream"]

Hystrix-DashBoard Server

  • org.springframework.cloud:spring-cloud-starter-netfilx-hystrix-dashboard 의존 필요
  • @Configuration 클래스 > @SpringBootApplication 기술 클래스에 @EnableHystrixDashboard 선언
  • 로컬 -> http://localhost:port/hystrix 접근
  • Hystrix 사용 Service 의 /actuator/hystrix.stream 으로 모니터링 시작.
plugins {
    id 'org.springframework.boot' version '2.3.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR9")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

test {
    useJUnitPlatform()
}
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class, args);
    }

}


Hystrix-Turbine

  • Hystrix 의존성을 가지고 있는 서비스 + Eureka 에 등록된 서비스에 한하여 통합 모니터링 기능을 제공
  • actuator 로 http://localhost:port/actuator/hystrix.stream 으로 각 서버의 상태를 점검하고 있으니 해당 End Point 를 허용해야 하는 듯 하다.
  • 필요 의존 : org.springframework.cloud:spring-cloud-starter-netflix-hystrix-turbine
  • 유레카 서버에 등록시키기 위해 o.s.c:spring-cloud-starter-netfilx-eureka-client 도 필요하다.
  • @Configuration 기술 클래스에 @EnableTurbine 을 선언
  • application.yml 에 모니터링 할 Eureka Service 이름을 기술한다.
  • Hystrix DashBoard 에 들어가서 http://localhost:port/turbine.stream 으로 접근하면 모니터링 화면 접근 가능하다.
plugins {
    id 'org.springframework.boot' version '2.3.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR9")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    implementation 'org.springframework.cloud:spring-cloud-starter-netflix-turbine'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

test {
    useJUnitPlatform()
}

main

@SpringBootApplication
@EnableTurbine
@EnableDiscoveryClient
public class HystrixTurbineApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixTurbineApplication.class, args);
    }

}

application.yml

server:
  port: 8888

spring:
  application:
    name: hystrix-turbine

eureka:
  client:
    service-url:
      defaultZone: ${EUREKA_SERVER_LIST:http://localhost:8761/eureka/}

    registry-fetch-interval-seconds: 1

turbine:
  cluster-name-expression: new String("default") 
  app-config: ACCOUNT-CONSUME-API,CLIENT-ONE # 서비스 목록

 

'Cloud' 카테고리의 다른 글

Config Server  (0) 2020.12.14
Sleuth & ZipKin  (0) 2020.12.14
Feign  (0) 2020.12.08
Ribbon  (0) 2020.12.08
Zuul  (0) 2020.12.07