Spring Cloud Gateway 구축 및 Nacos, Sentinel 연동 방법

Gateway 모듈 생성 및 기본 설정

마이크로서비스 아키텍처에서 각 서비스(사용자, 상품, 주문 등)의 진입점을 단일화하기 위해 서비스 게이트웨이 모듈인 shop-gateway를 구축합니다.

의존성 추가

게이트웨이 모듈의 pom.xml 파일에 Spring Cloud Gateway 스타터 의존성을 추가합니다.

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

애플리케이션 설정

application.yml 파일을 생성하고 서버 포트, 경로 라우팅 및 CORS 설정을 정의합니다.

server:
  port: 10001

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowCredentials: true
            allowedHeaders: "*"
      routes:
        - id: user-service-route
          uri: http://localhost:8060
          order: 1
          predicates:
            - Path=/api-user/**
          filters:
            - StripPrefix=1
        - id: product-service-route
          uri: http://localhost:8070
          order: 1
          predicates:
            - Path=/api-product/**
          filters:
            - StripPrefix=1
  • globalcors: 브라우저의 교차 출처 리소스 공유(CORS) 문제를 해결하기 위한 설정입니다.
  • routes: 요청을 목적지 서비스로 전달하기 위한 라우팅 규칙 배열입니다.
  • predicates: 요청이 특정 조건(예: 경로 패턴)에 맞는지 판단합니다.
  • StripPrefix: 라우팅 시 요청 경로에서 지정된 개수만큼의 접두사를 제거합니다.

메인 실행 클래스

@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

Nacos 서비스 디스커버리 통합

고정된 URL 대신 서비스 명칭을 기반으로 동적 라우팅을 수행하기 위해 Nacos를 연동합니다.

의존성 추가

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

설정 업데이트

라우팅 주소를 lb://서비스명 형태로 변경하여 부하 분산이 가능하도록 수정합니다.

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/api-user/**
          filters:
            - StripPrefix=1

Sentinel을 이용한 트래픽 제어 및 제한

시스템 안정성을 위해 게이트웨이 레벨에서 유입되는 요청을 제한하는 Sentinel 설정을 적용합니다.

의존성 구성

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    </dependency>
</dependencies>

Sentinel 게이트웨이 설정 클래스

Sentinel 필터를 빈으로 등록하고, 제한 발생 시 사용자에게 반환할 응답을 구성합니다.

@Configuration
public class SentinelGatewayConfig {

    private final List<ViewResolver> resolvers;
    private final ServerCodecConfigurer codecConfigurer;

    @Value("${spring.cloud.gateway.discovery.locator.route-id-prefix:}")
    private String routePrefix;

    public SentinelGatewayConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                 ServerCodecConfigurer serverCodecConfigurer) {
        this.resolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.codecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelFilter() {
        return new SentinelGatewayFilter();
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler blockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(resolvers, codecConfigurer);
    }

    @PostConstruct
    public void setupRules() {
        Set<GatewayFlowRule> ruleSet = new HashSet<>();
        ruleSet.add(createRule(routePrefix + "user-service"));
        ruleSet.add(createRule(routePrefix + "product-service"));
        
        GatewayRuleManager.loadRules(ruleSet);
        configureBlockResponse();
    }

    private GatewayFlowRule createRule(String resourceName) {
        GatewayFlowRule rule = new GatewayFlowRule(resourceName);
        rule.setCount(1); // 초당 허용 요청 수
        rule.setIntervalSec(1);
        return rule;
    }

    private void configureBlockResponse() {
        BlockRequestHandler handler = (exchange, t) -> {
            Map<String, Object> responseBody = new HashMap<>();
            responseBody.put("status", 429);
            responseBody.put("message", "요청이 너무 많습니다. 잠시 후 다시 시도해주세요.");
            
            return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(responseBody));
        };
        GatewayCallbackManager.setBlockHandler(handler);
    }
}

최종 애플리케이션 설정

spring:
  main:
    allow-bean-definition-overriding: true
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8888
      eager: true
    gateway:
      discovery:
        locator:
          route-id-prefix: gateway-route-

태그: Spring Cloud Gateway nacos Sentinel Alibaba Cloud microservices

7월 23일 06:12에 게시됨