Spring Cloud Gateway 제한 작업 에 대한 자세 한 설명
7924 단어 SpringCloudGateway흐름 을 제한 하 다
API 게 이 트 웨 이 는 모든 요청 의 입구 로 서 요 구 량 이 많 습 니 다.저 희 는 동시 방문 요청 에 대해 속 도 를 제한 하여 시스템 의 가용성 을 보호 할 수 있 습 니 다.
자주 사용 하 는 제한 알고리즘,예 를 들 어 토 큰 통 알고리즘,누 출 통 알고리즘,카운터 알고리즘 등 이 있 습 니 다.
Zuul 에서 우 리 는 스스로 흐름 제한 기능 을 실현 할 수 있다. 스프링 클 라 우 드 게 이 트 웨 이의 등장 자체 가 줄 을 대체 하 는 데 쓰 인 다.
그것 을 대체 하려 면 반드시 강력 한 기능 이 있어 야 한다.성능 상의 장점 을 제외 하고 Spring Cloud Gateway 는 많은 새로운 기능 을 제공 했다.예 를 들 어 오늘 우리 가 말 하고 자 하 는 제한 작업 은 사용 하기에 매우 간단 하 다.오늘 우 리 는 Spring Cloud Gateway 에서 어떻게 제한 작업 을 하 는 지 배 울 것 이다.
현재 제한 흐름 은 Redis 기반 의 실현 을 제공 합 니 다.우 리 는 대응 하 는 의존 도 를 증가 해 야 합 니 다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
KeyResolver 를 통 해 흐름 을 제한 하 는 Key 를 지정 할 수 있 습 니 다.예 를 들 어 저 희 는 사용자 에 따라 흐름 을 제한 하고 IP 에 따라 흐름 을 제한 해 야 합 니 다.IP 제한
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
exchange 대상 을 통 해 요청 정 보 를 얻 을 수 있 습 니 다.여 기 는 HostName 을 사 용 했 습 니 다.사용자 에 따라 스 트림 을 제한 하려 면 현재 요청 한 사용자 ID 나 사용자 이름 을 가 져 오 면 됩 니 다.예 를 들 어:사용자 제한 흐름
이러한 방식 으로 흐름 을 제한 합 니 다.요청 경로 에 userId 인 자 를 휴대 해 야 합 니 다.
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
인터페이스 제한 흐름요청 주소 의 uri 를 스 트림 키 로 가 져 옵 니 다.
@Bean
KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
그리고 흐름 제한 필터 정 보 를 설정 합 니 다:
server:
port: 8084
spring:
redis:
host: 127.0.0.1
port: 6379
cloud:
gateway:
routes:
- id: fsh-house
uri: lb://fsh-house
predicates:
- Path=/house/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@ipKeyResolver}"
인터페이스 에 접근 하여 테스트 할 수 있 습 니 다.이 때 Redis 에 해당 하 는 데이터 가 있 습 니 다.
127.0.0.1:6379> keys *
1) "request_rate_limiter.{localhost}.timestamp"
2) "request_rate_limiter.{localhost}.tokens"
대괄호 안에 우리 의 흐름 제한 키 가 있 습 니 다.여 기 는 IP 이 고 로 컬 은 localhost 입 니 다.
Spring Cloud Gateway 가 현재 제공 하 는 제한 흐름 은 상대 적 으로 간단 하 다.실제 에서 우리 의 제한 흐름 전략 은 여러 가지 상황 이 있 을 것 이다.예 를 들 어:
스 트림 소스 코드
// routeId fsh-house,id key, localhost。
public Mono<Response> isAllowed(String routeId, String id) {
// RedisRateLimiter
if (!this.initialized.get()) {
throw new IllegalStateException("RedisRateLimiter is not initialized");
}
// routeId
Config routeConfig = getConfig().getOrDefault(routeId, defaultConfig);
if (routeConfig == null) {
throw new IllegalArgumentException("No Configuration found for route " + routeId);
}
//
int replenishRate = routeConfig.getReplenishRate();
// ,
int burstCapacity = routeConfig.getBurstCapacity();
try {
// key (request_rate_limiter.{localhost}.timestamp,request_rate_limiter.{localhost}.tokens)
List<String> keys = getKeys(id);
// The arguments to the LUA script. time() returns unixtime in seconds.
List<String> scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "",
Instant.now().getEpochSecond() + "", "1");
// allowed, tokens_left = redis.eval(SCRIPT, keys, args)
// LUA
Flux<List<Long>> flux = this.redisTemplate.execute(this.script, keys, scriptArgs);
// .log("redisratelimiter", Level.FINER);
return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L)))
.reduce(new ArrayList<Long>(), (longs, l) -> {
longs.addAll(l);
return longs;
}) .map(results -> {
boolean allowed = results.get(0) == 1L;
Long tokensLeft = results.get(1);
Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft));
if (log.isDebugEnabled()) {
log.debug("response: " + response);
}
return response;
});
}
catch (Exception e) {
log.error("Error determining if user allowed from redis", e);
}
return Mono.just(new Response(true, getHeaders(routeConfig, -1L)));
}
LUA 스 크 립 트:
local tokens_key = KEYS[1]
local timestamp_key = KEYS[2]
--redis.log(redis.LOG_WARNING, "tokens_key " .. tokens_key)
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
local fill_time = capacity/rate
local ttl = math.floor(fill_time*2)
--redis.log(redis.LOG_WARNING, "rate " .. ARGV[1])
--redis.log(redis.LOG_WARNING, "capacity " .. ARGV[2])
--redis.log(redis.LOG_WARNING, "now " .. ARGV[3])
--redis.log(redis.LOG_WARNING, "requested " .. ARGV[4])
--redis.log(redis.LOG_WARNING, "filltime " .. fill_time)
--redis.log(redis.LOG_WARNING, "ttl " .. ttl)
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil then
last_tokens = capacity
end
--redis.log(redis.LOG_WARNING, "last_tokens " .. last_tokens)
local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil then
last_refreshed = 0
end
--redis.log(redis.LOG_WARNING, "last_refreshed " .. last_refreshed)
local delta = math.max(0, now-last_refreshed)
local filled_tokens = math.min(capacity, last_tokens+(delta*rate))
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
new_tokens = filled_tokens - requested
allowed_num = 1
end
--redis.log(redis.LOG_WARNING, "delta " .. delta)
--redis.log(redis.LOG_WARNING, "filled_tokens " .. filled_tokens)
--redis.log(redis.LOG_WARNING, "allowed_num " .. allowed_num)
--redis.log(redis.LOG_WARNING, "new_tokens " .. new_tokens)
redis.call("setex", tokens_key, ttl, new_tokens)
redis.call("setex", timestamp_key, ttl, now)
return { allowed_num, new_tokens }
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
[MeU] Hashtag 기능 개발➡️ 기존 Tag 테이블에 존재하지 않는 해시태그라면 Tag , tagPostMapping 테이블에 모두 추가 ➡️ 기존에 존재하는 해시태그라면, tagPostMapping 테이블에만 추가 이후에 개발할 태그 기반 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.