반응형

본 내용은 인프런의 이도원 님의 강의 "Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)" 내용을 바탕으로 정리한 내용입니다.

 

기존 MSA 문제점

  • A 서비스에서 B서비스를 호출할 때 B 서비스에 문제가 생기면 A 서비스에도 오류가 발생하는 현상이 생김
  • ex) OrederService를 기동하지 않고 UserSerivce에서 호출하면 UnknownHostException 발생
  • CircuitBreaker를 사용하여 오류 발생한 서비스를 재호출하지 않도록 변경

CircuitBreaker란?

  • 마이크로서비스 아키텍처나 분산 시스템에서 자주 사용되는 패턴으로, 네트워크나 외부 서비스 호출 시 재 호출을 막고 시스템의 안정성을 높여 장애 확산을 방지하는 데 사용된다.

CircuitBreaker 의 목적

  • 실패 격리: 서비스나 기능이 실패할 경우 해당 요청을 격리하여 시스템 전체로 전파되지 않도록 한다.
  • 성능 보호: 반복적인 실패 시도를 줄여 시스템 리소스를 보호한다.
  • 복구 기능 제공: 일정 시간이 지나거나 실패 원인이 해결되면 다시 정상적인 요청을 재개한다.

Circuit Breaker의 상태

  • Closed (닫힘): 정상 상태로, 모든 요청을 통과시킨다. 실패가 발생해도 계속 시도한다.
  • Open (열림): 일정 횟수 이상의 실패가 발생하면 열림 상태로 전환되는데 이 상태에서는 모든 요청을 즉시 실패로 처리하여 외부 서비스나 기능에 대한 호출을 중단한다.
  • Half-Open (반 열림): 일정 시간이 지나면 다시 일부 요청을 허용하여 외부 서비스나 기능이 정상적으로 복구되었는지 확인하고 이 시점에 성공하면 다시 Closed 상태로 전환되고, 실패하면 다시 Open 상태로 돌아간다.

Resilience4j란?

  • Java 애플리케이션을 위한 고가용성 라이브러리로, 특히 분산 시스템에서의 안정성과 복원력을 높이는 다양한 패턴을 제공한다.
  • 각 패턴을 개별 모듈로 제공하여 필요에 따라 선택적으로 사용할 수 있다.
  • Resilience4j의 Circuit Breaker는 특정 서비스 호출이 실패하면 일정 시간 동안 추가 호출을 차단하고, 그 후에 다시 일부 호출을 허용하여 서비스가 복구되었는지 확인한다.

실습

UserService

pom.xml
<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>  
</dependency>
  • resilience4j Dependency 추가
UserServiceImpl.class
@Autowired  
private CircuitBreakerFactory circuitBreakerFactory;

/**  
 * 사용자 ID로 사용자 정보 조회  
 * @param userId  
 * @return  
 */  
@Override  
public UserDto getUserByUserId(String userId) {  
    UserEntity userEntity = userRepository.findByUserId(userId);  
    if(userEntity == null) {  
        throw new UsernameNotFoundException("User not found");  
    }  
    UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);

    // circuitBreaker 추가
    List<ResponseOrder> orderList = circuitBreaker.run(() -> orderServiceClient.getOrders(userId)  
            , throwable -> new ArrayList<>());  

    userDto.setOrders(orderList);  

    return userDto;  
}
  • CircuitBreakerFactory 추가
  • CircuitBreaker 를 사용하여 외부 서비스 호출 실패 시 빈 ArrayList를 반환하도록 변경하여 오류가 발생하지 않게 한다.

결과 확인

  • OrderService를 기동하지 않고 UserService 기동 후 http://127.0.0.1:8080/userService/users/${user_id} 호출 주문 정보 가져오는 로직에서 기동이 안된 OrderService 때문에 오류가 발생했지만 CircuitBreaker 추가 후 빈 리스트 orders를 반환하는 것을 볼 수 있다.

CircuitBreaker 커스터 마이징

Resilience4JConfiguration.class
package com.example.UserService.config;  

import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;  
import io.github.resilience4j.timelimiter.TimeLimiterConfig;  
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;  
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.security.config.Customizer;  

import java.time.Duration;  

@Configuration  
public class Resilience4JConfiguration {  

    @Bean  
    public Customizer<Resilience4JCircuitBreakerFactory> globalCustomConfiguration() {  

        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()  
                .failureRateThreshold(4)  
                .waitDurationInOpenState(Duration.ofMillis(1000))  
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)  
                .slidingWindowSize(2)  
                .build();  

        TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()  
                .timeoutDuration(Duration.ofSeconds(4))  
                .build();  

        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)  
                .timeLimiterConfig(timeLimiterConfig)  
                .circuitBreakerConfig(circuitBreakerConfig)  
                .build());  
    }
}
  • CircuitBreaker를 커스터 마이징 한다.
  • failureRateThreshold(4): Circuit Breaker가 open 상태로 전환되는 실패 비율의 임계값을 4%로 설정
  • waitDurationInOpenState(Duration.ofMillis(1000)): Circuit Breaker가 open 상태로 유지되는 시간을 1초로 설정
  • slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED): 슬라이딩 윈도우를 호출 수 기준으로 설정
  • slidingWindowSize(2): 슬라이딩 윈도우 크기를 2로 설정
  • timeoutDuration(Duration.ofSeconds(4)): 서비스 호출의 타임아웃 시간을 4초로 설정
  • Resilience4JConfigBuilder(id): 주어진 ID를 사용하여 Resilience4J 설정을 빌드
  • timeLimiterConfig(timeLimiterConfig): 위에서 정의한 Time Limiter 설정을 적용
  • circuitBreakerConfig(circuitBreakerConfig): 위에서 정의한 Circuit Breaker 설정을 적용
반응형

'Cloud > MSA' 카테고리의 다른 글

[MSA] Micrometer  (1) 2024.12.02
[MSA] MSA 분산 추적  (0) 2024.12.02
[MSA] Kafka Connect  (0) 2024.12.02
[MSA] Apache Kafka  (0) 2024.12.02
[MSA] Feign Client  (0) 2024.12.02

+ Recent posts