SpringCloud Alibaba - gateway整合sentinel
SpringCloud Alibaba系列篇
- SpringCloud Alibaba - Nacos搭建及服务注册发现
- SpringCloud Alibaba - openfeign服务间相互调用
- SpringCloud Alibaba - Seata分布式事务搭建及应用
- SpringCloud Alibaba - 整合gateway服务网关
- SpringCloud Alibaba - 搭建Sentinel监控流量控制
- SpringCloud Alibaba - gateway整合sentinel
上文已经依次介绍了gateway跟sentinel
本文将gateway网关于sentinel整合,我们知道所有的请求都经过网关,那在网关就可以对具体某个服务进行限流或者降级等处理。当然如果直接将服务与sentinel整合仅对单个服务进行限流等处理也是可行的。本文还是以跟gateway整合为例
编码coding
本文示例创建两个服务:esp-gateway、manage-api
演示在api上暴露一个外部接口,通过gateway网关调用api接口
再用sentinel动态配置manage-api上接口的限流控制
注:以esp-gateway、manage-api为例,其他可忽略
esp-server
代码与SpringCloud Alibaba - 整合gateway服务网关中一致,本文不再说明。
manage-api
代码与SpringCloud Alibaba - 整合gateway服务网关中一致,本文不再说明。
esp-gateway
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>esp-server</artifactId>
<groupId>com.esp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>esp-gateway</artifactId>
<dependencies>
<!--网关gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- openfeign服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 服务注册/发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--整合sentinel begin-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--整合sentinel end-->
</dependencies>
<build>
<finalName>gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8080
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true
sentinel:
transport:
dashboard: 127.0.0.1:9000
management:
endpoints:
web:
exposure:
include: '*'
sentinel.properties
# 让sentinel识别为网关类型服务
csp.sentinel.app.type=1
测试通过gateway访问api
url:http://localhost:8080/manage/logincaptcha
可以正常通过gateway访问manage-api即可;
8080:gateway端口
manage:manage-api服务名
/logincaptcha:具体api接口路径
网关Sentinel
多请求几次上述接口后刷新sentinel界面
流控
测试每秒限流1qps,选择流控
再刷新几次接口;1秒间隔阈值1QPS,超出限制QPS后提示如下错误
降级
熔断策略说明
- 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
- 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
API管理
只有配置了csp.sentinel.app.type=1才会有该tab项
新增API分组
流控规则 -> 新增,选择Api分组,再选择刚刚新增的api
再多请求几次接口测试流控效果如下
后续优化
自定义异常返回值
异常种类
- 限流异常:FlowException
- 降级异常:DegradeException
- 参数限流异常:ParamFlowException
- 系统负载异常:SystemBlockException
- 授权异常:AuthorityException
GatewayBlockRequestHandler.java
package com.esp.gateway.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.esp.common.enums.ResponseEnum;
import com.esp.common.util.data.JsonUtils;
import com.esp.common.util.response.ResponseUtils;
import com.esp.common.vo.ResponseVo;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义sentinel异常处理
* @author 954L
* @create 2021/12/3 15:51
*/
@Component
public class GatewayBlockRequestHandler implements BlockRequestHandler {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
ResponseEnum responseEnum = null;
if (throwable instanceof FlowException) responseEnum = ResponseEnum.REQUEST_FLOW;
else if (throwable instanceof DegradeException) responseEnum = ResponseEnum.REQUEST_DEGRADE;
else if (throwable instanceof ParamFlowException) responseEnum = ResponseEnum.REQUEST_PARAMFLOW;
else if (throwable instanceof SystemBlockException) responseEnum = ResponseEnum.REQUEST_SYSTEMBLOCK;
else if (throwable instanceof AuthorityException) responseEnum = ResponseEnum.REQUEST_AUTHORITY;
else return ServerResponse.badRequest().body(throwable, Throwable.class);
ResponseVo<Object> responseVo = ResponseUtils.getResponseVo(responseEnum);
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(JsonUtils.obj2Json(responseVo)));
}
}
自定义效果如下: