spring / / 2022. 12. 23. 07:31

Spring에서 HystrixCommand 사용

Spring Cloud에서 HystrixCommand를 적용한 소스이다.

호출방식은 Controller -> Service로 실행이 되고 Service에서 실행이 실패하여 fallback으로 빠져서 Exception을 던지는 형태이다.

예제 1) 1명의 사용자만 다운로드 가능 (SEMAPHORE 이용)

@RestController
@RequestMapping("download")
@RequiredArgsConstructor
public class DownloadController {

    private final DownloadService downloadService;

    @GetMapping
    public String download() {
        try {
            return downloadService.download(UUID.randomUUID().toString());
        } catch (HystrixRuntimeException e) {
            if (e.getFailureType() == HystrixRuntimeException.FailureType.REJECTED_SEMAPHORE_EXECUTION) {
                throw new DownloadFailedException(e.getFallbackException().getMessage());
            } else {
                throw e;
            }
        }
    }
}
@Slf4j
@Service
public class DownloadService {

    // SEMAPHORE 방식을 적용
    @HystrixCommand(
        groupKey = "downloadService", // 기본값은 클래스명이다.
        commandKey = "downloadCommandKey", // 따로 설정되지 않으면 메소드명으로 설정되고 동일한 메소드명이 있으면 오동작할 수 있으니 반드시 설정하자. 또한 application.yml에 설정을 추가할 때 사용한다.
        threadPoolKey = "downloadThreadPoolKey", // threadPool별로 다르게 설정을 사용할 경우에 적용
        fallbackMethod = "downloadFailed", // fallback이 실행될 메소드 명
        ignoreExceptions = { RuntimeException.class }, // exception으로 제외할 클래스
        commandProperties = {
                // application.yml에도 commandProperties의 설정이 있으면 application.yml의 설정이 우선한다.
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "20000"), // thread가 해당 시간 이상 실행이 되면 timeout이 발생하여 fallback으로 빠진다
                @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"), // semaphore 방식을 이용
                @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "2") // semaphore 방식을 이용할 경우 최대 동시 요청 수
        }
    )
    public String download(String id) {
        String threadName = Thread.currentThread().getName();
        HystrixThreadPoolMetrics instance = HystrixThreadPoolMetrics.getInstance(() -> "downloadThreadPoolKey");
        log.debug("Active thread: {}", instance.getCurrentActiveCount());
        log.debug("Core pool size: {}", instance.getCurrentCorePoolSize());
        log.debug("Maximum pool size: {}", instance.getCurrentMaximumPoolSize());
        log.debug("Current task count: {}", instance.getCurrentTaskCount());

        log.debug("[" + threadName + "] DownloadService#download start");
        execute(id);
        log.debug("[" + threadName + "] DownloadService#find stop");

        return "success";
    }

    private boolean execute(String id) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return true;
    }

    // service 메소드의 파라미터와 리턴타입이 같아야 한다. ==> String download(String id)
    private String downloadFailed(String id, Throwable throwable) {
        // throwable : java.lang.RuntimeException: could not acquire a semaphore for execution
        // exception을 던지면 해당 메소드를 호출하면 쪽으로 전달 (DownloadController)
        throw new DownloadFailedException("concurrent request exceeded");
    }
}

application.yml에서 특정 메소드에 대한 설정을 하려면 commandKey를 활용하여 추가하면 된다.

  • hystrix.command.{commandKey}.execution.isolation.strategy : ISOLATION 전략 => THREAD (default), SEMAPHORE
  • hystrix.command.{commandKey}.execution.isolation.thread.timeoutInMilliseconds : Thread의 Timeout 값
    default : 1000(ms)
  • hystrix.command.{commandKey}.execution.timeout.enabled : Timeout 사용여부 => default : true
  • hystrix.command.{commandKey}.execution.isolation.semaphore.maxConcurrentRequests : 세마포어 사용시 동시 호출량 제한 => default : 10

위의 DownloadController의 download 메소드의 commandProperties를 applicatoin.yml에 적용하면 아래와 같다.

application.yml

hystrix.command.downloadServiceCommandKey.execution.isolation.strategy: SEMAPHORE
hystrix.command.downloadServiceCommandKey.execution.isolation.thread.timeoutInMilliseconds: 10000
hystrix.command.downloadServiceCommandKey.execution.timeout.enabled: true
hystrix.command.downloadServiceCommandKey.execution.isolation.semaphore.maxConcurrentRequests : 2

예제 2) 실행시간 제한 (5초)

@Slf4j
@Service
public class TimeoutService {

    // THREAD 방식을 적용
    @HystrixCommand(
        groupKey = "timeoutService", // 기본값은 클래스명이다.
        commandKey = "timeoutCommandKey", // 따로 설정되지 않으면 메소드명으로 설정되고 동일한 메소드명이 있으면 오동작할 수 있으니 반드시 설정하자. 또한 application.yml에 설정을 추가할 때 사용한다.
        threadPoolKey = "timeoutThreadPoolKey", // threadPool별로 다르게 설정을 사용할 경우에 적용
        fallbackMethod = "failed", // fallback이 실행될 메소드 명
        ignoreExceptions = { RuntimeException.class }, // exception으로 제외할 클래스
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"), // thread가 해당 시간 이상 실행이 되면 timeout이 발생하여 fallback으로 빠진다
            @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"), // timeout 발생 시 thread를 interrupt 시킬지 여부
            @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD") // thread 방식을 이용
        },
        threadPoolProperties = {
            @HystrixProperty(name = "coreSize", value = "5"), // thread 수
            @HystrixProperty(name = "maxQueueSize", value = "0") // 최대 thread queue 크기
        }
    )
    public String check(String id) {
        String threadName = Thread.currentThread().getName();
        log.debug("[" + threadName + "] timeoutService#download start");
        execute(id);
        log.debug("[" + threadName + "] timeoutService#find stop");

        return "success";
    }

    private boolean execute(String id) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return true;
    }

    // service 메소드의 파라미터와 리턴타입이 같아야 한다. ==> String check(String id)
    private String failed(String id, Throwable throwable) {
        // exception을 던지면 해당 메소드를 호출하면 쪽으로 전달 (TimeoutController)
        throw new ExecutionTimeoutException("timeout occurred");
    }
}

threadPool 설정

설명 기본값
coreSize ExecutorService가 유지하는 worker thread의 최소수 10
maximumSize ExecutorService가 사용할 수 있는 worker thread의 최대 수 10
maxQueueSize ExecutorService에서 대기하는 최대 수 -1
allowMaximumSizeToDivergeFromCoreSize true로 해야 maximumSize가 적용될 수 있다. true로 하면 coreSize와 maximumSize 중 큰값으로, false로 하면 coreSize가 적용된다. false

allowMaximumSizeToDivergeFromCoreSize의 설정에 따른 가용 thread 수

allowMaximumSizeToDivergeFromCoreSize coreSize maximimSize 가용 thread 수
true 1 2 2개
true 2 1 2개
false 1 2 1개
false 2 1 2개
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유