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-