1.简介
1.1Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
1.2Sentinel的主要特性
1.3解决服务中的哪些问题
1.4Sentinel组件
2.安装Sentinel控制台
运行环境:java8+
在这里我使用的是1.7.0 版本 ,切换版本点击 Tags
下载完毕之后,在当前目录进入cmd命令窗口,输入命令java -jar sentinel-dashboard-1.7.0.jar
注意:sentinel控制启动使用的8080端口,所以8080端口不能被占用
如此,我们访问 http://localhost:8080 即可访问到控制台界面
默认的账号密码都是sentinel
,登录成功我们的控制台就启动完成了
3.服务搭建
在这里我们搭建一个服务来演示sentinel的流量控制
3.1搭建项目基础
pom
在这里springcloud-alibaba
使用的是2.1.0.RELEASE
版本,spring-boot
使用的是2.2.2.RELEASE
版本
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot整合Web组件+actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
注意:在使用sentinel时,要暴露端口
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719 #指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer,如果该端口被占用,该端口号自动+1
management:
endpoints:
web:
exposure:
include: '*' ##暴露端口
主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Xkuna
* @date 2020/8/16 20:15.
*/
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args) ;
}
}
3.2创建流控 controller
很简单,在这里我们只是创建了连个接口
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Xkuna
* @date 2020/8/16 20:16.
*/
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "----testA" ;
}
@GetMapping("/testB")
public String testB() {
return "----testB" ;
}
}
3.3启动
启动该服务,访问 http://localhost:8080 登录成功后,会发现 列表中并不存在服务,这是为什么呢?
原来,sentinel采用了懒加载机制,所以我们访问一下 http://localhost:8401/testA ,刷新控制台就会看到服务列表中存在我们的服务了,
在簇点链路中没有发现我们的testB接口
,我们访问下http://localhost:8401/testB 刷新控制台 就可以看到了,
4.流量控制规则
4.1 QPS 直接 快速失败 规则
先解释下什么是QPS:
每秒查询率(QPS,Queries-per-second)是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。
也就是每秒的请求数。
所以我们在这里设置的QPS为1,也就是每秒的请求数超过1会直接报错。
4.2 线程数 直接 规则
先删除上面的流控规则
注意:流控规则中,QPS有流控效果,而线程数没有
在这里我们设置的线程数为1,我们快速刷新 http://localhost:8401/testA ,发现不会报错。
那么线程数为1代表着什么呢?
这里的线程数为1,代表着 后台有一个线程处理该 资源名 的请求, 当我们快速点第二次刷新时,线程已经处理完了上一次请求,所以不会报错,而会接着处理我们的请求
在这里我们可以修改下controller,让testA
接口暂停 0.8秒
注意!!!
我们修改完controller重启服务,因为没有配置sentinel持久化规则(在该博文不介绍),所以sentinel中我们在上面设置的 线程直接规则会丢失,服务重启完刷新控制台,重新配置即可
我们用浏览器 快速多次 访问 http://localhost:8401/testA,
此时证明,我们的规则配置生效了
4.3 QPS 关联 快速失败 规则
在配置该规则之前 我们把修改的controller 修改回来
然后刷新sentinel控制台,创建我们的新的流控规则— QPS 关联 快速失败 规则
这样配置的含义:
当与A关联的资源B达到阈值后,就限流自己,简而言之:“B惹事,A挂了”
此时,为了方便测试,我们用postman 连续访问 testB (连续访问200次, 每次0.3s),我们在用浏览器访问testA
此时,我们可以看出我们配置的规则生效了,
4.4 QPS 直接 预热 规则
默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值
在这里我们的配置的含义是:大量请求开始在预热时长 5 s 内,QPS阈值慢慢的从3(10 / 3)长到 10
请求 http://localhost:8401/testA 快速刷新,发现前5s 大量请求中会出现报错,5s后几乎没有报错(手动刷新请求很难做到QPS > 10)
如此,我们的配置生效了
4.5QPS 直接 排队等待 规则
我们首先将之前的规则全部删除
然后配置新的规则
这里的规则的含义是:
QPS 阈值为1,如果一秒内QPS > 1,那么这些请求不会直接失败,而是在 设置的超时时间内排队,超时时间内还没有请求资源,那么该请求才会失败。
以上是常用的几种Sentinel流量控制规则 😄
5.自定义返回信息
我们发现 服务在违背 流量控制规则时,返回的信息为sentinel默认返回的,实际上是可以自定义的
在这里修改controller
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Xkuna
* @date 2020/8/16 20:16.
*/
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
//value 可以随便起 保证唯一即可,一般是url路径取消斜线
@SentinelResource(value = "testA", blockHandler = "testAHandler")
public String testA() {
return "----testA" ;
}
// 自定义blockHandler 一定要传入 BlockException 否则不起效
public String testAHandler(BlockException blockException){
return "testA blockException-----" ;
}
@GetMapping("/testB")
public String testB() {
return "----testB" ;
}
}
此时先访问下 http://localhost:8401/testA
然后登录控制台 发现多了一个资源路径 testA
所以 要想我们自定义的BlockHandler生效 ,在配置流量规则时的资源名就得是 代码中 @SentinelResource
的 value
多次连续访问 http://localhost:8401/testA
所以我们自定义的 @SentinelResource
的blockHandler生效了
关于@SentinelResource
注解
-
@SentinelResource
写在业务类方法上 -
value
是我们自定义的资源名 -
blockHandler
是违背sentinel的规则(流控,熔断降级,热点规则等等)时的处理方法除了这些还有我们下面要讲的
-
blockHandlerClass
:每个方法都配一个blockHandler
会导致代码臃肿,耦合度高,所以我们可以配置一个类单独配置blockHandler
,使用时用blockHandlerClass
指向类,blockHandler
指向方法即可
还有处理java程序运行时出现错误的 fallbackHandlerClass
以及 fallbackHander
,在这里就不赘述了
6.配置HandlerClass
import com.alibaba.csp.sentinel.slots.block.BlockException;
/**
* @author Xkuna
* @date 2020/8/17 17:29.
*/
public class MyHandler {
public static String blockHandlerInfo(BlockException blockException){
return "---自定义blockHandlerInfo" ;
}
}
修改controller
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springcloud.myHandler.MyHandler;
/**
* @author Xkuna
* @date 2020/8/16 20:16.
*/
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
@SentinelResource(value = "testA", blockHandlerClass = MyHandler.class, blockHandler = "blockHandlerInfo")
public String testA() {
return "----testA" ;
}
// 自定义blockHandler 一定要传入 BlockException 否则不起效
// public String testAHandler(BlockException blockException){
// return "testA blockException-----" ;
// }
@GetMapping("/testB")
public String testB() {
return "----testB" ;
}
}
此时,重启8401服务,访问 一次 http://localhost:8401/testA
进入控制台 看到testA
资源名称入驻
添加流控规则
多次刷新访问 http://localhost:8401/testA
此时,我们配置的blockHandlerClass 成功😄
7.热点规则
在sentinel中特别的划分出了热点规则,在这里我们配置下
修改controller(添加/hot
接口)
注意:热点规则配置的blockHandler需要按照原方法 传参,参数也不可缺少BlockException
, 所以在这里我们就不使用blockHandlerClass
了
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springcloud.myHandler.MyHandler;
/**
* @author Xkuna
* @date 2020/8/16 20:16.
*/
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
@SentinelResource(value = "testA", blockHandlerClass = MyHandler.class, blockHandler = "blockHandlerInfo")
public String testA() {
return "----testA" ;
}
// 自定义blockHandler 一定要传入 BlockException 否则不起效
// public String testAHandler(BlockException blockException){
// return "testA blockException-----" ;
// }
@GetMapping("/testB")
public String testB() {
return "----testB" ;
}
@GetMapping("/hot")
@SentinelResource(value = "hot", blockHandler = "blockHandlerHot")
public String hot(@RequestParam(value = "type", required = false) String type,
@RequestParam(value = "name", required = false) String name){
return "hot controller" ;
}
//注意参数要跟原方法一直 ,但要加上 BlockException参数
public String blockHandlerHot(String type, String name, BlockException blockException){
return "hot blockHandler" ;
}
}
重启服务,访问 http://localhost:8401/hot 刷新控制台
找到左侧菜单栏的 热点规则
7.1 普通配置
发现只支持QPS模式
- 参数索引:是 我们controller hot 接口的参数下标,在这里指的是 参数
type
- 单机阈值:在这里是1,所以一秒钟允许通过1个请求
- 窗口时长:是在访问该资源QPS超过阈值,触发该规则的有效期,超过后重新统计
在这里我们的接口两个参数都为非必须传参,又因为热点规则的索引为0(限制参数为type),所以我们此时请求的url
只要不含有 type参数,该规则与请求无关
访问 http://localhost:8401/hot 和 http://localhost:8401/hot?name=xxx 多次连续刷新请求,都会发现没有触发我们的热点规则
此时我们访问 http://localhost:8401/hot?type=xxx 或者 http://localhost:8401/hot?type=xxx&name=xxxx
只要携带了参数type
,当我们多次快速刷新请求时,就会触发热点规则,并返回我们自定义的blockHandler
热点规则的普通配置生效!
7.2高级配置
此时,我们配置了参数例外项
不论普通配置还是高级配置,一个热点规则都是针对一个参数,只有携带此参数的url才生效
解释下这样配置的含义:
- url含有参数
type
该规则才生效,不含有参数type
的url直接忽略该规则 - 参数
type
的值为ok时,阈值为1,参数type
的值为其他时,阈值为5 - 窗口时长为5 s
测试ok,sentinel的流控规则 和 热点规则就是这些了 😃