1. 개요

이 예제에서는 내결함성 라이브러리인 Spring Cloud Netflix Hystrix를 다룰 것입니다. 우리는 라이브러리를 사용하고 응용 프로그램의 여러 수준에서 계단식 오류에 대한 전략을 설명하는 회로 차단기 엔터프라이즈 패턴을 구현합니다.

원칙은 전자 제품과 유사합니다. Hystrix 는 관련 서비스에 대한 호출 실패에 대한 방법을 감시합니다. 이러한 오류가 있는 경우 회로를 열고 폴백 메서드로 호출을 전달합니다.

라이브러리는 최대 임계값까지 오류를 허용합니다. 그 외에도 회로를 열어 둡니다. 즉, 향후 실패를 방지하기 위해 모든 후속 호출을 fallback 메서드로 전달합니다. 이렇게 하면 관련 서비스가 실패 상태에서 복구할 수 있는 시간 버퍼가 생성됩니다.

2. REST 생산자

회로 차단기 패턴을 시연하는 시나리오를 생성하려면 먼저 서비스가 필요합니다. 다음 단계에서 생성할 Hystrix 지원 "REST 소비자"에 대한 데이터를 제공하므로 이름을 "REST 생산자"로 지정합니다.

spring-boot-starter-web 의존성 을 사용하여 새 Maven 프로젝트를 생성해 보겠습니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

프로젝트 자체는 의도적으로 단순하게 유지됩니다. 이것은 단순히 문자열을 반환하는 하나의 @RequestMapping 어노테이션이 달린 GET 메서드가 있는 컨트롤러 인터페이스, 이 인터페이스를 구현하는 @RestController 및 @SpringBootApplication 으로 구성 됩니다.

인터페이스부터 시작하겠습니다.

public interface GreetingController {
    @GetMapping("/greeting/{username}")
    String greeting(@PathVariable("username") String username);
}

그리고 구현:

@RestController
public class GreetingControllerImpl implements GreetingController {
 
    @Override
    public String greeting(@PathVariable("username") String username) {
        return String.format("Hello %s!\n", username);
    }
}

다음으로 기본 애플리케이션 클래스를 작성합니다.

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

이 섹션을 완료하기 위해 남은 일은 우리가 수신할 애플리케이션 포트를 구성하는 것입니다. 기본 포트 8080은 다음 단계에서 설명하는 애플리케이션용으로 예약된 상태로 유지되어야 하므로 사용하지 않습니다.

또한 나중에 소개할 클라이언트 애플리케이션에서 생산자를 조회할 수 있도록 애플리케이션 이름을 정의하고 있습니다.

그런 다음 application.properties 파일 에서 포트 9090나머지 생산자 이름 을 지정해 보겠습니다 .

server.port=9090
spring.application.name=rest-producer

이제 cURL을 사용하여 생산자를 테스트할 수 있습니다.

$> curl http://localhost:9090/greeting/Cid
Hello Cid!

3. Hystrix를 사용한 REST 소비자

데모 시나리오에서는 RestTemplateHystrix 를 사용하여 이전 단계에서 REST 서비스를 사용하는 웹 애플리케이션을 구현합니다 . 단순화를 위해 이를 "REST 소비자"라고 합니다.

결과적으로 spring-cloud- starter- hystrix , spring-boot-starter-webspring-boot-starter-thymeleaf 를 종속 항목으로 사용하여 새 Maven 프로젝트를 생성합니다 .

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

회로 차단기가 작동하려면 Hystix가 @HystixCommand 어노테이션 메서드에 대한 @Component 또는 @Service 어노테이션 클래스를 스캔 하고 이를 위한 프록시를 구현하고 해당 호출을 모니터링합니다.

@Controller 에 주입될 @Service 클래스를 먼저 생성할 것 입니다. Thymeleaf를 사용하여 웹 애플리케이션을 구축하고 있으므로 보기 역할을 할 HTML 템플릿도 필요합니다.

이것은 @HystrixCommand 와 연관된 fallback 메소드를 구현하는 주입 가능한 @Service 가 될 것입니다. 이 폴백은 원본과 동일한 서명을 사용해야 합니다.

