기본으로 제공되는 RequestRateLimiter 필터를 사용하게 되면 사용률 제한에 걸리지 않은 요청에 대해서는 문제없지만,
제한이 걸린 요청에 대해 우리 서비스 내부적으로 사용하는 응답 포맷을 사용할 수 없다.
그렇기 때문에 응답 포맷을 항상 지정된 형태로 주어야한다면 RequestRateLimiter를 그대로 사용하기는 힘들고,
custom filter를 만들어서 이를 RequestRateLimiter 대신 사용해야할 것이다.
아래는 RedisRateLimiter를 사용하는 custom 사용률 제한 필터의 예제 코드다. (링크)
여기서 Spring Cloud Gateway의 RateLimiter를 사용하기위해 Config 클래스는 HasRouteId 를 상속받아 Router의 ID를 받을 수 있도록 하였다.
(우리가 따로 set하는 코드를 작성할 필요는 없다)
package com.tistory.devs0n.gateway.filters.ratelimit
import org.springframework.cloud.gateway.filter.GatewayFilter
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter
import org.springframework.cloud.gateway.support.HasRouteId
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.server.reactive.ServerHttpResponse
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
@Component
class RequestRateLimitFilter(
private val rateLimiter: RateLimiter<RedisRateLimiter.Config>,
) : AbstractGatewayFilterFactory<RequestRateLimitFilter.Config>(Config::class.java) {
class Config(
val keyResolver: KeyResolver,
) : HasRouteId {
private var routeId: String? = null
override fun getRouteId(): String = this.routeId!!
override fun setRouteId(routeId: String) {
this.routeId = routeId
}
}
override fun apply(config: Config): GatewayFilter {
return GatewayFilter { exchange, chain ->
val response = exchange.response
val keyResolver = config.keyResolver
val routeId = config.routeId
return@GatewayFilter keyResolver.resolve(exchange)
.flatMap { key ->
return@flatMap this.rateLimiter.isAllowed(routeId, key)
}.flatMap { rateLimitResponse ->
return@flatMap when (rateLimitResponse.isAllowed) {
true -> chain.filter(exchange)
false -> this.responseTooManyRequest(response)
}
}
}
}
private fun responseTooManyRequest(response: ServerHttpResponse): Mono<Void> {
response.statusCode = HttpStatus.TOO_MANY_REQUESTS
response.headers.contentType = MediaType.APPLICATION_JSON
return response.writeWith(
Mono.just(
response.bufferFactory().wrap(
"""{
|"success": false,
|"message":"You sent too many requests"
|}""".trimMargin().toByteArray()
)
)
)
}
}
RedisRateLimiter를 그대로 사용하기 때문에 관련 application.yml에 설정을 그대로 사용하면되고,
filter의 name만 변경해주면 그대로 적용가능하다.
server:
port: 9000
spring:
redis:
host: 127.0.0.1
port: 6379
database: 0
cloud:
gateway:
default-filters:
- name: AuthFilter
args:
whiteList:
chrisToken: chris
routes:
- id: all
uri: http://localhost:8080
predicates:
- Path=/**
filters:
# - name: RequestRateLimiter
- name: RequestRateLimitFilter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
redis-rate-limiter.requestedTokens: 1
# redis-rate-limiter.replenishRate: 20
# redis-rate-limiter.burstCapacity: 100
# redis-rate-limiter.requestedTokens: 3
key-resolver: "#{@userIdAsKeyResolver}"
'Java & Kotlin > Spring' 카테고리의 다른 글
[Spring Data JPA] entity update 후 JpaRepository.save 호출에 관하여 (6) | 2023.01.09 |
---|---|
[springdoc-openapi] 고정 header 설정하기 (0) | 2022.12.31 |
Request Rate Limiting with Spring Cloud Gateway - 3. RateLimiterFilter 파헤치기 (2) | 2022.02.08 |
Request Rate Limiting with Spring Cloud Gateway - 2. RequestRateLimiter 필터 적용 (3) | 2022.02.03 |
Request Rate Limiting with Spring Cloud Gateway - 1. 기본 프로젝트 생성 (0) | 2022.01.30 |
댓글