一、简单案例实现
1.pom文件
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba 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>
<!-- 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>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</version>
</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>
2.配置文件
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: '*'
3.启动类
package com.zj.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/18 13:34
*/
@EnableDiscoveryClient
@SpringBootApplication
public class SentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApplication.class,args);
}
}
4.Sentinel控制台
二、流控规则
- 资源名:唯一名称,默认请求路径
- 针对来源:Sentinel可以针对调用者进行限流,填写服务名。默认default(不区分来源)。
- 阈值类型/单机阈值:
QPS(每秒钟的请求数量):当调用该api的QPS达到阈值时,进行限流
线程数:当调用该api的线程数达到阈值时,进行限流 - 是否集群:不需要集群
- 流控模式:
直接:api达到限流条件时,直接限流
关联:当关联的资源达到阈值时,就限流自己
链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】 - 流控效果:
快速失败:直接失败,抛异常
Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。假设阈值设置为10,预热时长为5,就是说在前5秒中内的阈值为10/3=3,每秒的QPS为3,5秒之后,阈值为10,每秒的QPS为10
排队等待:匀速排队,让请求匀速通过,阈值类型必须设置为QPS,否则无效
三、降级规则
- 慢调用比例:
最大RT(即最大的响应时间):请求的响应时间大于该值则统计为慢调用
统计时长、最小请求数、比例阈值:在统计时长时间范围之内,请求数大于最小请求数,并且其中的慢调用比例达到比例阈值,则触发熔断
熔断时长:服务熔断的时间设置
- 异常比列(秒级)
在统计时长范围内,请求数大于最小请求数,并且其中的异常占比达到比例阈值,触发熔断
- 异常数(分钟级)
在统计时长范围内,请求数大于最小请求数,并且其中异常数量达到异常数,触发熔断
四、热点规则
1.自定义资源名和降级处理(不适用Sentinel默认处理)
- @SentinelResource注解
value:自定义资源名
blockHandler :自定义降级处理方法(注意该方法的参数列表需添加BlockException exception参数,否则服务熔断将会在浏览器直接抛异常)
package com.zj.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/18 13:35
*/
@RestController
public class FlowLimitController {
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1, String p2, BlockException exception) {
return "-----dealHandler_testHotKey";
}
}
2.热点规则配置
- 设置某个访问资源的参数列表中的某一个参数达到指定的阈值进行限流
- 参数例外项:并且单这个参数的类型和参数值符合设定的值时,可以设定单独的阈值
五、系统规则
- Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统保护。系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR阶段)。系统容量由系统的maxQpsminRt估算得出。设定参考值一般是CPU cores2.5。
- CPU usage(1.5.0+版本):当系统CPU使用率超过阀值即触发系统保护(取值范围0.0~1.0),比较灵敏。
- 平均RT:当单台机器上所有入口流量的平均RT达到阀值即触发系统保护,单位是毫秒。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阀值即触发系统保护。
- 入口QPS:当单台机器上所有入口流量的QPS达到阀值即触发保护。
六、@SentinelResource自定义限流处理
1.自定义限流处理逻辑
自定义限流处理类:
package com.zj.springcloud.handler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zj.springcloud.common.CommonResult;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/20 10:56
*/
public class CustomerBlockHandler {
public static CommonResult handleException(BlockException exception){
return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
}
}
使用@SentinelResource配置自定义处理类
package com.zj.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zj.springcloud.common.CommonResult;
import com.zj.springcloud.entity.Payment;
import com.zj.springcloud.handler.CustomerBlockHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/20 10:34
*/
@RestController
public class RateLimitController {
/**
* 自定义通用的限流处理逻辑,
blockHandlerClass:指定CustomerBlockHandler.class类作为处理类
blockHandler:指定CustomerBlockHandler.class类中的handleException方法作为处理方法
上述配置:找CustomerBlockHandler类里的handleException2方法进行兜底处理
*/
/**
* 自定义通用的限流处理逻辑
*/
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客户自定义限流处理逻辑");
}
}
七、SentinelResource+Ribbon熔断配置
- fallback:运行异常
- blockHandler:Sentinel控制台流控配置违规
1.SentinelResource无任何配置(异常直接反馈给用户,用户体验不好)
后台代码:
package com.zj.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.zj.springcloud.common.CommonResult;
import com.zj.springcloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/20 11:33
*/
@RestController
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_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;
}
}
访问页面:
2.SentinelResource只配置fallback(运行异常统一处理,不直接反馈给用户)
package com.zj.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.zj.springcloud.common.CommonResult;
import com.zj.springcloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/20 11:33
*/
@RestController
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback")
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_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;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
}
系统出现异常时,返回自定义的信息,不直接返回异常信息:
3.SentinelResource只配置blockHandler
限流自定义信息处理:
package com.zj.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zj.springcloud.common.CommonResult;
import com.zj.springcloud.entity.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 概述:
* 作者:zhujie
* 创建时间:2021/12/20 11:33
*/
@RestController
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id)
{
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_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;
}
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
2.SentinelResource同时配置fallback和blockHandler
若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑
八、Sentinel持久化规则配置
(1)引入jar包
<!--SpringCloud ailibaba sentinel-datasource-nacos -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
(2)配置文件
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
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
(3)nacos添加配置文件
文件内容:
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
各字段含义:
- resource:资源名称;
- limitApp:来源应用;
- grade:阈值类型,0表示线程数,1表示QPS;
- count:单机阈值;
- strategy:流控模式,0表示直接,1表示关联,2表示链路;
- controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
- clusterMode:是否集群。