@Service
public class GreetingService {
    @HystrixCommand(fallbackMethod = "defaultGreeting")
    public String getGreeting(String username) {
        return new RestTemplate()
          .getForObject("http://localhost:9090/greeting/{username}", 
          String.class, username);
    }
 
    private String defaultGreeting(String username) {
        return "Hello User!";
    }
}

RestConsumerApplication 은 우리의 주요 애플리케이션 클래스가 될 것입니다. @EnableCircuitBreaker 어노테이션은 호환 가능한 회로 차단기 구현에 대한 클래스 경로를 스캔합니다.

Hystrix를 명시적으로 사용하려면 @EnableHystrix 로 이 클래스에 어노테이션을 달아야 합니다 .

@SpringBootApplication
@EnableCircuitBreaker
public class RestConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(RestConsumerApplication.class, args);
    }
}

GreetingService 를 사용하여 컨트롤러를 설정합니다 .

@Controller
public class GreetingController {
 
    @Autowired
    private GreetingService greetingService;
 
    @GetMapping("/get-greeting/{username}")
    public String getGreeting(Model model, @PathVariable("username") String username) {
        model.addAttribute("greeting", greetingService.getGreeting(username));
        return "greeting-view";
    }
}

다음은 HTML 템플릿입니다.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Greetings from Hystrix</title>
    </head>
    <body>
        <h2 th:text="${greeting}"/>
    </body>
</html>

애플리케이션이 정의된 포트에서 수신하는지 확인하기 위해 application.properties 파일 에 다음을 입력합니다.

server.port=8080

작동 중인 Hystix 회로 차단기를 보기 위해 소비자를 시작하고 브라우저에서 http://localhost:8080/get-greeting/Cid 를 가리킵니다 . 정상적인 상황에서는 다음과 같이 표시됩니다.

Hello Cid!

프로듀서의 실패를 시뮬레이트하기 위해 간단히 중지하고 브라우저 새로 고침을 마친 후 @Service 의 fallback 메서드에서 반환된 일반 메시지를 확인해야 합니다 .

Hello User!

4. Hystrix 및 Feign을 사용하는 REST 소비자

이제 이전 단계의 프로젝트를 수정하여 Spring RestTemplate 대신 Spring Netflix Feign을 선언적 REST 클라이언트로 사용하겠습니다 .

이점은 서비스 검색 을 위해 Spring Netflix Eureka 를 사용하도록 나중에 Feign Client 인터페이스를 쉽게 리팩터링할 수 있다는 것 입니다.

새 프로젝트를 시작하기 위해 소비자의 복사본을 만들고 생산자와 spring-cloud-starter-feign 을 종속 항목으로 추가합니다.

<dependency>
    <groupId>com.baeldung.spring.cloud</groupId>
    <artifactId>spring-cloud-hystrix-rest-producer</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.1.5.RELEASE</version>
</dependency>

이제 GreetingController 를 사용하여 Feign 클라이언트를 확장할 수 있습니다. @Component 어노테이션이 달린 정적 내부 클래스로 Hystrix 폴백을 구현할 것 입니다.

또는 이 폴백 클래스의 인스턴스를 반환하는 @Bean 어노테이션 메서드 를 정의할 수 있습니다.

@FeignClient 의 name 속성 은 필수입니다. 이 속성이 지정된 경우 Eureka 클라이언트를 통한 서비스 검색 또는 URL을 통해 애플리케이션을 조회하는 데 사용됩니다.

@FeignClient(
  name = "rest-producer"
  url = "http://localhost:9090", 
  fallback = GreetingClient.GreetingClientFallback.class
)
public interface GreetingClient extends GreetingController {
     
    @Component
    public static class GreetingClientFallback implements GreetingController {
 
        @Override
        public String greeting(@PathVariable("username") String username) {
            return "Hello User!";
        }
    }
}

서비스 검색을 위해 Spring Netflix Eureka를 사용하는 방법에 대한 자세한 내용 은 이 기사를 참조하십시오 .

