티스토리 뷰

인프런 이도원님의 'Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)' 듣고 정리한 내용입니다.

 

Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA) - 인프런 | 강의

Spring framework의 Spring Cloud 제품군을 이용하여 마이크로서비스 애플리케이션을 개발해 보는 과정입니다. Cloud Native Application으로써의 Spring Cloud를 어떻게 사용하는지, 구성을 어떻게 하는지에 대해

www.inflearn.com

 

1. CircuitBreaker

마이크로서비스 통신할 때 발생하는 오류에 대해 살펴보겠다.

 

CircuitBreaker

장애가 전파되는 것을 막기 위해 장애가 발생할 경우 외부와의 통신을 막는 역할을 수행한다.

특정 서비스가 정상적으로 동작하지 않았을 경우 다른 기능으로 대체하여 수행하는데, 이것을 장애 회피라고 한다.

 

CircuitBreaker가 닫혔다는 의미는 정상적으로 다른 마이크로서비스를 사용할 수 있다는 의미이다.

CircuitBreaker가 열렸다는 의미는 클라이언트의 요청을 최종적인 마이크로서비스에게 전달하지 않고 CircuitBreaker에서 기본값이나 우회할 수 있는 값을 리턴한다는 의미이다.

 

즉, 여러 마이크로서비스들이 연결되어 있는 상태에서 문제가 생겼을 경우 하나의 마이크로서비스만큼은 정상적인 작동을 할 수 있게 도와주는 역할을 한다.

 

CircuitBreaker도 작동시키지 않고 order-service도 실행시키지 않았을 때를 먼저 확인해보자.

 

실행시켜야 할 것: Eureka 서버, apigateway-service, config-service, rabbitmq, user-service

order service를 실행시키지 않았을 때 발생하는 오류

 

2. Resilience4j

Resilience4j 라이브러리를 사용해보겠다.

 

pom.xml

<!--Resilience4j-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

 

UserServiceImpl.java

@Slf4j
@Service
public class UserServiceImpl implements UserService {

    UserRepository userRepository;
    BCryptPasswordEncoder passwordEncoder;

    Environment env;
    RestTemplate restTemplate;

    OrderServiceClient orderServiceClient;

    CircuitBreakerFactory circuitBreakerFactory;

    @Autowired
    public UserServiceImpl(UserRepository userRepository,
                           BCryptPasswordEncoder passwordEncoder,
                           Environment env,
                           RestTemplate restTemplate,
                           OrderServiceClient orderServiceClient,
                           CircuitBreakerFactory circuitBreakerFactory) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.env = env;
        this.restTemplate = restTemplate;
        this.orderServiceClient = orderServiceClient;
        this.circuitBreakerFactory = circuitBreakerFactory;
    }

...

    @Override
    public UserDto getUserByUserId(String userId) {
        UserEntity userEntity = userRepository.findByUserId(userId);

        UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);

        /* ErrorDecoder */
//        List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
        // 여기서 CircuitBreaker를 사용해보겠음!
        CircuitBreaker circuitbreaker = circuitBreakerFactory.create("circuitbreaker");
        List<ResponseOrder> orderList = circuitbreaker.run(() -> orderServiceClient.getOrders(userId),
                throwable -> new ArrayList<>());

        userDto.setOrders(orderList);

        return userDto;
    }

...

}

문제가 발생했을 경우 비어있는 List를 반환하도록 설정하였다.

 

 

Resilience4Config.java - Resilience 등록

@Configuration
public class Resilience4Config {
    @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());
    }
}

failureRateThreshold() : 실패율 임계값 구성 (임계값 이상일 경우 Open 상태)

waitDurationInOpenState() : CircuitBreaker를 열어둔 상태를 유지하는 지속 기간 (이 기간 이후는 half-open 상태)

slidingWindowType() : CircuitBreaker를 닫을 때 통화 결과를 기록하는 데 사용되는 슬라이딩 창의 유형 (카운트 or 시간)

slidingWindowSize() : CircuitBreaker를 닫을 때 호출 결과를 기록하는데 사용되는 슬라이딩 창의 크기 구성

 

- CircuitBreaker 상태

(1) Closed: CircuitBreaker가 감싼 내부 프로세스로 요청을 보내고 응답을 받을 수 있다.

(2) Open: CircuitBreaker가 내부 프로세스로 요청을 보내지 않는다.

(3) Half-Open: CircuitBreaker가 열려 있지만 내부 프로세스로 요청을 보내고 실패율을 측정해서 Closed 또는 Open 상태로 변경한다.

 

- CircuitBreaker 타입

(1) 카운트 기반: 새로운 측정값이 들어올 경우 제일 오래된 측정값을 제거한 뒤 총 집계 갱신

(2) 시간 기반: 가장 오래된 부분 집계 버킷이 제거되고 총 집계가 갱신

 

빈 List 반환 결과 확인

 

이제 order-service를 작동시켜보자.

 

order-servcie의 Controller에서 Kafka 설정들을 잠시 주석 처리하고 JPA 사용하도록 수정하자.

DB도 mysql이 아닌 h2 쓰도록 설정 바꿔주기!

 

주문등록하고 다시 조회해보면

order-service의 작동 유무에 따라 주문 내역이 빈값으로 들어오기도, 주문내역 데이터를 반환하기도 한다.

 

 

3. Distributed Tracing

분산된 환경에서 데이터를 수집하고 추적하는 시스템인 Zipkin에 대해 알아보자.

Zipkin의 대기 시간 문제를 해결하는데 필요한 타이밍 데이터를 수집하는데 도움이 된다.

 

4. Trace ID and Spand ID

마이크로서비스들 간의 통신할 때 전체 트랜잭션에 대한 로그 추적이 어렵다. 추적하기 위해서 연관된 ID가 필요한데, 이러한 ID를 자동으로 생성해주는 것이 Spring Cloud Sleuth이다.

 

Span

하나의 요청에 사용되는 작업의 단위이다.

 

Trace

트리 구조로 이루어진 Span 셋으로, 하나의 요청에 대한 같은 Trace ID 발급한다.

 

사용자의 요청이 시작이 되고 끝날 때까지 같은 Trace ID를 사용하고 각 마이크로서비스들 간에 생기는 Transaction 사이에서는 Span ID가 발급된다.

 

Zipkin 설치

curl -sSL https://zipkin.io/quickstart.sh | bash -s

 

Zipkin 실행

java -jar zipkin.jar

 

안녕

 

http://127.0.0.1:9411/zipkin/ 페이지 이동 시

1. 우측 상단에서 Trace ID 검색

2. Dependencies → 각각의 서비스들 간의 관계를 나타내준다.

3. Find a trace → 어떤 Trace를 사용할 수 있는지 검색할 수 있다.

 

5. Zipkin server 활용

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함