SpringCloudAlibaba 微服务讲解(四)Sentinel--服务容错(二)
4.7 Sentinel 规则
4.7.1 流控规则
流量控制,其原理是监控应用流量的QPS(每秒查询率)或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的榴莲高峰冲垮,从而保障应用的高可用性。
点击簇点链路,我们就可以看到访问过的接口地址,然后点击对应的流控按钮,进入流控规则配置页面。新增流控规则界面如下

资源名:唯一名称,默认是请求路径,可以自定义
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制
阈值类型单机阈值:
- QPS:当调用该接口的QPS达到阈值时,进行限流
 - 线程数:当调用该接口的线程数达到阈值的时候,进行限流
 
是否集群L:暂时不需要集群
我们接下来以QPS为例来研究限流规则的配置
4.7.1.1 简单配置
我们先做一个简单配置:设置阈值类型为QPS,单机阈值为3。即每秒请求量大于3的时候开始限流,接下来,在流控规则页面就可以看到这个配置。

4.7.1.2 配置流控模式
点击上面设置流控规则的编辑按钮,然后在编辑页面点击高级选项,会看到有流控模式一栏

Sentinel共有三种流控模式,分别是:
- 直接(默认):接口达到限流条件时,开启限流
 - 关联:当关联的资源达到限流条件时,开启限流
 - 链路:当从某个接口过来的资源达到限流条件时,开启限流
 
下面分别演示三种模式:
直接流控模式
直接留空模式是最简单的模式,当指定掉接口达到限流条件时开启。上面案例使用的就是直接留空模式
关联流控模式
关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。
第一步:配置限流规则,将流控模式设置为关联,关联资源设置为/order/message2。

第二步:通过postman软件向/order/message2连续发送请求,注意QPS一定大于3


第三步:访问/order/message1,会发现已经限流了

链路流控模式
链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对来源配置项,区别在于:针对来源是针对上级服务,而链路流控是针对上级接口,也就是说他的粒度更细
第一步:编写一个service,在里面添加一个方法message
@Service
public class OrderServiceImpl3{
@SentinelResource("message")
public void message(){
System.out.println("message");
}
}
第二步:在Controller中声明两个方法,分别调用service中的方法message
@RestController
@Slf4j
public class OrderController3{
@Autowired
private OrderServiceImpl3 orderServiceImpl3; @requestMapping("/order/message1")
public String message1(){
return "message1";
} @RequestMapping("/order/message2")
public String message2(){
orderServiceImpl3.message();
return "message2";
}
}
第三步:禁止收敛URL的入口context
从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效
1.7.0版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter引入了
WEB_CONTEXT_UNIFY参数,用于控制是否收敛context。将其配置为false即可根据不同的URL进行链路限流。SCA 2.1.1.RELEASE之后的版本,可以通过配置
spirng.cloud.sentinel.web-context-unify=false即可关闭收敛。我们当前使用的版本是SpringCloud Alibaba 2.1.0.RELEASE,无法实现链路限流目前官方还未发布SCA 2.1.2.RELEASE,所以我们只能用2.1.1.RELEASE,需要写代码的形式实现
暂时将SpringCloud Alibaba 的版本调整为2.1.1.RELEASE
<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>
配置文件中关闭sentinel的CommonFilter实例化
spring:
cloud:
sentinel:
filter:
enable: false
添加一个配置类,自己构建CommonFilter 实例
@Configuration
public class FilterContextConfig{
@Bean
public FilterRegistrationBean sentinelFilterRegistration(){
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new COmmonFilter());
registration.addUrlPatterner("/*");
// 入口资源关闭整合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration
}
}
第四步:控台配置限流规则

第五步:分别通过
/order/message1和/order/message2访问,发现2没问题,1 就被限流了4.7.1.3 配置流控效果
- 快速失败(默认):直接失败,抛出异常,不做任何额外的处理,是最简单的效果
 - Warm Up:它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,知道最大阈值,适用于将突然增大的流量转换为换不增长的场景。
 - 排队等待:让请求以均匀的速度铜鼓哦,单机阈值为每秒通过数量,其余的排队等待。它还会让设置一个超时时间,当请求超过超时时间还未处理,则会被丢弃
 
