목록
- CicuitBreaker
- Resilience4j
- Distributed Tracing
- Trace ID and Span ID
- Zipkin server 활용
MicroService간의 통신인식, 발생할 수 있는 대표적인 오류의 해결 방법
해결 방법 : CicuitBreaker
500번 에러를 보여주는 것이 아니라
200번(성공)을 보여주고 데이터가 없는 형태처럼 보여준다.
비록 OrderService를 통해 정상적인 주문 내역이 보이지는 않지만, User Service가 가지고 있었던 원래 사용자의 정보 목로을 보는 부분에서는 문제없이 정상적인 처리가 된다.
이렇게 문제가 생겼던 service나 fuction을 더 이상 사용하지 않도록 막고, 정상적으로 복구가 되면 이전처럼 정상적인 흐름으로 바꿔주는 장치를 CircuitBreaker라고 한다.
💡 CircuitBreaker
참고 링크
- circuit : 우회
- 장애가 발생하는 서비스에 반복적인 호출이 되지 못하게 차단
- 특정 서비스가 정상적으로 동작하지 않을 경우 다른 기능으로 대체 수행 → 장애 회피
closed : Cicuit Breake 작동하지 않는다.
open : Cicuit Breake 작동한다.
Resilience4j
Hystrix를 대체하여 Resilience4j를 사용한다.
Resilience4j는 CircuitBreaker를 지원한다.
rate limiter, bulkhead, retry, cache, time limiter를 이용해서 우회할 수 있는 방법, 다음에 어느 정도의 횟수를 가지고 작업을 진행할 것인지, 동기화하는 방법, 타임아웃 관련 작업 등이 제공된다.
💡 Resilience4j
참고링크
- a lightweight fault tolerance library
(fault tolerance library : 에러가 발생한다고 하더라고 정상적인 기능처럼 가용하도록 처리하는 라이브러리)
- Netfilx Hystrix를 기반으로 한다.
예제
CircuitBreaker 적용 X (Order Serivce 기동 X)
User Service에서 회원가입, 로그인까지 마치고 회원 정보 조회를 하려고 한다.
Order Service를 사용할 수 없다는 오류가 뜬다.(실제로 Order Sevice는 실행하지 않고 User Service만 기동했다.
오류 발생 : UnknownHostException: order-service
OrderServiceClient 클래스에서 FeignClient에서 "order-service"를 읽을 수 없다고 해석하면 된다.
CircuitBreaker 적용 O (Order Serivce 기동 X)
그럼 이제 CircuitBreaker를 적용해보자!
- pom.xml
dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
- UserSerivceImpl
@Service
@Slf4j
public class UserServiceImpl implements UserService{
CircuitBreakerFactory circuitBreakerFactory;
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) {
// ... 생략 ...
/* ErrorDecoder */
// circuitbreaker 생성
CircuitBreaker circuitbreaker = circuitBreakerFactory.create("circuitbreaker");
List<ResponseOrder> orderList = circuitbreaker.run(() -> orderServiceClient.getOrder(userId), throwable -> new ArrayList<>()); // circuitbreaker 실행
// run(() -> 정상작동할 경우 반환값, 문제 생겼을 경우 반환값)
userDto.setOrders(orderList);
return userDto;
}
}
Order Serivce는 켜져있지 않은 상태로 사용자 조회 실행!
아까는 오류 발생 : UnknownHostException: order-service 이렇게 Order Serivce가 알 수 없다고 했지만,
지금은 Order Servie와 관련된건 빈 리스트(new ArrayList)로 뜬다.
Resilience4J 설정/커스텀화
- class
Resilience4JConfig 생성
- bean등록
import org.springframework.cloud.client.circuitbreaker.Customizer;
커스텀하고자 하는 팩토리를 Customizer로 감싼다.
@Configuration
public class Resilience4JConfig {
@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 적용 O (Order Serivce 기동 O)
Order Service 기동!
주문 넣기.
User Service에서 사용자 조회
주문이 잘 뜨고 있다.
정리
마이크로 서비스 간(User Serivce와 Order Serivce) 네트워크 통신을 통해서 요청할 때
해당하는 마이크로 서비스에 전달이 안되면 네트워크 관련 오류가 발생할 수 있다.
이런 문제점을 해결하기 위해 문제를 계속 전달하지 않기 위해서 CircuitBreaker를 킨 상태해서 클라이언트 요청을 더이상 Order Serivce로 전달하지 않고 기본적인 데이터 값을 반환할 수 있도록 만들어 놓은 것이다.
마이크로 서비스 간에 호출했을 때 문제가 발생하면
CircuitBreaker을 통해서 더 이상 연쇄적으로 문제가 생기는 것을 전달하지 않고 막는 디폴트 값이라던가 우회할 수 있는 값을 전달한다.