포스트

LG CNS 부트캠프 학습일지 51일차

학습 내용

  • Kafka에 메세지를 보낼 때 스키마와 페이로드를 구분해서 보내는 방법
  • Resilient4J를 사용해서 서킷 브레이커 (Circuit Breaker) 구현하기
  • Zipkin을 사용해서 마이크로서비스 간 통신을 추적하기

Kafka에 메세지를 보낼 때 스키마와 페이로드를 구분해서 보내는 방법

예를 들어 주문정보를 보내는 경우를 생각해보자. Kafka에 주문정보만 받아서 메세지를 보낼 수도 있다. 하지만 JSON 데이터를 문자열 형태로 보내기 때문에 그 안에 있는 데이터가 문자인지 또는 숫자인지 받는 쪽에서는 알기 어렵다. 따라서 전달하는 데이터의 자료형에 대한 정보를 같이 보내주면 더 좋을 것이다. 그래서 자료형에 대한 정보를 스키마 그리고 실제 데이터를 페이로드의 형태로 보내주는 것이 필요하고 그것을 이번 실습 때 구현해봤다.

구현하는 것 자체는 어렵지 않았다. 주고 받는 자료를 자바 클래스로 만들면 되기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
@AllArgsConstructor
public class KafkaOrderDto implements Serializable {
    private Schema schema;
    private Payload payload;

    @Data
    @Builder
    public static class Schema {
		...
    }

    @Data
    @Builder
    public static class Payload {
		...
    }
}

Resilient4J를 사용해서 서킷 브레이커 (Circuit Breaker) 구현하기

구현한 서비스가 아무런 문제가 없이 잘 돌아가는 것이 이상적일 것이다. 하지만 현실에서 그런 것은 없다. 다양한 이유로 서비스 장애가 발생할 수 밖에 없다. 하나의 서비스가 응답을 하기 위해서 다른 서비스에게 요청을 보내고 응답을 받아야 하는 경우를 상정해보자. 만약 그 다른 서비스에 문제가 발생해서 응답을 하지 못하고 있다면 요청을 보낸 반대쪽 서비스는 자신에게 들어온 요청에 대한 응답을 하지 못한다. 만약 사용자가 그 응답을 기다리고 있어야 한다면 사용자는 좋아하지 않을 것이다.

때문에 다른 마이크로서비스에 문제가 있는지를 요청을 보내기 전에 인지하고 만약 문제가 있다면 즉각 대체 응답을 보내는 것이 필요하다. 이 역할을 하는 것이 서킷 브레이커이다. 한국어로 번역하자면 회로차단기라고 할 수 있는데, 스위치라고 생각하면 편하다. 열려있으면 (open) 스위치가 열려있어서 전기가 흐르지 않는 것이고, 반대로 닫혀있으면 (closed) 스위치가 닫혀있어서 전기가 흐른다.

1
2
3
4
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class Resilience4JConfig {
    @Bean
    public Customizer<Resilience4JCircuitBreakerFactory> specificCustomConfiguration1() {
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                .failureRateThreshold(6)
				.waitDurationInOpenState(Duration.ofMillis(1000))
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
                .slidingWindowSize(3)
				.build();

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

        return factory -> factory.configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig)
                .timeLimiterConfig(timeLimiterConfig).build(), "circuitBreaker1");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UserServiceImpl implements UserService {
    CircuitBreakerFactory circuitBreakerFactory;

    @Override
    public UserDto getUserByUserId(String userId) {
		...

        CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker1");
        orderList = circuitBreaker.run(
                () -> orderServiceClient.getOrders(userId),
                throwable -> new ArrayList<>()
        );

        userDto.setOrders(orderList);

        return userDto;
    }
}

Resilience4JConfig 에서 CircuitBreaker 객체를 빈으로 등록하는 것이 아니라 CircuitBreakerFactoryCustomizer를 등록하고 있다는게 눈에 띤다. 사실 왜 그러냐고 물으면 프레임워크를 만든 사람이 결정한 것이라서 답하기 어려울 것 같다. 아무튼 CircuitBreaker를 이런 방식으로 정의해서 사용한다는 것을 배웠다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

Comments powered by Disqus.