4.7.2 降级规则
降级规则就是设置当满足什么条件的时候,对服务进行降级。Sentinel提供了三个衡量条件:
平均响应时间:当资源的平均响应时间超过阈值(以ms为单位)之后, 资源进入准降级状态。如果接下来1s内持续进入5个请求,它们的RT都将持续超过这个阈值,那么在接下来的时间窗口(以s为单位)之内,就会对这个方法进行服务降级。

注意Sentinel默认统计的RT上限是4900ms,超过此阈值都会算作4900ms,若需要变更此上限可以通过启动配置项
-Dcsp.sentinel.statistic.max.rt=xxx来配置异常比例:当资源的每秒异常总数占通过量的壁纸超过阈值之后,资源进入降级状态,即在接下来的时间窗口(以s为单位)之内,对这个方法的调用都会自动地返回,异常比率的阈值范围是【0.0,0.1】
第一步:首先模拟一个异常
int i=0;
@RequestMapping("/order/message2")
public String message2(){
i++;
//异常比例为0.333
if(i%3==0){
throw new RuntimeException();
}
return "message2";
}
第二步:设置异常比例为0.25

异常数:当资源近1分钟的异常数目超过阈值之后会进行服务降级,注意由于统计时间窗口是分钟级别的,若时间窗口小于60s,则结束熔断状态后仍可能在进入熔断状态。

问题:流控规则和降级规则返回的异常页面是一样的,我们怎么来区分到底是什么原因导致的呢?
4.7.3 热点规则
热点参数流控规则是一种耕细粒度的流控规则,它允许将规则具体到参数上。
热点规则的简单使用
第一步:编写代码
@RequestMapping("order/message3")
//注意这里必须使用这个注解标识,热点规则不生效
@SentinelResource("message3")
public String message3(String name, Integer age){
  return name+age;
}
第二步:配置热点规则

第三步:分别用两个参数访问,会发现只对一个参数限流了

热点规则增强使用
参数例外项允许对一个参数的具体值进行流控
编辑刚才定义的规则,增加参数例外项

4.7.4 授权规则
很多时候,我们需要根据调用来源来判断该次请求是否允许方放行,这时候可以使用Sentinel的来源访问控制的功能,来源访问控制根据资源的请求来源(origin)限制资源是否通过:
- 若配置白名单,则只有请求来源位于白名单内时才通过;
 - 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过;
 

上面的资源名和授权类型不难理解,但是流控应用怎么填写呢?
其实这个位置要填写的是来源标识,Sentinel提供了ReuestOriginParser 接口来处理来源。
只要Sentinel保护的接口资源被访问,Sentinel就会调用RequestOriginParser的实现类去解析访问来源。
第一步:自定义来源处理规则
@Component
public class RequestOriginParserDefinition implements RequestOriginParser{
  @Override
  public String parserOrigin(HttpServletRequest request){
    String serviceName =  request.getParameter("serviceName");
    return serviceName;
  }
}
第二步:授权规则配置
这个配置的意思是只有serviceName=pc 不能访问(黑名单)

第三步:访问:http://localhost:8091/order/message1?serviceName=pc 观察结果
4.6.5 系统规则
系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体Load、RT、入口QPS、CPU使用率和线程数五个维度,而不是资源维度的,并且仅对入口流量(进入应用的流量)生效。
- Load(仅对Linux/Unix-like及其生效):当系统load1超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的maxQps* minRt计算得出。设定参考值一版是CPU cores)*2.5。
 - RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒。
 - 线程数:当单台机器上所有有入口流量的并发线程数达到阈值即触发系统保护。
 - 入口QPS:当单台机器上所有入口流量QPS达到阈值即触发系统保护。
 - CPU使用率:当单台机器上所有入口流量的CPU使用率达到阈值即触发系统保护
 
