ゲートウェイサービスの電流制限構成
背景
電流制限の役割は言うまでもなく、一部の悪意のあるリクエストを防ぐためによく使用されます。制限のないリクエスト インターフェイスはサービスの処理時間が長くなり、応答の遅延やサービスのブロックなどにつながるため、一部の高頻度インターフェイスでは、電流制限機能などの追加も可能です。
たまたまゲートウェイには独自のサービスフロー制限機能があるので、その機能を実現するためにゲートウェイを利用します。
スロットルルールを作成する
1.RateLimiterConfig設定ファイルを追加します
package org.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
@Configuration
@Slf4j
public class RateLimiterConfig {
RateLimiterConfig(){
log.info("限流配置加载");
}
/**
* 根据用户ip限流规则
* @return
*/
@Primary
@Bean(value = "remoteAddrKeyResolver")
public KeyResolver remoteAddrKeyResolver(){
return exchange -> {
String hostAddress=exchange.getRequest()
.getRemoteAddress()
.getAddress()
.getHostAddress();
System.out.println(hostAddress);
return Mono.just(hostAddress);
};
}
/**
* 根据Path限流
*
* @return key
*/
@Bean(value = "pathKeyResolver")
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().toString());
}
}
2. 追加したばかりの Bean を構成ファイルでスキャンできるように構成ファイルを作成します。
spring:
cloud:
gateway:
discovery:
locator:
enabled: false
routes:
# 商品模块
- id: Commodity #路由id,唯一
uri: lb://Commodity
predicates:
- Path=/commodity/** #以city开头的请求都负载到consumer服务
filters:
- name: RequestRateLimiter
args:
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 5
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@remoteAddrKeyResolver}"
知らせ:
- サービスでフローを制限する必要がある場合は、そのサービスにフィルター設定を追加するだけでよく、フロー制限が必要ない場合は追加する必要はありません。
- 名前フィールドは次のようにする必要があります
RequestRateLimiter
- キーリゾルバーパラメータは、Spring に挿入された Bean の名前に対応します。
しかし、ユーザーがリクエスト後に上限に達するようにインターフェースをリクエストした場合、戻り値は 1 となり、フロントエンドはバックエンドに何が起こったのか分からず、設計要件を満たしていないことがわかりました。
それでサービスアップグレード
|
|
|
\|/
カスタム戻り情報構成クラス
GatewayRequestRateLimiterGatewayFilterFactory カスタム例外クラスを追加します
package org.example.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/***
* @限流自定义
* @author CMS
*/
@Slf4j
@Component
public class GatewayRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {
private final RateLimiter defaultRateLimiter;
private final KeyResolver defaultKeyResolver;
public GatewayRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
super(defaultRateLimiter, defaultKeyResolver);
this.defaultRateLimiter = defaultRateLimiter;
this.defaultKeyResolver = defaultKeyResolver;
log.info("限流自定义返回加载");
}
@Override
public GatewayFilter apply(Config config) {
KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
return (exchange, chain) -> resolver.resolve(exchange).flatMap(key -> {
String routeId = config.getRouteId();
if (routeId == null) {
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
routeId = route.getId();
}
String finalRouteId = routeId;
return limiter.isAllowed(routeId, key).flatMap(response -> {
for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
}
if (response.isAllowed()) {
return chain.filter(exchange);
}
log.warn("已限流: {}", finalRouteId);
ServerHttpResponse httpResponse = exchange.getResponse();
//修改code为500
httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
if (!httpResponse.getHeaders().containsKey("Content-Type")) {
httpResponse.getHeaders().add("Content-Type", "application/json");
}
//此处无法触发全局异常处理,手动返回
DataBuffer buffer = httpResponse.bufferFactory().wrap(("{\n"
+ " \"code\": \"1414\","
+ " \"message\": \"服务器限流\","
+ " \"data\": \"Server throttling\","
+ " \"success\": false"
+ "}").getBytes(StandardCharsets.UTF_8));
return httpResponse.writeWith(Mono.just(buffer));
});
});
}
private <T> T getOrDefault(T configValue, T defaultValue) {
return (configValue != null) ? configValue : defaultValue;
}
}
次に、設定ファイルを変更します
spring:
cloud:
gateway:
discovery:
locator:
enabled: false
routes:
# 商品模块
- id: Commodity #路由id,唯一
uri: lb://Commodity
predicates:
- Path=/commodity/** #以city开头的请求都负载到consumer服务
filters:
- name: GatewayRequestRateLimiter #修改此处,改成我们自定义的返回信息配置类
args:
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 5
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@remoteAddrKeyResolver}"
最後に、電流制限の作業は問題ありません。
リマインダー: ゲートウェイはデフォルトの電流制限ルールを 1 つだけ持つことができます。2 つ設定する必要がある場合は、-> ( メッセージ 3 件) Spring Cloud Gateway の電流制限適応型マルチルール ソリューションの CSDN ブログ記事を参照してください。