기본으로 제공되는 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 |
댓글