扩展:自定义异常返回
// 异常处理页面
@Companent
public class ExceptionHandlerPage implements UrlBlockHandler{
  // BlockException 异常接口,包含Sentinel的五个异常
  // 用于定义资源,并提供可选的异常处理和fallback配置项。其主要参数如下:
  // FlowException 限流异常
  // DegradeException 降级异常
  // ParamFlowException 参数限流异常
  // AuthorityException 授权异常
  // SystemBlockException 系统负载异常
  @SentinelResource
  @Override
  public void blocked(HttpServletRequest request ,HttpServletResponse response,BlockException e) throws IOEception{
    respnse.setContentType("application/json;charset=utf-8");
    ResponseData data = null;
    if(e instanceof FlowException){
      data = new ResponseData(-1,"接口被限流了。。。。");
    }else if(e instanceof DegradeException){
      data = new ResponseData(-2,"接口被降级了。。。");
    }
    response.gerWriter().wirte(JSON.toJSONString(data));
  }
}
@Data
@AllArgsConstructor
class ResponseData{
  private int code;
  private String message;
}
4.8 @SentinelResource 的使用
在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能通过@SentinelResource 来指定出现异常时的处理策略
@SentinelResource 用于定义资源,并提供可选择的异常处理和fallback配置项。其主要参数如下:

定义限流和降级后的处理方法
方式一:直接将限流和降级方法定义在方法中
@Service
@Slf4j
public class OrderServiceImpl3{
  int i=0;
  @SentinelResource(
  	value="message",
    blockHandler = "blockHandler",//指定发生BlockException 时进入的方法
    fallback = "fallback"//指定发生Throwable时进入的方法
  )
  public String message(){
    i++;
    if(i % 3==0){
      throw new RuntimeException();
    }
    return "message";
  }
  // BlockException 时进入的方法
  public String blockHandler(BlockException ex){
    log.error("{}",ex);
    return "接口被限流和或降级了。。。。";
  }
  // Throwable 时进入的方法
  public String fallback(Throwable throwable){
    log.error("{}",throwable);
    return "接口发生异常了。。。。";
  }
}
方式二:将限流和降级方法外置到单独类中
@Service
@Slf4j
public class OrderServiceImpl3{
  int i=0;
  @SentinelResource(
  	value = "message",
    blockHandlerClass = OrderServiceImpl3BlockHandlerClass.class,
    blockHandler = "blockHandler",
    fallbackClass = OrderServiceImpl3FallbackClass.class,
    fallback = "fallback"
  )
  public String message(){
    i++;
    if(i % 3 ==0){
      throw new RuntimeException();
    }
    return "message4";
  }
}
@Slf4j
public class OrderServiceImpl3BlockHandlerClass{
  // 注意这里必须使用static 修饰方法
  public static String blockHandler(BlockException ex){
    log.error("{}",ex);
    return "接口被限流或者降级了。。。。";
  }
}
@Slf4j
public class OrderServiceImpl3FallbackClass {
   // 注意这里必须使用static 修饰方法
  public static String fallback(Throwable throwable){
    log.error("{}",throwable);
    return "接口发生异常了。。。。";
  }
}
4.9 sentinel 规则持久化
通过前面的讲解,我们已经知道,可以通过Dashboard来为每个Sentinel客户端设置各种各样的规则,但是这里有一个问题,就是这些规则默认是存放在内存中的,非常不稳定,所以需要持久化。
本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:

