Sentinel + GateWay 接口限流
一、安装Sentinel控制台
-
下载控制台程序,GitHub连接如下:
-
启动控制台:
java -jar sentinel-dashboard-1.8.0.jar
-
访问控制台
1)默认访问端口是8080,如果要修改,在启动时使用--server.port=port
指定即可。
java -jar sentinel-dashboard-1.8.0.jar --server.port=8081
2)默认访问地址:http://127.0.0.1:8080/#/login
3)访问会要求登录,账户和密码都是sentinel
4)首次登录应该是空空如也
二、引入依赖
父项目依赖:
(此处引入spring-cloud-alibaba的主要是为了通过父工程统一控制与spring-cloud-alibaba相关的依赖版本;不引入也是可以的,只是要在后续的依赖中手动指定版本)
<properties>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
<spring-cloud-alibaba.version>2.1.2.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
网关项目依赖:
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
这里需要注意,实现网关限流,只需要引入上面这两个依赖即可,网上有很多教程依赖了很多没用或者重复的引用,这里给出这两个依赖包含的依赖包供大家参考:
三、配置网关
spring:
cloud:
sentinel:
#心跳启动
eager: true
transport:
#sentinel控制台访问路径
dashboard: localhost:8080
port: 8080
gateway:
routes:
- id: redirect_route
#转发地址
uri: lb://service
#断言,要转发的请求
predicates:
- Method=POST,GET
#过滤器
filters:
#把请求路径中的test替换成service
- RewritePath=/test/(?<segment>.*), /service/$\{segment}
四、添加限流
启动网关项目和后台服务,在Sentinel控制台就可以看到网关的项目了。此时可以添加限流规则:
资源名:此处填网关的路由id(或者请求路径,比如 /test),资源表示请求路径或者网关路由ID。按照上面的yaml配置文件,此处我填的是redirect_route
单机阈值:每秒支持的请求次数
填好之后,便可以接口限流了:
五、测试
频繁请求下,Sentinel会返回如下信息:
至此,Sentinel接口限流完成。
Sentinel + GateWay + Nacos 实现配置持久化
上面的流控配置,是存在内存中的,一旦网关项目重启,配置就会消失,所以生产是不可能采用这种情况的。通常,需要结合注册中心使用,把流控规则放到配置中心,当规则变化时,也可以由配置中心推送到Sentinel。这里实现基于推模式的配置持久化,详请可参考官网:在生产环境中使用 Sentinel
六、网关增加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
七、配置Nacos
spring:
application:
name: pms-gateway
cloud:
nacos:
config:
#这里表示使用dev命名空间
namespace: dev
#分组ID
group: pms-v3.0
#nacos服务地址
server-addr: 10.10.62.43:8848
#声明从配置中心中读取的配置文件格式,缺省值为properties
file-extension: yml
discovery:
namespace: dev
group: pms-v3.0
server-addr: 10.10.62.43:8848
sentinel:
#心跳启动
eager: true
transport:
#sentinel控制台访问路径
dashboard: localhost:8080
#sentinel数据源配置(从哪里获取限流规则)
datasource:
ds:
nacos:
#读取哪个命名空间的配置(要和上面的nacos.config.namespace配置一致,否则会读不到文件。如果默认public,则可以不写)
namespace: dev
server-addr: 10.10.62.43:8848
#限流规则配置文件的名称
dataId: pms-gateway-sentinel-flow-limit
groupId: pms-v3.0
data-type: json
rule-type: flow
gateway:
routes:
- id: redirect_route
#转发地址
uri: lb://service
#断言,要转发的请求
predicates:
- Method=POST,GET
#过滤器
filters:
#重写url把url中的pms替换成passenger-management-system
- RewritePath=/test/(?<segment>.*), /service/$\{segment}
我的Nacos命名空间:
八、添加限流规则
在Nacos的dev命名空间添加限流配置,注意Data ID和Group要和上面配置文件写的一致。
[
{
"resource":"redirect_route",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":"false"
}
]
字段详情可参考官网:Sentinel流量控制
resource:资源名,即限流规则的作用对象
count: 限流阈值
grade: 限流阈值类型(QPS 或并发线程数)
limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
strategy: 调用关系限流策略
controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)
九、测试
重启gateway项目,到Sentinel的【流控规则】可以看到Nacos的配置已经同步过来了。
自定义响应结果
在第五步,可以看到,限流返回的是429报错,如果想自定义返回响应信息,可以通过以下方式实现:
//sentinel异常统一返回处理
package com.pci.pms.gateway.filter;
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.pci.common.entity.ResponseCodeEnum;
import com.pci.pms.gateway.entity.CommonResponse;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
@Component
public class SentinelExceptionHandler implements BlockRequestHandler {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
CommonResponse<String> resp = new CommonResponse<>();
int status = 200;
//限流响应
if (throwable instanceof FlowException) {
resp.setCode(10001);
resp.setMessage("提示:系统繁忙,请稍后再试");
status = 429;
}
//服务降级响应
else if (throwable instanceof DegradeException) {
}
//热点参数限流响应
else if (throwable instanceof ParamFlowException) {
}
//触发系统保护规则响应
else if (throwable instanceof SystemBlockException) {
}
//授权规则不通过响应
else if (throwable instanceof AuthorityException) {
}
//返回固定响应信息
ServerHttpResponse response = serverWebExchange.getResponse();
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return ServerResponse.status(status).contentType(MediaType.APPLICATION_JSON_UTF8).body(fromObject(JSONObject.toJSONString(resp)));
}
}
//响应类
package com.pci.pms.gateway.entity;
import lombok.Data;
@Data
public class CommonResponse<T> {
/**
* 响应码
*/
private String code;
/**
* 响应消息
*/
private String message;
/**
* 响应数据
*/
private T data;
}