一,应用场景:开放平台提供url给第三方调用时,针对调用方请求进行限流,实现对ip和参数进行限流
两种实现方式
1,对SentinelGatewayBlockExceptionHandler进行改造,新创建一个该类,重写里面的返回方法
可参考:http://www.cppcns.com/ruanjian/java/257764.html
@Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { // Register the block exception handler for Spring Cloud Gateway. return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); }
2,平台实现,自定义响应处理类,实现BlockRequestHandler接口,包装返回信息,初始化调用自定义响应处理类
参考:https://blog.csdn.net/developlee/article/details/100987345
您可以在 GatewayCallbackManager
注册回调进行定制:
-
setBlockHandler
:注册函数用于实现自定义的逻辑处理被限流的请求,对应接口为BlockRequestHandler
。默认实现为DefaultBlockRequestHandler
,当被限流时会返回类似于下面的错误信息:Blocked by Sentinel: FlowException
。
二,代码实现,改造SentinelGatewayBlockExceptionHandler
1,Sentinel配置类
/**
* sentinel配置
*
* @author lr
* @date 2019-09-12 13:31
*/
@Configuration
public class SentinelGatewayConfiguration {
@Autowired
private InetUtils inetUtils;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public SentinelGatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
// @Bean
// @Order(Ordered.HIGHEST_PRECEDENCE)
// public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// // Register the block exception handler for Spring Cloud Gateway.
// return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
// }
/**
* 自定义响应参数
*
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
initGatewayRules();
//GatewayCallbackManager.setBlockHandler(new OpenBlockRequestHandler());
//设置监控ip(多网卡时默认获取有问题,所以需要采用springCloud网卡工具类)
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, inetUtils.findFirstNonLoopbackAddress().getHostAddress());
}
/**
* 初始化路由规则
*/
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
//限制每个ip每秒只能调用5次,
//setBurst突发请求额外增加5个
rules.add(new GatewayFlowRule("open_gateway")
.setCount(5)
.setIntervalSec(1)
.setBurst(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
)
);
//限制一个应用appId每秒只能调用5次
//setBurst突发请求额外增加5个
rules.add(new GatewayFlowRule("open_gateway")
.setCount(5)
.setIntervalSec(1)
.setBurst(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("appId")
)
);
//限制一个应用appId每秒只能调用5次
//setBurst突发请求额外增加5个
rules.add(new GatewayFlowRule("open_gateway")
.setCount(5)
.setIntervalSec(1)
.setBurst(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("appId").setPattern("testApp")
)
);
GatewayRuleManager.loadRules(rules);
}
}
2,自定义响应类
public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
private List<ViewResolver> viewResolvers;
private List<HttpMessageWriter<?>> messageWriters;
private final Supplier<ServerResponse.Context> contextSupplier = () -> {
return new ServerResponse.Context() {
public List<HttpMessageWriter<?>> messageWriters() {
return JsonSentinelGatewayBlockExceptionHandler.this.messageWriters;
}
public List<ViewResolver> viewResolvers() {
return JsonSentinelGatewayBlockExceptionHandler.this.viewResolvers;
}
};
};
public JsonSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers;
this.messageWriters = serverCodecConfigurer.getWriters();
}
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
JSONObject responseObj = new JSONObject();
responseObj.put("code", 403);
responseObj.put("msg", "请求过于频繁,请稍后重试");
byte[] datas = responseObj.toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
}
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
} else {
return !BlockException.isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> {
return this.writeResponse(response, exchange);
});
}
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}
三,代码实现,GatewayCallbackManager
回调
1,Sentinel配置类
/**
* sentinel配置
*
* @author lr
* @date 2019-09-12 13:31
*/
@Configuration
public class SentinelGatewayConfiguration {
@Autowired
private InetUtils inetUtils;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public SentinelGatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
/**
* 自定义响应参数
*
* @return
*/
// @Bean
// @Order(Ordered.HIGHEST_PRECEDENCE)
// public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
// return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
// }
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
initGatewayRules();
GatewayCallbackManager.setBlockHandler(new OpenBlockRequestHandler());
//设置监控ip(多网卡时默认获取有问题,所以需要采用springCloud网卡工具类)
SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, inetUtils.findFirstNonLoopbackAddress().getHostAddress());
}
/**
* 初始化路由规则
*/
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
//限制每个ip每秒只能调用5次,
//setBurst突发请求额外增加5个
rules.add(new GatewayFlowRule("open_gateway")
.setCount(5)
.setIntervalSec(1)
.setBurst(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
)
);
//限制一个应用appId每秒只能调用5次
//setBurst突发请求额外增加5个
rules.add(new GatewayFlowRule("open_gateway")
.setCount(5)
.setIntervalSec(1)
.setBurst(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("appId")
)
);
//限制一个应用appId每秒只能调用5次
//setBurst突发请求额外增加5个
rules.add(new GatewayFlowRule("open_gateway")
.setCount(5)
.setIntervalSec(1)
.setBurst(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("appId").setPattern("testApp")
)
);
GatewayRuleManager.loadRules(rules);
}
}
2,自定义响应消息类
/**
* @author lr
* @date 2019-09-12 15:15
*/
public class OpenBlockRequestHandler implements BlockRequestHandler {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {
// JSON result by default.
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(fromObject(buildErrorResult(ex)));
}
private ResponseBean buildErrorResult(Throwable ex) {
return ResponseBeanUtils.getResponseBean(ErrorCode.REQUEST_QPS_LIMIT_ERROR);
}
}
四,postman调用结果