RestConsumerFeignApplication 에서 기본 애플리케이션 클래스에 대한 Feign 통합(실제로 @EnableFeignClients ) 활성화하기 위한 추가 어노테이션을 추가합니다 .

@SpringBootApplication
@EnableCircuitBreaker
@EnableFeignClients
public class RestConsumerFeignApplication {
     
    public static void main(String[] args) {
        SpringApplication.run(RestConsumerFeignApplication.class, args);
    }
}

인사말을 검색하기 위해 이전에 주입된 @Service 대신 자동 연결된 Feign 클라이언트를 사용하도록 컨트롤러를 수정 하겠습니다.

@Controller
public class GreetingController {
    @Autowired
    private GreetingClient greetingClient;
 
    @GetMapping("/get-greeting/{username}")
    public String getGreeting(Model model, @PathVariable("username") String username) {
        model.addAttribute("greeting", greetingClient.greeting(username));
        return "greeting-view";
    }
}

이 예제를 이전 예제와 구별하기 위해 application.properties에서 애플리케이션 수신 포트를 변경 합니다 .

server.port=8082

마지막으로 이전 섹션에서와 같이 이 Feign 지원 소비자를 테스트합니다. 예상 결과는 동일해야 합니다.

5. Hystrix 를 사용한 캐시 폴백

이제 Spring Cloud 프로젝트 에 Hystrix를 추가하겠습니다 . 이 클라우드 프로젝트에는 데이터베이스와 통신하고 책의 등급을 가져오는 등급 서비스가 있습니다.

데이터베이스가 수요가 많은 리소스이고 응답 대기 시간이 시간에 따라 달라지거나 시간에 따라 사용하지 못할 수 있다고 가정해 보겠습니다. 우리는 Hystrix Circuit Breaker가 데이터 캐시로 폴백하는 방식으로 이 시나리오를 처리할 것입니다.

5.1. 설정 및 구성

평가 모듈에 spring-cloud-starter-hystrix 의존성을 추가해 보겠습니다 .

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

등급이 데이터베이스에 삽입/업데이트/삭제되면 Repository 를 사용하여 동일한 내용을 Redis 캐시에 복제합니다 . Redis에 대해 자세히 알아보려면 이 문서를 확인하세요.

RatingService를 업데이트하여 @HystrixCommand 를 사용하여 Hystrix 명령에서 데이터베이스 쿼리 메서드를 래핑하고 Redis에서 읽기에 대한 폴백으로 구성해 보겠습니다 .

@HystrixCommand(
  commandKey = "ratingsByIdFromDB", 
  fallbackMethod = "findCachedRatingById", 
  ignoreExceptions = { RatingNotFoundException.class })
public Rating findRatingById(Long ratingId) {
    return Optional.ofNullable(ratingRepository.findOne(ratingId))
      .orElseThrow(() -> 
        new RatingNotFoundException("Rating not found. ID: " + ratingId));
}

public Rating findCachedRatingById(Long ratingId) {
    return cacheRepository.findCachedRatingById(ratingId);
}

폴백 메서드는 래핑된 메서드와 동일한 시그니처를 가져야 하며 동일한 클래스에 있어야 합니다. 이제 findRatingById 가 실패하거나 지정된 임계값보다 더 지연되면 Hystrix는 findCachedRatingById로 폴백합니다.

Hystrix 기능은 AOP 어드바이스로 투명하게 주입되므로 Spring의 트랜잭션 어드바이스 같은 다른 어드바이스가 있는 경우 어드바이스가 쌓이는 순서를 조정해야 합니다. 여기서 우리는 Hystrix AOP 조언보다 낮은 우선 순위를 갖도록 Spring의 트랜잭션 AOP 조언을 조정했습니다.

@EnableHystrix
@EnableTransactionManagement(
  order=Ordered.LOWEST_PRECEDENCE, 
  mode=AdviceMode.ASPECTJ)
public class RatingServiceApplication {
    @Bean
    @Primary
    @Order(value=Ordered.HIGHEST_PRECEDENCE)
    public HystrixCommandAspect hystrixAspect() {
        return new HystrixCommandAspect();
    }
 
