10--SpringCloud Alibaba:Sentinel周阳老师
2021:10--SpringCloud Alibaba:Sentinel
https://www.cnblogs.com/coderD/p/14350076.html SpringCloud
https://www.cnblogs.com/coderD/p/14350073.html SpringCloud 和 Eureka
https://www.cnblogs.com/coderD/p/14350082.html SpringCloud 和 Zookeeper
https://www.cnblogs.com/coderD/p/14350086.html SpringCloud-Ribbon/OpenFeign
https://www.cnblogs.com/coderD/p/14350091.html SpringCloud:Hystrix 断路器
https://www.cnblogs.com/coderD/p/14350097.html SpringCloud:服务网关 gateway
https://www.cnblogs.com/coderD/p/14350099.html SpringCloud:Config/Bus
https://www.cnblogs.com/coderD/p/14350103.html SpringCloud:Stream/Sleuth
https://www.cnblogs.com/coderD/p/14350110.html SpringCloud Alibaba:Nacos
https://www.cnblogs.com/coderD/p/14350114.html SpringCloud Alibaba:Sentinel
https://www.cnblogs.com/coderD/p/14350119.html SpringCloud Alibaba:Seata
代码:https://gitee.com/xue--dong/spring-cloud
阳哥脑图:https://gitee.com/xue--dong/spring-cloud
SpringCloud Alibaba Sentinel 实现熔断与限流
一个轻量级的面向云原生微服务的流量控制,熔断降级组件。
监控保护微服务。
复制代码
Sentinel:分布式系统的流量防卫兵。Hystrix的阿里版。
看一下我们以前学习的Hystrxi所具有的缺陷:
1. 需要我们自己搭建监控平台微服务。
2. 没有一套web界面可以给我们进行更细粒化话的配置
流量监控,速率控制,服务熔断,服务降级......
细化的监控要求越多,Hystrix的配置越多,各种@HystrixCommand的注解标签...
Sentinel:现在做成一个网站,我们在网站上进行配置即可。
Sentinel:
单独一个组件,可以独立出来,不需要搭建相应的监控微服务。
支持界面化的细粒度统一配置。
约定 > 配置 > 编码:
这些配置我们都可以写在代码里,但是现在的趋势是:约定 > 配置 > 编码。所以我们本次
还是大规模的学习使用配置和注解,尽量少些代码。
主要特征:
复制代码
一句话:就是我们之前讲的Hystrix
复制代码
Sentinel 的下载安装
怎么用
主要应对以下场景:
服务雪崩
服务降级
服务熔断
服务限流
复制代码
Sentibel 分为两个部分
核心库:Java客户端,不依赖任何框架,能够运行所有Java运行时环境,同时对Dubbo/SpringCloud等框架
也有较好的支持。
控制台:Dashboard基于SpringBoot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
复制代码
Sentinel 安装
1. 下载到本地:sentinel-dashboard-1.7.2.jar
2. 运行命令:
1.前提: JDK1.8,8080端口不能被占用。
命令:java -jar sentinel-dashboard-1.7.2.jar
复制代码
可能会卡住:刷出这些信息就启动好了。
复制代码
后台启动成功。
2. 访问Sentinel管理界面
http://localhost:8080
账号/密码:sentinel/sentinel
复制代码
SpringCloud Alibaba Sentinel 初始化演示工程
1. 启动 Nacos8848 成功:startup.cmd
2. 新建一个 Module
1. cloudalibaba-sentinel-service8401
2. POM
以后基本上:nacos+sentinel一起配。
复制代码
<!--SpringCloud alibaba Sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--spring cloud alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
复制代码
完整POM
复制代码
<!--SpringCloud alibaba sentinel-datasource-nacos:后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud alibaba Sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--spring cloud alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--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>
<!--热部署-->
<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>
复制代码
3. yml
spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,
该 Server 会与 Sentinel 控制台做交互。
比如 Sentinel 控制台添加了1个限流规则,会把规则数据 push 给这个 Http Server 接收,
Http Server 再将规则注册到 Sentinel 中。
spring.cloud.sentinel.transport.port:指定应用与Sentinel控制台交互的端口,
应用本地会起一个该端口占用的HttpServer
复制代码
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
# Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,假如被占用会自动从8719开始一次+1扫描,直至找到被占用的端口。
port: 8719
management:
endpoints:
web:
exposure:
include: "*"
复制代码
4. 主启动类
复制代码
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelMainApp8401 {
public static void main(String[] args) {
SpringApplication.run(SentinelMainApp8401.class, args);
}
}
复制代码
5. 业务类
流控controller:FlowLimitController
复制代码
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA(){
return "-------testA";
}
@GetMapping("/testB")
public String testB(){
return "--------testB";
}
}
复制代码
3. 启动 Sentinel8080
要敲一下回车
复制代码
4. 启动 8401 微服务后查看 sentinel 控制台
空空如也,啥都没有。
原因:Sentinel采用的是懒加载机制
执行一次访问即可:
http://localhost:8401/testA
http://localhost:8401/testB
效果:8401 cloudalibaba-sentinel-service 被监控到
复制代码
8401微服务已经成功纳入Sentinal:8080,被实时监控。
复制代码
Sentinel 流控规则
1. 基本介绍
流控规则分为:流控模式和流控效果。
复制代码
选项含义:全的限流属性
复制代码
2. 流控模式
看一下在流控效果:快速失败下的三个流控模式。
复制代码
2.1 QPS 直接失败
QPS:每秒钟的请求数量,当调用该api的QPS达到阈值的时候,进行限流。
1. 直接(默认)失败
新增一个流控规则:
对/testA的访问超过 1/s 直接页面报错
没有修改高级选项:采用系统默认的快速失败。
复制代码
2. 测试一下:
1s以内多次发送请求:http://localhost:8401/testA
被限流:被Sentinel直接限流了。
Blocked by Sentinel (flow limiting)
复制代码
还能继续请求,只要是不超过1/s
3. 小结
表示1秒钟内查询一次就是OK,若超过次数1,就直接-快速失败,报默认错误。
复制代码
4. 思考:
如果超过了流控规则,页面会报如下错误:
复制代码
直接调用默认的报错信息,技术方面OK。但是是否应该有我们自己的后续处理呢?
应该有类似Hystrix的服务奖fallback的兜底方法。
复制代码
2.2 线程数直接失败
线程数:当调用该api的线程数达到阈值的时候,进行限流。
线程数1:一次只能处理一个该api:/testA请求
复制代码
演示效果:
复制代码
被Sentinel限流了:
复制代码
2.3. 流控模式:关联
关联: 当关联的资源达到阈值时,就限流自己。
比如当与A关联的资源B达到阈值后,就限流A自己。
比如支付接口达到阈值后,就限流下单订的接口。
1. 设置流控规则:
A关联资源B,当B访问量超过1/s,限流A
复制代码
2. 测试一下效果
postman模拟并发密集访问/testB
复制代码
3. 效果
20个线程轮流每0.3s访问一下/testB,在此期间显然超过了给A设定得到 流控模式:关联。
被Sentinel限流。
复制代码
大批量线程高并发访问B,导致A失效了。
复制代码
2.4. 流控模式:链路
多个请求调用同一个微服务。
1. 达到指定的QPS n/s 就会被限流。
2. 调用该API的线程数,达到指定的阈值时,进行限流。
复制代码
3. 流控效果:
快速失败在上面的流控模式我们演示过了,它是默认的流控效果。
直接失败,抛出异常:Blocked by Sentinel(flow limiting)
复制代码
3.1 流控效果:预热 Warm up
公式:阈值除以coldFactor(默认是3),经过预热时长后才会达到阈值。
Warm up:预热,冷加载
复制代码
QPS设定的是10,预热时长5,这里采用Warm up的流控效果:
实际上阈值是,即一次性过来10的并发量会被我限流,最开始只能接受10/3的并发量,
这是为了保护系统遭受突然性的高并发。
然后阈值开始慢慢上升,最后达到了设定的10,给系统一个适应的过程。
复制代码
测试:
复制代码
狂点请求:
最开始阈值是3/s,会报错限流。
5s后能抗的住10/s,不会报错了。
复制代码
应用场景:
秒杀系统在开启的瞬间,会有很多流量上来,很可能会把系统杀死,预热方式就是为了保护系统。
可以慢慢把流量放进来,慢慢的把阈值增长到设置的阈值。
复制代码
3.2 流控效果:排队等待
匀速排队,阈值只能是QPS不能是线程数。
这种方式主要用于处理间隔性突发的流量,例如消息队列。
想象一下这样的场景:
在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的
空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
理解:
当阈值超出时,不要直接拒绝,让其排队。
大量的请求到来时,一点一点匀速处理。
复制代码
测试:
Postman模拟请求:20个线程每1s访问一个。
复制代码
结果:
复制代码
Sentinel 降级规则
Circuit-Breaking:熔断降级
复制代码
1. 降级策略:
1. RT:平均响应时间,秒级
平均响应时间:超出阈值且在时间窗口设定的时间内,且通过的请求>=5,两个条件同时满足后触发降级。
窗口期过后关闭断路器。
RT最大49000,更大的需要通过 -Dcsp.sentinel.statistic.max.rt=XXXX才能生效。
注意: 虽然叫做平均响应时间,但是含义是1秒内发过来的所有请求,处理花费的时间。
所以应该是:处理1s内所有请求话费的时间。
1.1 例如:设置平均响应时间200ms,时间窗口1s
含义: 每秒请求数超过5个,并且请求的响应时间超过0.2s秒时,则下一个请求1s内不可用。
当时间窗口结束则关闭降级。
5是一个固定值。
如果1S内发送过来6个请求,但是我在200ms内处理完了不触发。
时间窗口就是不可用时间。
复制代码
2. 异常比例(秒级):
OPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;
时间窗口内服务降级,时间窗口结束,关闭降级。
3. 异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级。
4. 进一步说明:
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高)
对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致的联机错误。
当资源被降级后,在接下来的降级时间窗口内,对该资源的调用都将自动熔断
(默认抛出DegradeException)
5. Sentinel的断路器是没有半开状态的:
要么断,要么开。
半开的状态:系统会自动去检测请求可以抗住了。可以抗住就打开,不行就继续半开。
复制代码
2. Sentinel 降级:RT
1. 测试接口:
为了让平均响应时间增大,在代码中加入sleep休眠。
这样每一个请求的响应时间就会增大,让其平均响应时间大于前面配置的RT,200ms。
实际上RT策略就是模拟服务器可能线程忙,CPU处理不过来,处理的慢的情况。
平时正常时不会降级,当该服务状态差时,就需要减少请求,让其从忙的状态恢复出来,也就是时间
窗口内开启熔断降级让服务器恢复到正常状态。
RT:是为了让服务器从突然变忙的状态回去过来而设置的。
复制代码
@GetMapping("/testD")
public String testD(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "------testD";
}
复制代码
2. 新增降级规则:RT的配置
复制代码
3. 测试:
Jmeter压测:
并发数10,每次并发时间间隔1s,一直循环。
一秒10个,一秒10个....
设置的降级规则:RT的配置
允许的平均响应时间:200ms
熔断降级后:有1s时间恢复。
复制代码
压测开启前:正常
虽然平均响应时间超过200ms,但是并发量只是1,没有超过5,所以没有触发熔断降级。
复制代码
开启压测:
10个线程1秒内,同时访问/testD。
显然会触发熔断降级:
复制代码
4. 开启压测后:
永远一秒钟打进来10个线程(大于5个)调用/testD,我们希望200ms处理完本次任务,
如果200ms还没处理完,在之后1s时间窗口内,断路器打开,微服务不可用。
复制代码
3. Sentinel 降级:异常比例
异常比例:
当资源的每秒请求量 >=5,并且每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态。
每秒异常占比超过10%时,且每秒的请求超过5次,打开熔断降级:
复制代码
模拟异常:
复制代码
思考这样的情况:
复制代码
我们只访问一次,就报错。
我们会疑惑:只发送了一次请求,虽然错了,但是没有满足一秒超过5次请求。
为什么弹出这个页面。
要注意这个页面不是熔断降级,是一个正常的异常抛出。
根本没有进入熔断,走的时候RuntimeException。
因为我们的服务再接下来的1秒内还能正常请求。
复制代码
4. Sentinel 降级:异常数
异常数:
当资源近1分钟的异常数目超过阈值后会进行熔断。
注意时间窗口一定要大于60s,如果小于60s,则熔断结束后仍可能在进入熔断状态。
所以时间窗口必须大于60s。
复制代码
测试:
一次请求:
复制代码
发送5次请求后,
复制代码
Sentinel 热点 Key 限流。
1. 基本介绍
1. 热点参数限流
何为热点?即经常访问的数据。
很多时候我们希望统计某个热点数据中访问频次做高的数据(Top K),并对其进行访问限制。
例如:
商品ID为参数,统计一段时间内最长购买的商品ID并进行限购。
用户ID为参数,针对一段时间内频繁访问的用户ID进行限制。
热点参数限制会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源
调用进行限制。
热电参数限流可以看作是一种特殊的流量控制,仅对包含热电参数的资源调用生效。
Sentinel利用LRU策略统计最近最常访问的热电参数,及任何人令牌桶算法来进行参数级别的流控。
热电参数限流支持集群模式。
2. 承上启下复习:
兜底方法:分为系统默认和客户自定义两种
系统默认:
之前的case,限流出问题后,都是用sentinel系统默认的提示:Blocked by Sentinel(flow limiting)
我们能不能自定义?类似Hystrix,某个方法出问题了,就找对应的兜底降级方法?
可以:
从@HystrixCommand到@SentinelResource
复制代码
2. 案例:
1. 测试方法
@SentinelResource(value = "testHotKeyabc001", blockHandler = "del_testHotKey")
分析:
其中value = "testHotKeyabc001"是一个标识,标识testHotKey方法
blockHandler = "del_testHotKey":如果违背了Sentinel中配置的流控规则,就会调用我们
兜底的方法del_testHotKey()
复制代码
@GetMapping("/testHotKey") //这个代表rest请求地址
@SentinelResource(value = "testHotKeyabc001", blockHandler = "del_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p1", required = false) String p2){
return "------testHotKey";
}
public String del_testHotKey(String p1, String p2, BlockException e){
return "这次不用默认的兜底提示Blocked by Sentinel(flow limiting),自定义提示:del_testHotKeyo(╥﹏╥)o...";
}
复制代码
2. 配置热点参数限流规则。
绑定testHotKey()方法和它的第一个参数p1。
为其设定一个热点限流规则,当该资源的访问超过1/s时,放生限流执行自定的兜底方法,
复制代码
3. 测试:http://localhost:8401/testHotKey?p1=a&p2=b
1次/s:正常显示
复制代码
迅速点击两次:触发热点参数限流,执行自定义的兜底方法。
复制代码
测试:http://localhost:8401/testHotKey?p2=b
1次/s:正常显示
n/s:仍然正常显示:
因为是为第0个参数绑定的热单参数限流规则,所以该请求不是参数p1的相关资源,不会触发热点参数限流。
复制代码
同理:http://localhost:8401/testHotKey
也不会触发热点参数限流。
4. 修改测试方法
没有兜底方法
复制代码
触发热点参数限流会怎么办:
报一个不友好的页面,所以要配置blockHandler:
@SentinelResource(value = "testHotKeyabc001", blockHandler = "del_testHotKey")
复制代码
3. 参数例外项
上一个案例演示了第一个参数p1,为其绑定了一个热点参数限流规则。当其相关的资源QPS超过
1/s时,触发热点参数限流规则,马上限流。
特例情况:
我们期望p1参数当它是某个特例时,它的限流值和平时不一样。
特例:假如当p1的值等于5时,它的阈值可以达到200...
1. 配置规则
当参数p1的值为5时,阈值是200/s
当参数p1的值不为5,阈值是1/s
复制代码
2. 测试:狂点http://localhost:8401/testHotKey?p1=5
没有触发限流。
复制代码
3. 参数类型
复制代码
4. 测试方法添加一个异常:
复制代码
测试:直接报错
复制代码
要注意:
Sentinel它只管你有没有触发它的限流规则,也可以说只管这个web交互页面(控制台)里面的东西。
配置类的东西Sentinel可以管,java异常的错误我不管。
5. 小结:
@SentinelResource主管配置出错,运行出错该走异常就走异常。
Hystrix中可以通过fallback配置这种情况。
复制代码
Sentinel 系统规则
类似全局配置:总控的功能。
复制代码
Sentinel系统自适应限流从整体维度对应用入口流量进行控制。
对整个系统进行的规则配置,触发后整个系统限流,类似全局配置。
配置参数:
复制代码
Sentinel : @SentinelResource
1. 按资源名称限流 + 后续处理
1. 启动nacos+sentinel
复制代码
2. 修改module
cloudalibaba-sentinel-service8401
3. POM
添加依赖:
复制代码
<!--引入我们自定义的公共api jar包-->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
复制代码
4. 业务类RateLimitController
新建一个业务类:
复制代码
@RestController
@Slf4j
public class RateLimitController {
@GetMapping("/byResource")
@SentinelResource(value = "byResourceQWER", blockHandler = "handlerException")
public CommonResult byResource(){
return new CommonResult(200, "按资源名称限流测试OK", new Payment(2020L, "serial001"));
}
public CommonResult handlerException(BlockException e){
return new CommonResult(404, e.getClass().getCanonicalName()+"\t 服务不可用");
}
}
复制代码
5. 按资源名称添加限流规则
复制代码
6. 测试:
自测成功
复制代码
请求超过:1/s:触发限流
复制代码
7. 问题1:此时关闭服务8401看看:
刷新:发现刚才为8401中的byResourceQWER资源添加的流控规则消失了
复制代码
显然这种添加的规则是临时的。
复制代码
2. 按照 URL 地址限流 + 后续处理
1. 测试controller
复制代码
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl(){
return new CommonResult(200, "按照URL限流测试OK", new Payment(2020L, "serial002"));
}
复制代码
2. 自测成功:
复制代码
3. 按照URL配置限流规则
既可以用URL也可以用Resource来指定限流规则:
复制代码
这个没有自定义的兜底的方法,他会用系统默认的。
复制代码
3. 我们面临的问题:
每个方法/API接口我们都要添加一个对应的限流规则吗?---代码膨胀
如果我们要为接口添加自定义的兜底方法,这个处理方法又要和业务代码耦合在一起,一多就很臃肿
但是用系统自带的又没有体现我们的业务要求。
没有一个全局统一的处理方法。
复制代码
4. 客户自定义限流处理类
要和业务代码解耦
1. 创建CustomerBlockHandler类用于自定义限流处理逻辑
复制代码
//创建CustomerBlockHandler类用于自定义限流处理逻辑: 提示一个4444异常
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException e){
return new CommonResult(4444, "全局GLOBAL的客户自定义处理---1");
}
public static CommonResult handlerException2(BlockException e){
return new CommonResult(4444, "全局GLOBAL的客户自定义处理----2");
}
}
复制代码
2. 指定自定义限流处理类和其中的方法作为兜底的方法
自定义限流处理类:blockHandlerClass = CustomerBlockHandler.class
自定义限流处理方法:blockHandler = "handlerException2"
@SentinelResource(value = "CustomerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
3. 测试接口
复制代码
/**
* 测试全局的自定义限流处理方法:指定自定义限流处理类和其中的方法作为兜底的方法
* @return
*/
@GetMapping("/rateLimit/CustomerBlockHandler")
@SentinelResource(value = "CustomerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
public CommonResult CustomerBlockHandler(){
return new CommonResult(200, "全局的客户自定义处理", new Payment(2020L, "serial003"));
}
复制代码
4. 自测
复制代码
5. 添加限流规则
复制代码
6. 测试:
触发限流规则进入到我们自定义的全局限流处理方法中
复制代码
7. 结构图
复制代码
5. 更多注解属性说明
@SentinelResource注解:
注意:注解方式埋点不支持private方法。
虽然可以用代码做上面这些配置,但是一般我们不会去用,增加代码耦合。
了解一下3个相关的核心API:
复制代码
Sentinel : 服务熔断
主要内容:
1. sentinel整合ribbon+openFeign+fallback
2. Ribbon系列
3. Feign系列
4. 熔断框架比较
复制代码
1. Ribbon 系列
1.1 提供者微服务
1. 新建module
cloudalibaba-consumer-nacos-order84
cloudalibaba-provider-payment9003
cloudalibaba-provider-payment9004
2. 9904/9903 POM
复制代码
<dependencies>
<!--spring cloud alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--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>
<!--热部署-->
<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>
复制代码
3. 9003/9004 yml
除了端口其他一样。
复制代码
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 配置nacos地址
management:
endpoints:
web:
exposure:
include: "*"
复制代码
4. 主启动类
复制代码
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9004.class, args);
}
}
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9004.class, args);
}
}
复制代码
5. 业务类
我们知道nacos天生就支持负载均衡,因为它自带着ribbon。
Ribbo:支持负载均衡,自带RestTemplate
复制代码
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
public static HashMap<Long, Payment> hashMap = new HashMap<>();
//模拟一个表
static{
hashMap.put(1L, new Payment(1L, "qwertyuiop"));
hashMap.put(2L, new Payment(2L, "asdfghjkl;"));
hashMap.put(3L, new Payment(3L, "zxcvbnm,."));
}
//模拟查表
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
Payment payment = hashMap.get(id);
CommonResult<Payment> result = new CommonResult<>(200,
"from mysql, serverPORT: "+serverPort, payment);
return result;
}
}
复制代码
1.2 消费者微服务
1. cloudalibaba-consumer-nacos-order84
2. POM
复制代码
<dependencies>
<!--spring cloud alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--引入我们自定义的公共api jar包-->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--SpringCloud alibaba sentinel-datasource-nacos:后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud alibaba Sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--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>
<!--热部署-->
<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>
复制代码
3. yml
# 消费者将要去访问的微服务名册:方便controller的@value获取
server-url:
nacos-user-service: http://nacos-payment-provider
复制代码
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 # 配置Sentinel dashboard地址
port: 8719 #sentinel后台端口
# 消费者将要去访问的微服务名册:方便controller的@value获取
server-url:
nacos-user-service: http://nacos-payment-provider
复制代码
4. 主启动类
复制代码
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class,args);
}
}
复制代码
5. 业务类
因为用的Ribbon,所以使用其提供的RestTemplate
复制代码
package com.atguigu.springcloud.alibaba.config;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
复制代码
@RestController
@Slf4j
public class CircuitBreakerController {
@Value("${server-url.nacos-user-service}")
private String SERVER_URL;
//public static final String SERVER_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/"+id, CommonResult.class, id);
if(id==4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
}else if(result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
}
复制代码
6. 目的
加深@SentinelResource(value = "fallback")注解理解:
1. fallback管运行异常
2. blockHandler管配置违规
7. 测试地址:
http://localhost:84/consumer/fallback/1
满足负载均衡:轮询调用9003/9004
复制代码
此时没有配置限流规则也没有自定义的处理方法:所以直接返回一个error Page不友好
http://localhost:84/consumer/fallback/4
复制代码
http://localhost:84/consumer/fallback/5
复制代码
8. 配置限流规则:只配置fallback
类似于Hystrix的服务降级fallback
出现异常,找falback指定的方法。
复制代码
兜底处理异常方法:
复制代码
//handlerFallback:兜底处理异常方法
public CommonResult handlerFallback(Long id, Throwable e){
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 "+e.getMessage(), payment);
}
复制代码
业务类:
复制代码
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handlerFallback")
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/"+id, CommonResult.class, id);
if(id==4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
}else if(result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//handlerFallback:兜底处理异常方法
public CommonResult handlerFallback(Long id, Throwable e){
Payment payment = new Payment(id, "null");
//可以把异常带过来
return new CommonResult<>(444, "兜底异常handlerFallback,exception内容 "+e.getMessage(), payment);
}
复制代码
测试:出现异常fallback来管
复制代码
9. 配置限流规则:只配置blockHandler
blockHandler = "blockHandler"
复制代码
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback", blockHandler = "blockHandler") //fallback = "handlerFallback"
public CommonResult<Payment> fallback(@PathVariable Long id){
CommonResult<Payment> result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/"+id, CommonResult.class, id);
if(id==4){
throw new IllegalArgumentException("IllegalArgumentException,非法参数异常....");
}else if(result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//blockHandler:只负责sentinel控制台的违规配置
public CommonResult blockHandler(@PathVariable Long id, BlockException e){
Payment payment = new Payment(id, "null");
return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水:blockException " + e.getMessage());
}
复制代码
配置一个降级规则:
复制代码
当资源近1分钟的异常数目超过阈值后会进行熔断,时间窗口60s。
测试:http://localhost:84/consumer/fallback/4
一分钟内报了2次错,触发服务降级熔断。
复制代码
所以:blockHandler只负责sentinel控制台的违规配置
10. 配置限流规则:配置fallback和blockHandler
复制代码
配置了一个异常兜底方法:handlerFallback
配置一个流控
复制代码
限流其效果:
复制代码
没有配置限流时,我们迅速发出异常会被异常兜底方法处理,
那么配置限流时,我们再迅速发出多次异常请求,会出现什么效果呢?
触发限流。
也即: 同时配置fallback:处理异常和blockHandler:自定义限流处理方法
在不违规限流规则时,走的是fallback指定的异常处理方法,违反限流规则时,走
blockHandler指定的自定义限流处理方法
复制代码
11. 异常忽略属性:exceptionToIgnore
我们想要某些异常正常的显示出来,好排错。不让我们自定义的异常处理方法处理。
复制代码
2. Sentinel 熔断:openFeign 系列
1. 修改84消费者模块:Feign组件一般是消费侧
2. POM
复制代码
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
复制代码
3. yml
复制代码
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 # 配置Sentinel dashboard地址
port: 8719 #sentinel后台端口
# 消费者将要去访问的微服务名册:方便controller的@value获取
server-url:
nacos-user-service: http://nacos-payment-provider
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
复制代码
4. 主启动类
复制代码
5. 主业务类
后续我们的controller不找restTemplate,不是restTemplate去调用payment微服务中的接口
调用service:调用paymentServcie,service再去调用payment微服务中的接口。
带着@FeignClient注解的业务接口:
复制代码
1.
package com.atguigu.springcloud.alibaba.service;
public interface PaymentService {
@Component
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
//我们要到nacos中找nacos-payment-consumer这个微服务,并且指明了接口出错时的兜底方法
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}") //去找nacos-payment-consumer服务中的相应接口
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
}
2.
package com.atguigu.springcloud.alibaba.service;
public class PaymentFallbackService implements PaymentService {
@Component
public class PaymentFallbackService implements PaymentService {
//如果nacos-payment-consumer服务中的相应接口出事了,我来兜底
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(4444444,"服务降级返回---PaymentFallbackService", new Payment(id, "errorSerial...."));
}
}
}
3.
package com.atguigu.springcloud.alibaba.controller;
public class CircuitBreakerController {
//===========openFeign
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
return paymentService.paymentSQL(id);
}
}
复制代码
6. 自测:成功
复制代码
7. 关掉9003和9004
不会报404错误,因为我们指明了接口出错时的兜底方法
复制代码
Sentinel : 规则持久化
前面我们微服务新增的限流规则后,微服务关闭后就会丢失,当时配置都限流规则都是临时的。
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则
就能看到。只要nacos里面的配置不删除,针对8401上的sentinel上的流控规则就持续存在。
(也可以持久化到文件,redis,数据库等)
复制代码
步骤
针对8401:cloudalibaba-sentinel-service8401
1. POM
添加一个持久化限流规则依赖
复制代码
<!--SpringCloud alibaba sentinel-datasource-nacos:后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
复制代码
2. yml
复制代码
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
# Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,假如被占用会自动从8719开始一次+1扫描,直至找到被占用的端口。
port: 8719
# 添加Nacos数据源配置
datasource:
ds1: # 数据源1
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow # 流控规则
management:
endpoints:
web:
exposure:
include: "*"
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
复制代码
3. 限流规则配置:在nacos写进来一个限流规则配置文件
注意他不是yaml,不同于学nacos时配置yaml文件。
复制代码
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
复制代码
也就是:
为8401微服务中的/rateLimit/byUrl资源配置了一个限流规则。
"grade": 1,
"count": 1,
合起来表示:阈值类型是QPS,且1/s
4. 启动8401,访问一下接口,刷新一下Sentinel
持久化了我们的规则配置文件。
复制代码
5. 关掉8401
停机后流控规则没有了
复制代码
6. 再次重启
发送几次/rateLimit/byUrl请求后,流控规则又出现了。
7. 查看一下数据库
持久化到数据库中了。
复制代码