首先Sentinel控制台通过API将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的规则保存到本地的文件中。
编写处理类
public class FilePersistence implement InitFunc{
@Value("spring.application:name")
private String applicationName;
@Override
public void init() throws Exception{
String ruleDir = system.getProperty("user.home")+"/sentinel-rules/"+applicationName;
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir +"/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(reuleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath); // 流控规则
ReadableDataSource<String,List<FlowRule>> flowRuleRDS = new FileRefresableDataSource<>(
flowRulePath,flowRuleListParser);
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRulesWDS = new FileWritableDataSource<>(flowRulePath,this::encodeJson);
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS); // 降级规则
ReadableDataSource<String,List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
degradeRulePath,degradeRuleListParser);
DegradeRuleManager.register2Poperty(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(degradeRulePath,this::encodeJson); // 系统规则
ReadableDataSource<String,List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<> (systemRulePath,systemRuleListParser);
SystemRuleManager.register2Property(systemRuleRds.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource(systemnRulePath,this::encodeJson);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS); // 授权规则
ReadableDataSource<String,List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(authorityRulePath,authorityRuleListParser);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(authorityRulePath,this::encodeJson);
WiritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS); // 热点参数规则
ReadbaleDataSource<String,List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(paramFlowRulePath,paramFlowRuleListParaser);
ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
WritableDataSouece<List<ParamFlowRule>> paramFlowRuleWDS = new FileWirtableDataSouece<>(paramFlowRulePath,this::encodeJson);
ModifyParamFlowRulesCommandHandler.setWritableDataSouce(paramFlowRuleWDS);
} private Converter<String,List<FlowRule>> flowRuleListParser = source -> JSON.parsObject(source,new TypeReference<List<FlowRule>>(){}); private Converter<String,List<SystemRule>> flowRuleListParser = source -> JSON.parsObject(source,new TypeReference<List<SystemRule>>(){}); private Converter<String,List<AuthorityRule>> flowRuleListParser = source -> JSON.parsObject(source,new TypeReference<List<AuthorityRule>>(){}); private Converter<String,List<ParamFlowRule>> flowRuleListParser = source -> JSON.parsObject(source,new TypeReference<List<ParamFlowRule>>(){}); private void mkdirIfNotExits(String filePath) throws IOException{
File file = new File(filePath);
if(!file.exists()){
file.mkdirs();
}
} private void createFileIfNotExits(String filePath) throws IOException{
File file = new File(filePath);
if(!file.exists()){
file.createNewFile();
}
} private void createFileIfNotExits(String filePath) throws IOException{
File file = new File(fielPath);
if(!file.exists()){
file.createNewFile();
}
}
private <T> String encodeJson(T t){
return JSON.toJSONString(t);
} }
添加配置
在Resources下面创建配置目录,META-INF/services,然后添加文件
com.alibaba.csp.sentinel.init.InitFunc在文件中添加配置类的全路径
com.itheima.config.FilePersistence
SpringCloudAlibaba 微服务讲解(四)Sentinel--服务容错(二)的更多相关文章
- 【转载】Redis Sentinel服务配置
		
转载地址:http://blog.csdn.net/vtopqx/article/details/49247285 redis官网文档:http://www.redis.cn/topics/senti ...
 - SpringCloudAlibaba 微服务讲解(四)Sentinel--服务容错(一)
		
4.1 高并发带来的问题 在微服务中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现网 ...
 - SpringCloudAlibaba 微服务讲解(二)微服务环境搭建
		
微服务环境搭建 我们这次是使用的电商项目的商品.订单.用户为案例进行讲解 2.1 案例准备 2.1.1 技术选型 maven :3.3.9 数据库:mysql 持久层:SpringData JPA S ...
 - SpringCloudAlibaba 微服务讲解(一)微服务介绍
		
微服务介绍 1.1 系统架构的演变 随若互联网的发展,网站应用的规模也在不断的扩大,逬而导致系统架构也在不断的进行变化.从互联 网早起到现在,系统架构大体经历了下面几个过程:单体应用架构一蟻直应用架构 ...
 - Spring Boot + Spring Cloud 构建微服务系统(四):容错机制和熔断(Hystrix)
		
