테스트 코드 작성후 글 업데이트 예정
1. 시작하기
한번의 클릭으로도 동작하는 기능이라도, 사용자는 빠른 응답을 기대하며 다중 요청을 보내는 경우가 있습니다.
예를 들어 티켓예매, 선착순 경품 행사, 수강신청은 의도치 않은 과도한 요청이 서버를 대상으로 발생합니다.
또는 한명의 사용자가 초당 1000번의 API 호출을 할 때, 다른 사용자는 초당 1번의 API 호출을 한다면 API 사용관점에서 공정하지 않을 수 있습니다. 과도한 요청과 클라언트별 형평성있는 API 사용을 보장하기 위해 사용자별 요청을 제한할 필요가 있으며, Gateway에서 이를 관리할 수 있습니다.
이번 프로젝트에서는 Spring Cloud Gateway와 Redis를 이용하여 사용자의 요청을 제한하는 Rate Limiting을 구현 해보았습니다.
구현 코드는 아래 Github 에서 확인할 수 있습니다.
https://github.com/kwj2435/spring-cloud-rate-limiting
2. Spring Cloud Gateway란?
API Gateway는 클라이언트와 여러 서비스 간의 요청을 중계하는 서버로, 주로 여러 개의 백엔드 서비스를 하나의 진입점으로 집합하여 사클라이언트 요청을 라우팅, 필터링, 인증, 로깅 처리 합니다.
Spring Cloud Gateway는 이러한 기능을 제공하는 Spring 기반 프레임워크입니다.
주요 특징은 다음과 같습니다.
- 라우팅(Routing)
- 필터링(Filtering)
- 인증 및 보안 : Spring Security 와 쉽게 통합되어 인증 및 권한 부여 등을 처리
- Rate Limiting 및 Circuit Breaker : 일정 시간 동안의 요청 횟수 제한 및 실패한 요청을 처리하는 등의 기능을 제공
- 부하 분산
3. Spring Cloud Gateway에서 Rate Limiting 적용하기
이번 프로젝트에서는 Spring Cloud Gateway에서 제공하는 Rate Limiting 기능을 제공하여 반복된 클라이언트 요청에 제한을 두도록 구현해보았습니다.
Spring Cloud Gateway 사용과 Rate Limiting 적용은 공식 문서의 Guide를 참고하였습니다.
https://spring.io/projects/spring-cloud-gateway
Rate Limiting은 각 클라이언트에 대해 얼마나 많은 요청을 허용할지를 관리합니다. 예를 들어, 초당 10개의 요청을 허용하고 초과하면 요청을 차단하는 식입니다.
이 요청 카운트 정보를 상태(State)로 저장해야 하며, 각 클라이언트에 대해 요청을 추적해야 합니다.
이를 서버 메모리에 저장할 경우 메모리가 부족할 수 있고, DB에 저장하기에는 I/O 비용 부담이 발생합니다.
위와 같은 이유로 Cloud Gateway에서는 Redis를 이용하여 클라이언트의 요청 상태값을 저장 및 조회합니다.
또한 Redis를 통해 중앙 집중식 데이터 저장을 통해 여러대의 Gateway서버가 일관된 클라이언트 정보를 조회할 수 있으며, Redis Cluster 구성을 통한 유연한 확장성과 TTL을 통해 자동 만료 기능을 사용할 수 있는 이점이 있습니다.
구현
먼저 Spring 프로젝트에 Spring Cloud Gateway 의존성을 추가합니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'
}
Spring Cloud Gateway는 Spring Cloud Config, Spring Cloud Netflix 등 여러개의 서브 프로젝트를 포함하고 있고, 각 프로젝트는 서로 호환되는 버전이 필요합니다.
이로 인한 호환성 문제가 발생하지 않도록 BOM을 추가하여 자동으로 호환되는 버전으로 설정되도록 합니다.
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:2024.0.1"
}
}
Spring Cloud Gateway 2024.x 은 WebFlux 기반과 MVC 기반의 Gateway 두가지를 지원합니다.
WebFlux 기반의 Gateway는 spring-cloud-starter-gateway 의존성을 사용하며 Netty 기반의 비동기로 동작합니다.
반면 MVC 기반의 Gateway는 spring-cloud-gateway-mvc 의존성을 사용하며 서블릿,Tomcat 기반에서 동작합니다.
RateLimiting 기능은 WebFlux 기반에서만 동작하기때문에, 이번 프로젝트에서는 WebFlux 방식을 사용하였습니다.
RateLimit 설정은 application.yml 설정과 Java 코드 두가지 방식으로 나뉘며, 기본적으로는 yml파일의 설정을 권장하고 있습니다.
아래는 Redis와 RateLimit 설정이 포함된 yml 코드입니다.
spring:
data:
redis:
host: localhost
port: 6379
jedis:
pool:
max-active: 10
max-idle: 5
min-idle: 1
cloud:
gateway:
mvc:
routes:
- id: test-service
uri: http://localhost:8080
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter:
replenishRate: 5 # 초당 최대 요청 수
burstCapacity: 10 # 최대 초과 요청 허용 수
requestedTokens: 1 # 요청당 소모 토큰수(1개의 요청당 1개의 토큰 소모)
기본적인 Rate Limiting 설정은 yml 만으로도 충분하지만, 커스텀 설정이 필요할 경우 RedisRateLimiter를 직접 정의할 수도 있습니다.
@Configuration
public class RateLimiterConfig {
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(5, 10); // 초당 5개 요청, 최대 10개 초과요청
}
}
4. Rate Limiting 적용 시 고려해야 할 사항
테스트 코드 작성후 글 업데이트 예정 입니다.
'Spring' 카테고리의 다른 글
[Spring] Java 예외(Exception) 처리 전략 (1) | 2024.11.29 |
---|---|
[Spring] 카카오 로그인 REST API 방식 적용 및 구현 (0) | 2024.11.26 |
[Spring] 스프링 부트(Spring Boot) - H2 DB 연동하여 사용하기(with.JPA) (0) | 2024.11.22 |
[Spring] Junit 테스트시 Atomikos 커넥션풀의 Connection Name 중복 오류 (0) | 2024.07.08 |
IntelliJ에서 Spring MVC 세팅하기 (0) | 2024.03.17 |