1. 准备
下载链接:https://linta0.lanzout.com/iKoau06h7s8h
密码:an8j
启动方法:进入cmd模式,接着在cmd中进入下载后的文件夹,然后输入下面的指令
java -Dserver.port=8898 -Dcsp.sentinel.dashboard.server=localhost:8898 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar
注意:8898是自己指定的端口,可以按照自己的需求进行指定
本次的SpringBoot版本为: 2.2.5.RELEASE
SpringCloud Alibaba版本为: 2.2.1.RELEASE
2. 公共返回值准备
导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
说明:
- 在项目中,基本上都会定义一个公共的返回值类型。前端向后端发出的请求,最终都是以公共的返回值类型响应
- 前端在发生触发流控的规则后,可以编写一个服务降级或服务熔断后的方法,给用户返回一个托底数据。在Sevice层中,每个方法都会有不同的返回值,如果每一个方法都去单独编写一个相对应的降级或熔断的处理方法,那么会有很大的工作量。所以需要有一个公共的返回值,这样所有的方法都可以使用同一个降级或熔断的处理方法
- 在项目的开发中,理应使用公共的返回值,也可以用枚举编写专门的错误码
服务降级和服务熔断不了解的,可以去看我的另一篇文章:
公共返回值
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private String code;
private String msg;
private T data;
}
公共返回值相关方法
public class ResultUtil {
public static<T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode("200");
result.setMsg("success");
result.setData(data);
return result;
}
public static<T> Result<T> success() {
Result<T> result = new Result<>();
result.setCode("200");
result.setMsg("success");
return result;
}
public static<T> Result<T> success(String msg, T data) {
Result<T> result = new Result<>();
result.setCode("200");
result.setMsg(msg);
result.setData(data);
return result;
}
public static<T> Result<T> success(String msg) {
Result<T> result = new Result<>();
result.setCode("200");
result.setMsg(msg);
return result;
}
public static<T> Result<T> fail(String code, String msg) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
result.setData(null);
return result;
}
public static<T> Result<T> fail() {
Result<T> result = new Result<>();
result.setCode("500");
result.setMsg("系统异常");
result.setData(null);
return result;
}
public static<T> Result<T> fail(T data) {
Result<T> result = new Result<>();
result.setData(null);
return result;
}
public static<T> Result<T> fail(ResultEnum resultEnum) {
Result<T> result = new Result<>();
result.setCode(resultEnum.getCode());
result.setMsg(resultEnum.getMsg());
result.setData(null);
return result;
}
}
2. SpringBoot项目集成
导入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2.1 注解方式
2.1.1 配置文件
spring:
cloud:
sentinel:
enabled: true
eager: true
transport:
port: 8719
dashboard: localhost:8898
注意:
- 上面代码中“port: 8719”,不能更改
- “dashboard: localhost:8898”,这里的端口号要和启动时指定的端口号一致
2.1.2 使用举例
在Controller方法对应的Service方法的头上加上 @SentinelResource 注解,然后再编写相对应的方法,如下:(下面的方法是用来举例)
@SentinelResource(value = "selectListById",
blockHandler = "selectListByIdBlockHandle",
fallback = "selectListByIdFallback"
)
public Result selectListById(Integer id) {
return ResultUtil.success("请求成功");
}
/**
* 服务降级后所调用的方法
* @param id
* @param exception
* @return
*/
public Result selectListByIdBlockHandle(Integer id, BlockException exception) {
exception.printStackTrace();
return ResultUtil.fail("服务器繁忙,请稍后再试");
}
/**
* 服务熔断后所调用的方法
* @param id
* @param throwable
* @return
*/
public Result selectListByIdFallback(Integer id, Throwable throwable) {
System.out.println("请求异常,异常信息如下:" + throwable);
return ResultUtil.fail("服务器错误,请稍后再试");
}
@SentinelResource注解说明:
- 这个注解可以放在任何位置,可以是一行代码,一个变量,或者是一个方法
- value:资源名称,必需的
- blockHandler:方法被限流后触发的方法,参数最后多一个 BlockException,其余于原函数一致
- fallback:方法被熔断后执行的方法,函数签名(返回值,参数,类型)一致,参数可以多加一个 Throwable
- blockHandler和fallback的方法,都不能是private方法
2.1.3 流控测试
重启项目,进入Sentinel的可视化界面:http://localhost:8898
这里的8898是上面启动时的端口号,首次访问要进行登录。账号和密码都是sentinel
不用关心资源名,主要看我框住的操作
重启项目后,要再一次访问接口,才会在Sentinel中显示出来
资源名是访问该接口的url
QPS 单位时间内的请求上限,单位是秒
单机阈值为1的意思是:在1秒钟内,服务器处理的请求只能是1个。超过1个后,会根据流控的策略(流控效果)进行相对应的处理
点击“新增”后,可以去项目中快速的多次请求该接口
因为我是使用了公共的返回值,那么前端也应该有相对应的方法去接收和解析该公共返回值
当前端解析,发现返回的状态是“失败”后,就可以通过状态给用户做出相对应的提示
前端的处理方式在文章的后面
2.2 文件方式
像上述的注解方式,给每个方法通过注解编写对应的降级和熔断方法。但是在项目中,会有很多的接口,要是每个接口都去编写方法的话,工作量也会很大
而且像上述在可视化界面添加的流控规则,是保存在内存里的,项目重启后会消失。所以需要一种可持久化存储的方式
2.2.1 修改配置文件
spring:
cloud:
sentinel:
enabled: true
eager: true
transport:
port: 8719
dashboard: localhost:8898
datasource:
ds1:
file:
file: classpath:flowRule.json
data-type: json
rule-type: flow
注意:
- “data-type: json”,编写的流控规则保存的文件类型为json文件。拓展一下,用于保存流控规则的地方可以是file(文件),Nacos,ZooKeeper,Apollo,Redis。在这里使用file(文件)
- “file: classpath:flowRule.json”,流控规则的json文件的所在地址
classpath的路径:
2.2.2 json文件的规则说明和可视化界面的关系
json文件编写示例:
- resource:资源名,访问接口的url
- count:单机阈值
- grade:保持为1,其他值会把可视化界面中的流控效果和超时时间给隐藏掉
- limitApp:保持为default,其他值没有太深入摸索,大家可以去了解一下
- strategy:保持为0,其他值会把编写的规则在可视化界面中隐藏
- controlBehavior:流控效果。0表示快速失败,1表示Warm Up,2表示排队等待
对应关系:
重启项目后,进入Sentinel
2.2.3 流控测试
注意:本次是没有在方法上使用注解和编写相对应的降级和熔断方法,而是直接通过在json文件中编写的流控规则来实现流量的控制
在这里,也是同样将QPS调成1来进行测试。然后快速的多次请求接口,接着去查看浏览器控制台
这种编写流控规则文件的方式才是比较常用的。同样,前端当解析到429状态码后,也应该有相对应的处理方法,在这里举例说明一下
注意:本次举例使用的是 Vue 和 Axios
export async function request(params) {
return service({
url: '请求的url',
method: 'POST',
data: params,
headers: {'Content-type': POST_HEADER_URLENCODED}
}).then(response => {
if(response.msg == 'success') {
return response;
} else {
// 请求状态为失败
}
}).catch(error => {
// 状态码除200外的,可以在这里进行处理。比如上面的429
})
}
上面的请求方式是我封装过的,具体的封装可以去看我的另一篇文章