서킷 브레이커 구현 실습

날아올라돼지야·2024년 8월 28일
0

서킷 브레이커 패턴 구현

이번 강의에서는 서킷 브레이커 패턴을 실제로 마이크로서비스에 구현하는 방법을 단계별로 설명합니다. 이 패턴을 사용하면 마이크로서비스 네트워크에서 개별 서비스의 실패가 전체 시스템에 영향을 미치지 않도록 할 수 있습니다.

1. 새로운 섹션 설정

먼저 작업할 새로운 섹션을 설정합니다:

  1. 섹션 폴더 복사:

    • 기존 Section 9 폴더를 복사하여 Section 10으로 이름을 변경합니다. 이 폴더는 새로운 변경 사항을 적용할 공간입니다.
  2. IntelliJ에서 섹션 열기:

    • IntelliJ IDEA에서 Section 10 폴더를 열고 모든 Maven 프로젝트를 로드합니다.
    • Lombok 라이브러리에 대한 애노테이션 프로세싱을 활성화하고, 클린 빌드를 수행합니다.

2. Gateway 서버에서 서킷 브레이커 패턴 구현

2.1 pom.xml 파일 업데이트

GatewayServerpom.xml 파일에 spring-cloud-starter-circuitbreaker-reactor-resilience4j 의존성을 추가합니다. 이를 통해 Gateway 서버에서 서킷 브레이커 기능을 사용할 수 있습니다.

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

2.2 서킷 브레이커 필터 추가

Gateway 서버의 메인 클래스 GatewayServerApplication에서 라우팅 설정에 서킷 브레이커 필터를 추가합니다.

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayServerApplication {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("accountsRoute", r -> r.path("/easybank/accounts/**")
                .filters(f -> f.circuitBreaker(config -> config
                    .setName("accountCircuitBreaker")
                    .setFallbackUri("forward:/fallback/accounts")
                ))
                .uri("lb://ACCOUNTS"))
            .build();
    }
}

