去官方下载:https://github.com/alibaba/Sentinel/releases
本次下载的是1.7.0版本。
运行jar包:java -jar sentinel-dashboard-1.7.0.jar。(nohup)
用户名和密码默认都是sentinel。
第一步:创建项目,导入依赖。
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
第二步:配置application.yml文件。
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos服务注册中心地址
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,假如被占用从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
management:
endpoint:
web:
exposure:
include: '*'
第三步:创建启动类。
package com.itholmes.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @description: TODO
* @date: 2022/8/8 21:26
*/
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}
第四步:创建controller,测试。
package com.itholmes.springcloud.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @description: TODO
* @date: 2022/8/8 21:28
*/
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA(){
return "----testA";
}
@GetMapping("/testB")
public String testB(){
return "----testB";
}
}
第五步:启动Sentinel 和 创建的启动类。
阈值类型:QPS(每秒的请求数量) 、线程数。
QPS:当调用该api的QPS达到阈值的时候,进行限流。
新增流控规则:
一旦访问超过阈值就会直接限流(这里设置的是快速失败):
阈值类型:QPS(每秒的请求数量) 、线程数。
线程数:当调用该api的线程数达到阈值的时候,进行限流。
流控关联就是当关联的资源达到阈值时,就限流自己。
就是B导致A限流了。
Warm Up方式,即预热/冷启动方式。
Warm Up:根据codeFactor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
就是突然请求量增多,通过冷启动,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上线,给冷系统一个预热的时间,避免冷系统被压垮。
Warm Up详细解释:
Sentinel的断路器是没有半开状态的,要么开启,要么断开。
RT(平均响应时间):例如:当1s内持续进入5个请求,对应时刻的平均响应时间(秒级) 均超过阈值(count,以ms为单位),那么在接下的时间窗口(DegradeRule 中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException)。
注意:Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx来配置。
异常比例(DEGRADE_GRADE_EXCEPTION_RATIO):当资源的每秒请求量 >= 5 ,并且每秒异常总数占通过量的比值超过阈值(DegradeRule中的count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0 , 1.0],代表0% ~ 100%。
就是我们访问接口,当资源每秒请求量超过了规定次数,并且异常数/总数 超过了规定的阈值,就会熔断降级。
异常数:就是当资源近1分钟的异常数目超过阈值之后就会进行熔断降级。
热点key就可以理解为针对请求参数key的限流
。
代码如下:
@GetMapping(value = "/testHotKey")
//@SentinelResource的value一般设置为上面路径名一样。添加热点的资源名就是该value值。
@SentinelResource(value = "testHotKey_SentinelResource",blockHandler = "deal_testHotKey")
public String testHotKey(
@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2
){
return "----testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception){
return "---------deal_testHotKey";
}
注意:如果blockHandler没有设置,那么就会将限流的异常页面返回给前端。因此,必须要配置blockHandler!!
6.1目录下面只是普通情况,针对某个参数索引熔断限流。
但是,我们能期望某个参数当它是某个特殊值时,它的限流值和平时不一样。
例如:p1参数,阈值为1;但是当p1的值等于5时,它的阈值可以达到200。
Sentinel 系统自适应限流:是从整体维度对应用入口流量进行控制。
就是个总入口,在总入口处进行限流。
在@SentinelResource属性中:
(上面没有指定blockHandlerClass,写了blockHandler走的就是当前类的方法)
@GetMapping(value = "/rateLimit/customerBlockHandler")
@SentinelResource(
value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, //指定好哪个类(一般是统一异常处理类)
blockHandler = "handlerException2" //类下面的哪个方法。
)
public CommonResult customerBlockHandler(){
return new CommonResult(200,"按客户自定义",new Payment(2020L,"serial003"));
}
统一返回类:
package com.itholmes.springcloud.myhandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.itholmes.springcloud.entities.CommonResult;
/**
* @description: TODO
* @date: 2022/8/11 21:22
*/
public class CustomerBlockHandler {
public static CommonResult handlerException(BlockException exception){
return new CommonResult(200,"按客户自定定义--global");
}
public static CommonResult handlerException2(BlockException exception){
return new CommonResult(200,"按客户自定定义--global2");
}
}
@SentinelResource注解,注解方式不支持private的方法。
其实他的效果就是通过try-catch-finally来解决:
@LoadBalanced注解做负载均衡:
package com.itholmes.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced // @LoadBalanced负载均衡注解很重要,不要忘记配置,,没有它启动不起来项目
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
使用配合nacos如下使用访问:
package com.itholmes.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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;
import javax.annotation.Resource;
/**
* @description: TODO
* @date: 2022/8/12 20:38
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_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(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4){
throw new IllegalArgumentException("IllegalArgument");
}else if (result.getData() == null){
throw new NullPointerException("NullPointException");
}
return result;
}
}
只配置fallback属性,程序只要碰到异常,注意这里指的是java代码里面的异常,并不是sentinel那一套的限流,就会走兜底的方法:
package com.itholmes.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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;
import javax.annotation.Resource;
/**
* @description: TODO
* @date: 2022/8/12 20:38
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/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("IllegalArgument非法参数异常");
}else if (result.getData() == null){
throw new NullPointerException("NullPointException空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id, "null");
return new CommonResult(444,"兜底异常handlerFallback,异常信息" + e.getMessage(),payment);
}
}
blockHandler需要注意的是,只要有达到流控/降级/热点等等设置的条件才会走对应兜底的方法,只要不违背配置规则,还会将异常信息抛给前台的。
package com.itholmes.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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;
import javax.annotation.Resource;
/**
* @description: TODO
* @date: 2022/8/12 20:38
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/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("IllegalArgument非法参数异常");
}else if (result.getData() == null){
throw new NullPointerException("NullPointException空指针异常");
}
return result;
}
public CommonResult blockHandler(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id, "null");
return new CommonResult(444,"触发限流规则blockHandler,异常信息" + e.getMessage(),payment);
}
}
fallback和blockHandler都配置,在不触发sentinel阈值配置规则的情况下的java异常走fallback,一旦触发配置规则,就走blockHandler。
package com.itholmes.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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;
import javax.annotation.Resource;
/**
* @description: TODO
* @date: 2022/8/12 20:38
*/
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(
value = "fallback",
fallback = "handlerFallback",
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("IllegalArgument非法参数异常");
}else if (result.getData() == null){
throw new NullPointerException("NullPointException空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id, "null");
return new CommonResult(444,"兜底异常handlerFallback,异常信息" + e.getMessage(),payment);
}
public CommonResult blockHandler(@PathVariable Long id,Throwable e){
Payment payment = new Payment(id, "null");
return new CommonResult(445,"触发限流规则blockHandler,异常信息" + e.getMessage(),payment);
}
}
异常忽略就是可以指定忽略的异常,这里的忽略指的是sentinel服务熔断降级忽略该异常!还是会走fallback的!
第一步:要激活Sentinel对Feign的支持!!
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
第二步:添加启动注解@EnableFeignClients。
package com.itholmes.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @description: TODO
* @date: 2022/8/12 20:36
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class MainOrder84 {
public static void main(String[] args) {
SpringApplication.run(MainOrder84.class,args);
}
}
业务接口:
package com.itholmes.springcloud.service;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* value指定微服务名称。
*
* 只要访问的接口出现问题,就会走服务降级,走fallback指定对应的接口实现类方法。
* fallback会走对应实现该接口的实现类对应重写的该方法。
*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
(这里踩坑,注意@PathVariable一定要指定value值,在OpenFeign中,不指定就会报错!!!)
业务实现类:
package com.itholmes.springcloud.service;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallbackService implements PaymentService{
/**
* 上面请求没有成功就会触发服务降级,走对应实现类的方法。
*/
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(444,"服务降级返回,--PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
对应测试的controller接口:
package com.itholmes.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.itholmes.springcloud.entities.CommonResult;
import com.itholmes.springcloud.entities.Payment;
import com.itholmes.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
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;
import javax.annotation.Resource;
/**
* @description: TODO
* @date: 2022/8/12 20:38
*/
@RestController
@Slf4j
public class CircleBreakerController {
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id ){
return paymentService.paymentSQL(id);
}
}
这样只要接口访问不成功就会走fallback那一套。
一般一旦重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化。
原理就是将规则持久进Nacos进行保存。
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不上出,针对8401上sentinel上的流控规则持续有效。
第一步:导入依赖 sentinel-datasource-nacos ,该依赖为了解决持久化问题。
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
第二步:配置yml文件,从nacos中获取sentinel的配置。
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: 150.158.199.52:8848 # Nacos服务注册中心地址
sentinel:
transport:
# 配置Sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,假如被占用从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
# 以下就是sentinel从nacos中获取的配置
datasource:
ds1:
nacos:
server-addr: localhost:8848 # 配置nacos服务器地址
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoint:
web:
exposure:
include: '*'
配置如下:
[
{
"resource":"/rateLimit/byUrl",
"limitApp":"default",
"grade":1,
"count":1,
"strategy":0,
"controlBehavior":0,
"clusterMode":false
}
]