    // other beans, configurations
}

여기에서 Hystrix AOP 조언보다 낮은 우선 순위를 갖도록 Spring의 트랜잭션 AOP 조언을 조정했습니다.

5.2. Hystrix 폴백 테스트

이제 회로를 구성했으므로 리포지토리가 상호 작용하는 H2 데이터베이스를 중단하여 테스트할 수 있습니다. 그러나 먼저 H2 인스턴스를 내장형 데이터베이스로 실행하는 대신 외부 프로세스로 실행해 보겠습니다.

H2 라이브러리( h2-1.4.193.jar )를 알려진 디렉토리에 복사하고 H2 서버를 시작하겠습니다.

>java -cp h2-1.4.193.jar org.h2.tools.Server -tcp
TCP server running at tcp://192.168.99.1:9092 (only local connections)

이제 이 H2 서버를 가리 키도록 rating-service.properties 에서 모듈의 데이터 소스 URL을 업데이트해 보겠습니다 .

spring.datasource.url = jdbc:h2:tcp://localhost/~/ratings

Spring Cloud 시리즈 의 이전 기사 에서 제공한 대로 서비스를 시작하고 실행 중인 외부 H2 인스턴스를 종료하여 각 책의 등급을 테스트할 수 있습니다.

H2 데이터베이스에 연결할 수 없는 경우 Hystrix가 자동으로 Redis로 돌아가 각 책의 등급을 읽는 것을 볼 수 있습니다. 이 사용 사례를 보여주는 소스 코드는 여기 에서 찾을 수 있습니다 .

6. 범위 사용

일반적으로 @HytrixCommand 어노테이션이 달린 메서드는 스레드 풀 컨텍스트에서 실행됩니다. 그러나 때로는 @SessionScope 또는 @RequestScope 와 같은 로컬 범위에서 실행되어야 합니다 . 이는 명령 어노테이션에 인수를 제공하여 수행할 수 있습니다.

@HystrixCommand(fallbackMethod = "getSomeDefault", commandProperties = {
  @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
})

7. Hystrix 대시보드

Hystrix의 멋진 옵션 기능은 대시보드에서 상태를 모니터링하는 기능입니다.

이를 활성화하기 위해 소비자 pom.xml 에 spring-cloud-starter-hystrix-dashboardspring-boot-starter-actuator 를 넣습니다.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>

전자는 @EnableHystrixDashboard 로 @Configuration어노테이션을 달아 활성화해야 하며 후자는 웹 애플리케이션 내에서 필요한 메트릭을 자동으로 활성화합니다.

응용 프로그램을 다시 시작한 후 http://localhost:8080/hystrix 에서 브라우저를 가리키고 Hystrix 스트림의 메트릭 URL을 입력하고 모니터링을 시작합니다.

마지막으로 다음과 같이 표시되어야 합니다.

스크린샷_20160819_031730

Hystrix 스트림을 모니터링하는 것은 좋은 일이지만 여러 Hystrix 지원 애플리케이션을 감시해야 한다면 불편할 것입니다. 이를 위해 Spring Cloud는 스트림을 집계하여 하나의 Hystrix 대시보드에 표시할 수 있는 Turbine이라는 도구를 제공합니다.

Turbine 구성은 이 글의 범위를 벗어나지만 여기에서 그 가능성을 언급해야 합니다. 따라서 Turbine 스트림을 사용하여 메시징을 통해 이러한 스트림을 수집하는 것도 가능합니다.

8. 결론

지금까지 살펴본 것처럼 이제 Spring RestTemplate 또는 Spring Netflix Feign과 함께 Spring Netflix Hystrix를 사용하여 회로 차단기 패턴을 구현할 수 있습니다.

즉, 기본 데이터를 사용하여 폴백이 포함된 서비스를 사용할 수 있고 이 데이터의 사용을 모니터링할 수 있습니다.

늘 그렇듯이 GitHub 에서 소스를 찾을 수 있습니다 .

REST footer banner