위 코드에서 circuitBreaker 필터를 사용하여 accountCircuitBreaker라는 이름으로 서킷 브레이커를 설정합니다. 이 설정은 /easybank/accounts/** 경로로 들어오는 요청을 모니터링하고, 필요한 경우 서킷을 열거나 닫습니다.

2.3 설정 파일 업데이트 (application.yml)

application.yml 파일에 서킷 브레이커의 동작을 제어할 설정을 추가합니다.

resilience4j:
  circuitbreaker:
    configs:
      default:
        slidingWindowSize: 10
        permittedNumberOfCallsInHalfOpenState: 2
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
    instances:
      accountCircuitBreaker:
        baseConfig: default

이 설정은 다음과 같은 역할을 합니다:

  • slidingWindowSize: 서킷 브레이커가 상태를 결정하기 위해 모니터링할 요청 수를 지정합니다.
  • permittedNumberOfCallsInHalfOpenState: half-open 상태에서 허용할 요청 수를 설정합니다.
  • failureRateThreshold: 실패율이 이 값을 초과하면 서킷이 open 상태로 전환됩니다.
  • waitDurationInOpenState: 서킷이 open 상태에 머무는 시간을 지정합니다.

3. 서킷 브레이커 동작 확인

모든 설정을 마친 후, 서킷 브레이커가 제대로 동작하는지 확인합니다.

3.1 서비스 시작

  1. Config Server를 시작합니다.
  2. Eureka Server를 시작하여 다른 서비스들이 등록될 수 있도록 합니다.
  3. Accounts MicroserviceGateway Server를 차례대로 시작합니다.

3.2 서킷 브레이커 동작 확인

Postman을 사용해 /easybank/accounts/api/contact-info 엔드포인트에 요청을 보냅니다.

@GetMapping("/api/contact-info")
public AccountsContactInfoDto getContactInfo() {
    return new AccountsContactInfoDto("Customer Support", "123-456-7890", "support@easybank.com");
}

위 API는 계정 관련 문의 정보를 반환합니다. 이 요청이 성공적으로 처리되면 서킷 브레이커는 closed 상태를 유지합니다.

이제 의도적으로 서비스의 응답을 지연시켜 서킷 브레이커가 open 상태로 전환되도록 해보겠습니다.

@GetMapping("/api/contact-info")
public AccountsContactInfoDto getContactInfo() throws InterruptedException {
    Thread.sleep(15000); // 15초 동안 대기
    return new AccountsContactInfoDto("Customer Support", "123-456-7890", "support@easybank.com");
}

이렇게 하면, 서킷 브레이커가 다수의 요청이 실패하는 것을 감지하고 open 상태로 전환됩니다. 이 상태에서는 추가적인 요청이 즉시 실패하며 503 Service Unavailable 상태 코드가 반환됩니다.

4. 서킷 브레이커 상태 모니터링

4.1 서킷 브레이커 이벤트 확인

서킷 브레이커 이벤트는 /actuator/circuitbreakerevents 엔드포인트를 통해 확인할 수 있습니다.

http://localhost:8072/actuator/circuitbreakerevents?name=accountCircuitBreaker

이 URL을 통해 서킷 브레이커의 상태 변화와 각 이벤트의 세부 정보를 확인할 수 있습니다.

4.2 서킷 브레이커 상태 확인

/actuator/circuitbreakers 엔드포인트를 통해 서킷 브레이커의 현재 상태를 확인할 수 있습니다.

http://localhost:8072/actuator/circuitbreakers

이 페이지에서 accountCircuitBreaker의 상태가 closed, open, 또는 half-open 상태인지 확인할 수 있습니다.

중간 요약

여기까지 서킷 브레이커 패턴을 Gateway 서버에 구현하고, 이를 통해 마이크로서비스 네트워크에서 장애를 예방하는 방법을 실습했습니다. 서킷 브레이커는 시스템의 안정성을 크게 향상시킬 수 있는 중요한 패턴입니다. 이제 이 패턴을 다른 마이크로서비스에 적용하는 방법을 살펴보겠습니다.

앞서 게이트웨이 서버에 서킷 브레이커 패턴을 구현했는데, 이를 보완하는 폴백 메커니즘을 추가하는 방법을 다루겠습니다. 실제 비즈니스 애플리케이션에서는 단순히 런타임 예외를 던지는 것보다 사용자에게 의미 있는 메시지를 전달하는 폴백 메커니즘을 구현하는 것이 중요합니다.

5. FallbackController 클래스 생성

먼저 폴백 메커니즘을 처리할 컨트롤러 클래스를 생성합니다.

1.1 패키지 및 클래스 생성

  • GatewayServer 프로젝트에 새로운 패키지를 생성합니다. 이름은 controller로 지정합니다.
  • 그 안에 FallbackController라는 이름의 클래스를 생성합니다.
package com.easybytes.gatewayserver.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
public class FallbackController {

    @GetMapping("/contactsupport")
    public Mono<String> contactSupport() {
        return Mono.just("An error occurred. Please try after some time or contact the support team.");
    }
}

위 코드에서 FallbackController 클래스는 /contactsupport 경로로 접근할 수 있는 단순한 REST API를 제공합니다. 이 API는 에러 메시지를 반환하며, 이는 폴백 메커니즘에서 사용됩니다.

6. GatewayServerApplication 클래스 수정

이제 생성한 폴백 API를 서킷 브레이커 패턴에 통합합니다.

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayServerApplication {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("accountsRoute", r -> r.path("/easybank/accounts/**")
                .filters(f -> f.circuitBreaker(config -> config
                    .setName("accountCircuitBreaker")
                    .setFallbackUri("forward:/contactsupport")  // Fallback URI 설정
                ))
                .uri("lb://ACCOUNTS"))
            .build();
    }
}

위 코드에서 setFallbackUri("forward:/contactsupport")를 추가하여, 서킷 브레이커가 open 상태로 전환될 때 폴백 메커니즘이 동작하도록 설정합니다.

7. 설정 파일 업데이트 (application.yml)

이미 서킷 브레이커 패턴 설정을 추가한 application.yml 파일에 별도의 변경은 필요하지 않지만, 이전에 설명드린 설정이 제대로 구성되어 있는지 다시 확인합니다.

resilience4j:
  circuitbreaker:
    configs:
      default:
        slidingWindowSize: 10
        permittedNumberOfCallsInHalfOpenState: 2
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
    instances:
      accountCircuitBreaker:
        baseConfig: default

8. 서킷 브레이커 및 폴백 메커니즘 테스트

모든 설정이 완료되면 Postman을 사용해 폴백 메커니즘을 테스트합니다.

8.1 정상적인 시나리오

Postman에서 /easybank/accounts/api/contact-info 경로로 요청을 보냅니다. 이 경우 서킷 브레이커가 closed 상태로 유지되며, 요청이 정상적으로 처리됩니다.

An error occurred. Please try after some time or contact the support team.

이 폴백 메시지가 반환됩니다.

8.2 서킷 브레이커 활성화

이제 서비스의 응답을 지연시켜 서킷 브레이커가 open 상태로 전환되도록 해보겠습니다.

@GetMapping("/api/contact-info")
public AccountsContactInfoDto getContactInfo() throws InterruptedException {
    Thread.sleep(15000); // 15초 동안 대기
    return new AccountsContactInfoDto("Customer Support", "123-456-7890", "support@easybank.com");
}

위 코드로 서비스 응답을 지연시키고, Postman에서 동일한 요청을 다시 보내면 서킷 브레이커가 open 상태로 전환되며, 설정한 폴백 메시지가 반환됩니다.

8.3 서킷 브레이커 상태 확인

/actuator/circuitbreakers/actuator/circuitbreakerevents 엔드포인트를 사용해 서킷 브레이커의 상태를 모니터링할 수 있습니다.

http://localhost:8072/actuator/circuitbreakers

위 엔드포인트에서 서킷 브레이커의 상태를 확인할 수 있습니다. open, half-open, closed 상태 변화를 확인하고, 관련 이벤트 로그를 /actuator/circuitbreakerevents를 통해 확인할 수 있습니다.

중간 요약

이번 강의에서는 게이트웨이 서버에 서킷 브레이커 패턴과 폴백 메커니즘을 구현하는 방법을 다뤘습니다. 서킷 브레이커 패턴을 사용해 마이크로서비스가 장애 상황에서도 안정적으로 동작할 수 있도록 보장하며, 폴백 메커니즘을 통해 사용자에게 의미 있는 메시지를 전달하는 방법을 배웠습니다.

이제 accounts 마이크로서비스에 서킷 브레이커 패턴을 구현하고, feign 클라이언트를 사용하여 loanscards 마이크로서비스와의 통신에서 발생할 수 있는 문제를 처리하기 위해 폴백 메커니즘을 추가하는 방법을 다루겠습니다.

9. pom.xml에 서킷 브레이커 의존성 추가

먼저, accounts 마이크로서비스의 pom.xml 파일에 서킷 브레이커 관련 의존성을 추가합니다.

9.1 pom.xml 수정

spring-cloud-starter-circuitbreaker-resilience4j 의존성을 추가합니다.

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

의존성을 추가한 후, Maven 빌드를 갱신하여 새로 추가된 의존성을 다운로드합니다.

10. application.yml에 서킷 브레이커 설정 추가

서킷 브레이커를 활성화하려면 application.yml 파일에서 feign 클라이언트와 서킷 브레이커를 통합하도록 설정해야 합니다.

10.1 application.yml 파일 수정

다음과 같은 설정을 추가합니다:

spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true

resilience4j:
  circuitbreaker:
    configs:
      default:
        slidingWindowSize: 10
        permittedNumberOfCallsInHalfOpenState: 2
        failureRateThreshold: 50
        waitDurationInOpenState: 10s

여기서 enabled: true 설정은 feign 클라이언트에서 서킷 브레이커를 활성화하는 역할을 합니다. 그리고 추가된 resilience4j 설정은 서킷 브레이커의 동작 방식을 정의합니다.

11. 폴백 클래스 생성

LoansFeignClientCardsFeignClient에 대한 폴백 클래스를 생성합니다. 폴백 클래스는 원래 요청이 실패할 때 실행되는 대체 로직을 정의합니다.

11.1 LoansFallback 클래스 생성

package com.easybytes.accounts.client;

import org.springframework.stereotype.Component;
import java.util.List;

@Component
public class LoansFallback implements LoansFeignClient {

    @Override
    public List<Loan> getLoans(String customerId) {
        return null;  // 예시로 null 반환. 실제로는 더 의미 있는 폴백 로직을 구현할 수 있습니다.
    }
}

11.2 CardsFallback 클래스 생성

package com.easybytes.accounts.client;

import org.springframework.stereotype.Component;
import java.util.List;

@Component
public class CardsFallback implements CardsFeignClient {

    @Override
    public List<Card> getCards(String customerId) {
        return null;  // 예시로 null 반환. 실제로는 더 의미 있는 폴백 로직을 구현할 수 있습니다.
    }
}

이 클래스들은 feign 클라이언트 인터페이스를 구현하며, @Component 애너테이션으로 스프링 컨텍스트에서 빈으로 등록됩니다.

12. FeignClient 인터페이스에 폴백 설정 추가

FeignClient 인터페이스에서 폴백 클래스를 설정합니다.

12.1 LoansFeignClient 수정

@FeignClient(name = "loans", fallback = LoansFallback.class)
public interface LoansFeignClient {
    @GetMapping("/loans/{customerId}")
    List<Loan> getLoans(@PathVariable("customerId") String customerId);
}

12.2 CardsFeignClient 수정

@FeignClient(name = "cards", fallback = CardsFallback.class)
public interface CardsFeignClient {
    @GetMapping("/cards/{customerId}")
    List<Card> getCards(@PathVariable("customerId") String customerId);
}

이제, loanscards 마이크로서비스가 실패하거나 시간이 초과될 경우, LoansFallbackCardsFallback 클래스의 폴백 메서드가 호출됩니다.

13. 서비스 로직에서 폴백 처리

서비스 로직에서 null 체크를 추가하여 폴백 로직이 실행된 경우 적절히 처리합니다.

13.1 CustomerServiceImpl 수정

@Service
public class CustomerServiceImpl implements CustomerService {

    @Autowired
    private LoansFeignClient loansFeignClient;

    @Autowired
    private CardsFeignClient cardsFeignClient;

    @Override
    public CustomerDetails fetchCustomerDetails(String customerId) {
        List<Loan> loans = loansFeignClient.getLoans(customerId);
        if (loans != null) {
            // loans 데이터 처리
        }

        List<Card> cards = cardsFeignClient.getCards(customerId);
        if (cards != null) {
            // cards 데이터 처리
        }

        // 나머지 로직
    }
}

이 코드는 각 FeignClient 호출 후, null 체크를 수행하여 폴백 메커니즘이 작동했는지 확인합니다.

14. 빌드 및 테스트

모든 변경사항을 저장하고 프로젝트를 빌드한 후, 실행하여 제대로 동작하는지 확인합니다. Postman을 사용하여 다양한 시나리오를 테스트할 수 있습니다.

중간 요약

여기까지 accounts 마이크로서비스에 서킷 브레이커 패턴과 폴백 메커니즘을 구현하는 방법을 다뤘습니다.

이 패턴을 통해 특정 마이크로서비스가 실패할 때 전체 시스템이 영향을 받지 않도록 하고, 클라이언트에게 더 나은 사용자 경험을 제공할 수 있습니다.

이제 accounts 마이크로서비스에서 서킷 브레이커 패턴을 구현하고, feign 클라이언트를 사용하여 loanscards 마이크로서비스와의 통신에서 발생할 수 있는 문제를 처리하기 위해 폴백 메커니즘을 추가한 후 데모를 보여드리겠습니다.

15. 프로젝트 설정 및 초기화

15.1 마이크로서비스 실행

우선, Config 서버, Eureka 서버, 그리고 accounts, cards, loans, 그리고 마지막으로 Gateway 서버 애플리케이션을 순차적으로 실행합니다. 이 마이크로서비스들이 모두 실행 중인지 확인하기 위해 Eureka 대시보드에서 각 마이크로서비스가 정상적으로 등록되었는지 확인합니다.

15.2 Accounts 마이크로서비스의 Actuator 확인

accounts 마이크로서비스의 Actuator 엔드포인트를 통해 서킷 브레이커 상태를 모니터링합니다. 이를 위해 브라우저에서 http://localhost:8080/actuator에 접속합니다.

서킷 브레이커 관련 정보를 보려면, circuitbreakers 링크를 클릭합니다. 이 시점에서 요청이 전송되기 전에는 서킷 브레이커가 생성되지 않았기 때문에 아무런 정보도 표시되지 않을 것입니다.

16. 데이터 초기화 및 첫 번째 요청

16.1 Postman을 통한 데이터 생성

Postman을 사용해 accounts, cards, loans 마이크로서비스에 데이터를 생성합니다. 각 마이크로서비스에 데이터를 추가한 후, fetchCustomerDetails API를 호출하여 정상적인 응답을 확인합니다. 이때, 모든 데이터가 제대로 반환되는지 확인할 수 있습니다.

17. 서킷 브레이커 이벤트 확인

17.1 Actuator에서 서킷 브레이커 상태 확인

정상적인 요청이 전달된 후, Actuator 페이지에서 서킷 브레이커 정보를 새로 고칩니다. 이 시점에서 서킷 브레이커가 생성되었으며, 상태가 CLOSED로 표시됩니다. 또한 circuitbreakerevents 엔드포인트를 통해 각 서킷 브레이커의 이벤트 내역을 확인할 수 있습니다.

18. Loans 마이크로서비스 중지 및 테스트

18.1 Loans 마이크로서비스 중지

이제 loans 마이크로서비스를 중지하고 accounts 마이크로서비스에서 해당 서비스에 요청을 보내도록 합니다. 이때 서킷 브레이커는 폴백 메커니즘을 통해 null 값을 반환하며, fetchCustomerDetails API를 호출할 때 loans 데이터가 null로 반환되는 것을 확인할 수 있습니다.

18.2 상태 전환 확인

loans 마이크로서비스가 중지된 상태에서 여러 번 요청을 보내면, 서킷 브레이커가 CLOSED에서 OPEN 상태로 전환됩니다. 이를 Actuator 페이지에서 확인할 수 있으며, circuitbreakerevents에서 상태 전환 이벤트를 확인할 수 있습니다.

19. Cards 마이크로서비스 중지 및 추가 테스트

19.1 Cards 마이크로서비스 중지

이제 cards 마이크로서비스도 중지하고, 동일한 방식으로 fetchCustomerDetails API를 호출합니다. 이 경우, cards 데이터 역시 null로 반환되며, Actuator 페이지에서 서킷 브레이커의 상태가 OPEN으로 변경된 것을 확인할 수 있습니다.

19.2 성공적인 응답 후 서킷 브레이커 상태 복구

loanscards 마이크로서비스를 다시 시작한 후 성공적인 요청을 보내면, 서킷 브레이커의 상태가 OPEN에서 CLOSED로 복구됩니다.

요약 및 결론

구현 단계 요약

  1. 의존성 추가: pom.xml 파일에 spring-cloud-starter-circuitbreaker-resilience4j 의존성을 추가합니다.
  2. Feign Client 수정: FeignClient 인터페이스에서 폴백 클래스를 설정합니다.
  3. 폴백 클래스 구현: FeignClient 인터페이스를 구현하는 폴백 클래스를 생성하여 폴백 로직을 정의합니다.
  4. 설정 추가: application.yml 파일에 서킷 브레이커 관련 설정을 추가합니다.

이번 강의에서는 accounts 마이크로서비스에 서킷 브레이커 패턴과 폴백 메커니즘을 성공적으로 구현하였고, 이를 통해 서비스 간의 의존성으로 인해 발생할 수 있는 문제를 어떻게 방지할 수 있는지 확인했습니다. 이 패턴을 통해 특정 마이크로서비스가 실패하더라도 전체 시스템이 안정적으로 동작할 수 있도록 합니다.

이후 강의에서는 Resilience4j의 다른 패턴들도 탐색할 예정입니다.

profile
무슨 생각하며 사니

0개의 댓글