使用Spring Cloud Alibaba创建springcloud项目-6.Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
官网:https://github.com/alibaba/Sentinel/wiki
下载地址:https://github.com/alibaba/Sentinel/releases
下载完成后,启动sentinel。在你放sentinel-dashboard.jar的文件处打开cmd,输入java -jar sentinel-dashboard.jar,启动完成后。访问localhost:8080,出现以下页面证明成功。账号密码都是sentinel/sentinel
现在我们改造一下payment9001,首先我们在pom.xml加上Sentinel的坐标
<!-- sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
改造application.yml,改造后的yml文件如下:
server:
port: 9001
spring:
application:
name: nacos-payment-provider #本服务的名称
cloud: #注册到nacos
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
management:
endpoints:
web:
exposure:
include: "*"
创建SentinelController
@RestController
public class SentinelController {
@GetMapping(value = "/SentinelA")
public String SentinelA(){
return "SentinelA";
}
@GetMapping(value = "/SentinelB/{id}")
public String SentinelB(@PathVariable Integer id){
return "SentinelB---"+id;
}
}
启动9001。
现在我们登录上sentinel,登录后查看首页
此时发现并没有我们刚才启动的服务。现在我们访问localhost:9001/ SentinelA,访问成功后,再次查看Sentinel控制台。
我们会发现出现了我们启动的服务,可以看出来Sentinel是懒加载。
接下来点击服务,查看菜单
当我们使用QPS为阈值类型时,并设置阈值为1,定义资源,其他默认,则表示一秒内,只需要通过1次请求,其他的均失败。其他的选项我就不一个一个实例了,有兴趣的可以查查资料。
刷新快一点,观察情况
Sentinel会帮你给一个友好的错误提示,但是也可以自己定义处理方法。
改造SentinelController
@RestController
public class SentinelController {
@GetMapping(value = "/SentinelA")
@SentinelResource(value = "SentinelA",blockHandler = "aaa")
public String SentinelA(){
return "SentinelA";
}
public String aaa(BlockException e){
return "被限流了!o(╥﹏╥)o";
}
@GetMapping(value = "/SentinelB/{id}")
public String SentinelB(@PathVariable Integer id){
return "SentinelB---"+id;
}
}
重启项目,重新设置限流,快速刷新,观察情况。
这里注意一下,自定义的方法要跟原接口的返回类型和请求参数一模一样(参数需要多一个BlockException)。
这里还可以写一个处理类,把各个接口的处理方法写进去,创建handler包ControllerHandler类
/**
* 注意都是静态方法
*/
public class ControllerHandler {
public static String SentinelAhandler(BlockException e){
return "SentinelA被限流!"+e.getMessage();
}
public static String SentinelBhandler(Integer id,BlockException e){
return "SentinelB被限流!"+e.getMessage();
}
}
注意都是静态方法。SentinelController改造如下
@RestController
public class SentinelController {
@GetMapping(value = "/SentinelA")
@SentinelResource(value = "SentinelA",blockHandlerClass = ControllerHandler.class,blockHandler = "SentinelAhandler")
public String SentinelA(){
return "SentinelA";
}
// public String aaa(BlockException e){
// return "被限流了!o(╥﹏╥)o";
// }
@GetMapping(value = "/SentinelB/{id}")
@SentinelResource(value = "SentinelB",blockHandlerClass = ControllerHandler.class,blockHandler = "SentinelBhandler")
public String SentinelB(@PathVariable Integer id){
return "SentinelB---"+id;
}
}
启动9001,访问A和B
写到这里会不会有人发现,每次重启服务之后,我们之前配置的规则都不见了。这样对我们来说很不方便,关于sentinel持久化有多种方法,详情见https://www.jianshu.com/p/609961eb6a6e。下面来说一下用nacos的方法
首先pom.xml添加
<!--sentinel持久化-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
application.yml
server:
port: 9001
spring:
application:
name: nacos-payment-provider #本服务的名称
cloud: #注册到nacos
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
datasource:
ds1:
nacos:
server-addr: localhost:8848 #nacos地址
dataID: nacos-payment-provider
groupId: DEFAULT_GROUP
data-type: json #注意是json类型
rule-type: flow
management:
endpoints:
web:
exposure:
include: "*"
然后打开nacos
点击发布。其中内容规则如下
[
{
"resource": "SentinelA",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名
limitApp:流控针对的调用来源,default不区分来源
grade:限流阈值类型(0-根据并发数量来限流 1-根据QPS来进行流量控制)
count:限流阈值
strategy:调用关系限流策略
controlBehavior:流量控制效果(直接拒绝、WarmUP、匀速排队)
clusterMode:是否集群模式
重启9001,访问一下SentinelA,连续点击会发现已经有流控规则了。也可以看Sentinel是否已经有流控规则了。
defaultFallback
现在我们修改一下SentinelA里面的代码
@GetMapping(value = "/SentinelA")
@SentinelResource(value = "SentinelA",blockHandlerClass = ControllerHandler.class,blockHandler = "SentinelAhandler" )
public String SentinelA(){
int i = 10/0;
return "SentinelA";
}
这个运行肯定会报错,看一下是不是进入我们自定义的方法。
直接报错,并没有进入我们自定义的方法。这里注意一下blockHandler方法只能用例流控规则,代码自身出问题是处理不了的。要处理代码自身问题的话需要用defaultFallback,看一下示例
@GetMapping(value = "/SentinelA")
@SentinelResource(value = "SentinelA",blockHandlerClass = ControllerHandler.class,blockHandler = "SentinelAhandler",defaultFallback ="bbb" )
public String SentinelA(){
int i = 10/0;
return "SentinelA";
}
public String bbb(){
return "系统出错了!";
}
再次访问:
这个也有类似blockHandler自定义一个类的写法,这里不做演示了,两个类似。