雪崩效应 在微服务架构中,由于服务众多,通常会涉及多个服务层级的调用,而一旦基础服务发生故障,很可能会导致级联故障,进而造成整个系统不可用,这种现象被称为服务雪崩效应.服务雪崩效应是一种因“服务提供者 ...
 - SpringCloudAlibaba 微服务讲解(三)Nacos Discovery-服务治理
		
3.1 服务治理 先来思考一个问题,通过上一章的操作,我们已经实现微服务之间的调用,但是我们把服务提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题: 一旦服务提供者地址变化,就需 ...
 - 学习一下 SpringCloud (四)-- 服务降级、熔断 Hystrix、Sentinel
		
(1) 相关博文地址: 学习一下 SpringCloud (一)-- 从单体架构到微服务架构.代码拆分(maven 聚合): https://www.cnblogs.com/l-y-h/p/14105 ...
 - 第二个视频作品《[SpringCloudAlibaba]微服务之注册中心nacos》上线了
		
1.场景描述 第二个视频作品出炉了,<[SpringCloudAlibaba]微服务之注册中心nacos>上线了,有需要的朋友可以直接点击链接观看.(如需购买,请通过本文链接购买) 2. ...
 - SpringCloudAlibaba—微服务概念及SpringCloudAlibaba介绍
		
目录 1.1 系统架构演变 1.1.1 单体应用架构 1.1.2垂直应用架构 1.1.3 分布式架构 1.1.4 SOA架构 1.1.5 微服务架构 1.2 微服务架构介绍 1.2.1 微服务架构的常 ...
 
随机推荐
- C# 开始支持动态化编程
			
在.NET 4.0的运行时进行动态编程时,我们引入了一个新功能:动态语言运行时.可以这样理解,CLR的目的是为静态类型的编程语言提供一个统一的框架或编程模型,而DLR便是在.NET平台上为动态语言提供 ...
 - C#基础之Foreach
			
下面是Foreach的介绍. 如何让一个类可以用Foreach来遍历呢. 结论:让这个类实现IEnumerable接口. 这个类有一个public的GetEnumerator的实例方法,并且返回类型中 ...
 - Linux常用文件权限命令详解
			
pwd pwd命令用于获取当前工作目录的绝对路径. 使用示例: pwd 效果如下图: cd cd命令用于切换工作目录. 使用示例: cd 万猫学社/ 效果如下图: 其中在路径表示时, 一个半角句号(. ...
 - Springboot循环依赖实践纪实
			
测试的Springboot版本: 2.6.4,禁止了循环依赖,但是可以通过application.yml开启(哈哈) @Lazy注解解决循环依赖 情况一:只有简单属性关系的循环依赖 涉及的Bean: ...
 - Git更新本地仓库及冲突"Commit your changes or stash them before you can merge"解决
			
Git中从远程的分支获取最新的版本到本地有这样2个命令: 1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge git fetch origin mastergit log ...
 - linux 文件查找 find
			
find 是实时查找工具,通过遍历指定路径完成文件查找 特点: 查找速度略慢 精确查找 实时查找 查找条件丰富 1.对每个目录先处理目录内的文件,再处理目录本身 find /data/test -de ...
 - Actor model 的理解与 protoactor-go 的分析
			
Overview Definition From wikipedia The actor model in computer science is a mathematical model of co ...
 - thinkphp 登录(未设置cookie+session)
			
<?php namespace app\Admin\controller; use think\Controller; use think\Loader; use think\Request; ...
 - Linux安装JDK8环境
			
1.下载JDK包 点击同意下载后,会让你注册oracel账号,登录了才能下载 2.上传到linux服务器,然后解压 解压命令(注意jdk的版本名称不一定相同): tar -zxvf jdk-8u181 ...
 - pandas常用操作详解——pandas的去重操作df.duplicated()与df.drop_duplicates()
			
df.duplicated() 参数详解: subset:检测重复的数据范围.默认为数据集的所有列,可指定特定数据列: keep: 标记哪个重复数据,默认为'first'.1.'first':标记重复 ...