前言

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。

由于我们熟悉的Hystrix豪猪哥已经停止更新,Spring Cloud Sentinel就成为很多开发人员选型的首选组件,用来替代Hystrix提供的服务降级,服务熔断,服务限流等功能。

官方文档

https://sentinelguard.io/zh-cn/docs/introduction.html

基本概念的介绍

请参考 https://blog.csdn.net/qq_43684005/article/details/118581690

Sentinel的特点

应用场景很丰富:承接了阿里巴巴双十一大促流量的核心场景。

完备的实时监控:可以在控制台中看到接入应用的单台机器秒级数据

广泛的开源生态:只需要引入配置就能快速的使用Sentinel

完善的SPI拓展点:提供简单易用、完善的 SPI 扩展接口

和Hystrix对比

Sentinel基本功能——详细参考官方文档

流量控制

用于调整网络包发送的数据的速度。因为在网络中任意时间发送来的请求往往是随机和不可控的,而系统的处理能力也是有限的。流量控制就是根据系统的处理能力进行流量的控制。

 流量控制有以下几个角度

资源的调用关系,例如资源的调用链路,资源和资源之间的关系;

运行指标,例如 QPS、线程池、系统负载等;

控制的效果,例如直接限流、冷启动、排队等。

熔断降级

https://blog.csdn.net/qq_43684005/article/details/118581690

Sentinel 和 Hystrix 的原则是一致的: 当调用链路中某个资源出现不稳定,例如,表现为 timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,避免影响到其它的资源,最终产生雪崩的效果。

熔断降级的设计理念

在熔断降级的设计理念上,Sentienl和Hystrix采用了不同的方法。

Hystrix:采用了线程池来对资源进行隔离,这样做的好处是能做到彻底的隔离,但是会增加线程切换的成本,而且还需要给哥哥资源做线程池大小的分配。

Sentinel:

对并发线程数进行限制:和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。

通过响应时间对资源进行降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。

系统负载保护

Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。

针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

Sentinel下载安装运行

下载:

https://github.com/alibaba/Sentinel/releases

下载到本地sentinel-dashboard-1.7.0.jar

 运行命令:

前提

Java8环境

8080端口不能被占用

java -jar sentinel-dashboard-1.7.0.jar

访问Sentinel管理界面

localhost:8080,账号密码都是sentinel

Sentinel初始化监控

(1)新建工程——cloudalibaba-sentinel-service8401

(2)改pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>cloud2020</artifactId><groupId>com.atguigu.springcloud</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-sentinel-service8401</artifactId><dependencies><dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>${project.version}</version></dependency><!--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></project>

YML

server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard地址port: 8719management:endpoints:web:exposure:include: '*'feign:sentinel:enabled: true # 激活Sentinel对Feign的支持

主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {public static void main(String[] args) {SpringApplication.run(MainApp8401.class, args);}
}

业务类

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.TimeUnit;@RestController
@Slf4j
public class FlowLimitController {@GetMapping("/testA")public String testA(){return "------testA";}@GetMapping("/testB")public String testB(){log.info(Thread.currentThread().getName()+"\t"+"...testB");return "------testB";}
}

测试

启动Nacos8848,Sentinel8080,启动微服务8401

查看控制台,然而并没有东西。说明Sentinel是懒加载:只有当访问的时候信息才会出现在控制台。

访问

http://localhost:8401/testA

http://localhost:8401/testB

Sentinel流控规则简介

 解释说明:

资源名:唯一名称,默认请求路径。
针对来源:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)。
阈值类型/单机阈值:
        QPS(每秒钟的请求数量)︰当调用该API的QPS达到阈值的时候,进行限流。
        线程数:当调用该API的线程数达到阈值的时候,进行限流。
是否集群:不需要集群。
流控模式
        直接:API达到限流条件时,直接限流。
        关联:当关联的资源达到阈值时,就限流自己。
        链路:只记录指定链路上的流量(指定资源从入口资源进来的流量)
流控效果
        快速失败:直接失败,抛异常。
        Warm up:根据Code Factor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
        排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。

QPS直接失败

配置:直接 -> 快速失败(系统默认)

配置说明

表示1秒钟内查询1次就是OK,若超过次数1,就直接->快速失败,报默认错误

测试:快速多次点击访问http://localhost:8401/testA

结果:返回页面 Blocked by Sentinel (flow limiting)

源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

思考:直接调用默认报错信息,技术方面OK,但是,是否应该有我们自己的后续处理?类似有个fallback的兜底方法?我们后续会讲。

线程数直接失败

线程数:当调用该API的线程数达到阈值的时候,进行限流。

关联

关联:当和当前的A关联的资源B达到阈值以后,就会限流A自己。(B惹事,A负责)

设置:/testA

当关联资源/testB的QPS阀值超过1时,就限流/testA的Rest访问地址,当关联资源到阈值后限制配置好的资源名

Postman模拟并发密集访问testB,Postman运行后,点击访问http://localhost:8401/testA,发现testA挂了。

Warm Up预热

Warm Up

Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细官方文档可以参考 流量控制 - Warm Up 文档,具体的例子可以参见 WarmUpFlowDemo。

默认coldFactor为3,即请求QPS 从 threshold / 3开始,经预热时长逐渐升至设定的QPS阈值。

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:

案例,阀值为10+预热时长设置5秒。

系统初始化的阀值为10/ 3约等于3,即阀值刚开始为3;然后过了5秒后阀值才慢慢升高恢复到10

排队等待

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。

Sentinel降级规则简介

RT(平均响应时间,秒级):平均响应时间 超出阈值 且 在时间窗口内通过的请求>=5,两个条件同时满足后触发降级。窗口期过后关闭断路器。
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)。
异常比列(秒级):QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级 。
异常数(分钟级):异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级

注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常。

降级-RT

什么是RT?

平均响应时间(DEGRADE_GRADE_RT):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阈值( count,以ms为单位),那么在接下的时间窗口(DegradeRule中的timeWindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出DegradeException )。注意Sentinel 默认统计的RT上限是4900 ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置。

Sentinel 1.7.0才有平均响应时间DEGRADE_GRADE_RT),Sentinel 1.8.0的没有这项,取而代之的是慢调用比例 (SLOW_REQUEST_RATIO)。

 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

1.7版本的只需要设置降级规则就行,如下

异常比例

异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

测试代码:

@RestController
@Slf4j
public class FlowLimitController {...@GetMapping("/testD")public String testD() {log.info("testD 异常比例");int age = 10/0;return "------testD";}
}

异常数

异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

@RestController
@Slf4j
public class FlowLimitController{...@GetMapping("/testE")public String testE(){log.info("testE 测试异常数");int age = 10/0;return "------testE 测试异常数";}
}

 Sentinel热点规则

什么是热点

热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。

热点规则

我们之前提到过,sentinel的兜底方法怎么使用?接下来我们也会演示一些如何使用。主要的是一个注解:@SentinelResource

@RestController
@Slf4j
public class FlowLimitController
{...@GetMapping("/testHotKey")@SentinelResource(value = "testHotKey",blockHandler/*兜底方法*/ = "deal_testHotKey")public String testHotKey(@RequestParam(value = "p1",required = false) String p1,@RequestParam(value = "p2",required = false) String p2) {//int age = 10/0;return "------testHotKey";}/*兜底方法*/public String deal_testHotKey (String p1, String p2, BlockException exception) {return "------deal_testHotKey,o(╥﹏╥)o";  //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)}}

 测试:

@SentinelResource(value = "testHotKey", blockHandler = "dealHandler_testHotKey")
方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
异常用了我们自己定义的兜底方法

异常:因为对p1进行了热点限制
http://localhost:8401/testHotKey?p1=abc
http://localhost:8401/testHotKey?p1=abc&p2=33
正确访问
http://localhost:8401/testHotKey?p2=abc

参数例外项

什么叫参数例外项?

对于普通的热点限制,超过1秒钟一个后,达到阈值1后马上被限流。但是我先现在希望参数p1是一个特殊的值的时候,不遵循上述的热点限制规则,而是一个特殊的规则。参数例外项,可以针对指定的参数值单独设置限流阈值,不受前面 count 阈值的限制。仅支持基本类型和字符串类型

特例 - 假如当p1的值等于5时,它的阈值可以达到200

测试:

right - http://localhost:8401/testHotKey?p1=5

error - http://localhost:8401/testHotKey?p1=3

当p1等于5的时候,阈值变为200

当p1不等于5的时候,阈值就是平常的1

其他

我们看下面一段代码

@RestController
@Slf4j
public class FlowLimitController
{...@GetMapping("/testHotKey")@SentinelResource(value = "testHotKey",blockHandler/*兜底方法*/ = "deal_testHotKey")public String testHotKey(@RequestParam(value = "p1",required = false) String p1,@RequestParam(value = "p2",required = false) String p2) {int age = 10/0;//<----------------------------会抛异常的地方return "------testHotKey";}/*兜底方法*/public String deal_testHotKey (String p1, String p2, BlockException exception) {return "------deal_testHotKey,o(╥﹏╥)o";  //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)}}

将会抛出Spring Boot 2的默认异常页面,而不是兜底方法。

@SentinelResource - 处理的是sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;

RuntimeException int age = 10/0,这个是java运行时报出的运行时异常RunTimeException,@SentinelResource不管

总结 - @SentinelResource主管配置出错,运行出错该走异常走异常

Sentinel系统自适应保护

Sentinel 系统自适应保护从整体维度对应用入口流量进行控制,结合应用的 Load、总体平均 RT、入口 QPS 和线程数等几个维度的监控指标,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 和线程数四个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的阈值类型:

  • Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
  • RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

SentinelResource配置简介

按资源名称限流 + 后续处理

Module - cloudalibaba-sentinel-service8401添加新的Controller

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.alibaba.myhandler.CustomerBlockHandler;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RateLimitController {@GetMapping("/byResource")@SentinelResource(value = "byResource",blockHandler = "handleException")public CommonResult byResource() {return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));}public CommonResult handleException(BlockException exception) {return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");}
}

控制台配置

测试

1秒钟点击1下,OK

超过上述,疯狂点击,返回了自己定义的限流处理信息,限流发生

返回

{"code":444, "message":"com.alibaba.csp.sentinel.slots.block.flow.FlowException\t 服务不可用", "data":null}

但是此时关闭问服务8401再访问Sentinel控制台,流控规则消失了。并且现在的兜底方法面临着很多的问题。比方说:

系统默认的,没有体现我们自己的业务要求。

依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。

每个业务方法都添加—个兜底的,那代码膨胀加剧。

全局统—的处理方法没有体现。

客户自定义限流处理逻辑

自定义限流处理类 - 创建CustomerBlockHandler类用于自定义限流处理逻辑

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;public class CustomerBlockHandler {public static CommonResult handlerException(BlockException exception) {return new CommonResult(4444,"按客戶自定义,global handlerException----1");}public static CommonResult handlerException2(BlockException exception) {return new CommonResult(4444,"按客戶自定义,global handlerException----2");}
}

RateLimitController

@RestController
public class RateLimitController {...@GetMapping("/rateLimit/customerBlockHandler")@SentinelResource(value = "customerBlockHandler",blockHandlerClass = CustomerBlockHandler.class,//<-------- 自定义限流处理类blockHandler = "handlerException2")//<-----------public CommonResult customerBlockHandler(){return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));}
}

Sentinel控制台配置

启动微服务后先调用一次 - http://localhost:8401/rateLimit/customerBlockHandler。然后,多次快速刷新http://localhost:8401/rateLimit/customerBlockHandler。刷新后,我们自定义兜底方法的字符串信息就返回到前端。

SentinelResource 注解

@SentinelResource 注解

注意:注解方式埋点不支持 private 方法。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:

value:资源名称,必需项(不能为空)
entryType:entry 类型,可选项(默认为 EntryType.OUT)
blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
fallback /fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
返回值类型必须与原函数返回值类型一致;
方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

Sentinel服务熔断Ribbon环境

sentinel整合ribbon+openFeign+fallback。

启动nacos和sentinel

创建服务提供者

新建cloudalibaba-provider-payment9003/9004

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>cloud2020</artifactId><groupId>com.atguigu.springcloud</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-provider-payment9003</artifactId><dependencies><!--SpringCloud ailibaba nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>${project.version}</version></dependency><!-- SpringBoot整合Web组件 --><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>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>
</project>

YML

server:port: 9003spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址management:endpoints:web:exposure:include: '*'

主启动

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {public static void main(String[] args) {SpringApplication.run(PaymentMain9003.class, args);}
}

业务类

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;@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,"28a8c1e3bc2742d8848569891fb42181"));hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));}@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;}}

服务消费者84

新建cloudalibaba-consumer-nacos-order84

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>cloud2020</artifactId><groupId>com.atguigu.springcloud</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloudalibaba-consumer-nacos-order84</artifactId><dependencies><!--SpringCloud openfeign --><!--<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>--><!--SpringCloud ailibaba nacos --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--SpringCloud ailibaba sentinel --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity --><dependency><groupId>com.atguigu.springcloud</groupId><artifactId>cloud-api-commons</artifactId><version>${project.version}</version></dependency><!-- SpringBoot整合Web组件 --><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>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>
</project>

YML

server:port: 84spring:application:name: nacos-order-consumercloud:nacos:discovery:server-addr: localhost:8848sentinel:transport:#配置Sentinel dashboard地址dashboard: localhost:8080#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口port: 8719#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:nacos-user-service: http://nacos-payment-provider# 激活Sentinel对Feign的支持
feign:sentinel:enabled: false

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84 {public static void main(String[] args) {SpringApplication.run(OrderNacosMain84.class, args);}
}

业务类

ApplicationContextConfig

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@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();}
}

CircleBreakerController

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.alibaba.service.PaymentService;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
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;@RestController
@Slf4j
public class CircleBreakerController {public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate 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 ("IllegalArgumentException,非法参数异常....");}else if (result.getData() == null) {throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");}return result;}}

测试地址 - http://localhost:84/consumer/fallback/1

Sentinel服务熔断无配置

没有任何配置 - 给用户error页面,不友好

@RestController
@Slf4j
public class CircleBreakerController {public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate 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 ("IllegalArgumentException,非法参数异常....");}else if (result.getData() == null) {throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");}return result;}}

Sentinel服务熔断只配置fallback

fallback只负责业务异常

@RestController
@Slf4j
public class CircleBreakerController {public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//@SentinelResource(value = "fallback")//没有配置@SentinelResource(value = "fallback", fallback = "handlerFallback") //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;}//本例是fallbackpublic CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}}

测试地址 - http://localhost:84/consumer/fallback/4

返回结果:{"code":444,"message":"兜底异常nandlerFal1back, exception内容illegalkrgumentEBxceptiorn,非法参数异常……","data":{"id":4,"seria:"null"}}

Sentinel服务熔断只配置blockHandler

blockHandler只负责sentinel控制台配置违规

@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//@SentinelResource(value = "fallback") //没有配置//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规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;}//本例是fallback
/*    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}*///本例是blockHandlerpublic CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);}
}

测试地址 - http://localhost:84/consumer/fallback/4

Sentinel服务熔断fallback和blockHandler都配置

@RestController
@Slf4j
public class CircleBreakerController
{public static final String SERVICE_URL = "http://nacos-payment-provider";@Resourceprivate RestTemplate restTemplate;@RequestMapping("/consumer/fallback/{id}")//@SentinelResource(value = "fallback") //没有配置//@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只负责业务异常//@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规@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 ("IllegalArgumentException,非法参数异常....");}else if (result.getData() == null) {throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");}return result;}//本例是fallbackpublic CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {Payment payment = new Payment(id,"null");return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);}//本例是blockHandlerpublic CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {Payment payment = new Payment(id,"null");return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);}
}

Sentinel服务熔断exceptionsToIgnore

exceptionsToIgnore,忽略指定异常,即这些异常不用兜底方法处理。

@RestController
@Slf4j
public class CircleBreakerController    ...@RequestMapping("/consumer/fallback/{id}")@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",exceptionsToIgnore = {IllegalArgumentException.class})//<-------------public CommonResult<Payment> fallback(@PathVariable Long id){CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);if (id == 4) {//exceptionsToIgnore属性有IllegalArgumentException.class,//所以IllegalArgumentException不会跳入指定的兜底程序。throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");}else if (result.getData() == null) {throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");}return result;}...
}

Sentinel服务熔断OpenFeign

修改84模块

84消费者调用提供者9003

Feign组件一般是消费侧

POM

<!--SpringCloud openfeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

YML

# 激活Sentinel对Feign的支持
feign:sentinel:enabled: true

主启动

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients//<------------------------
public class OrderNacosMain84 {public static void main(String[] args) {SpringApplication.run(OrderNacosMain84.class, args);}
}

业务类

带@Feignclient注解的业务接口,fallback = PaymentFallbackService.class

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{@GetMapping(value = "/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.stereotype.Component;@Component
public class PaymentFallbackService implements PaymentService {@Overridepublic CommonResult<Payment> paymentSQL(Long id){return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));}
}

controller

@RestController
@Slf4j
public class CircleBreakerController {...//==================OpenFeign@Resourceprivate PaymentService paymentService;@GetMapping(value = "/consumer/paymentSQL/{id}")public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){return paymentService.paymentSQL(id);}
}

测试 - http://localhost:84/consumer/paymentSQL/1

测试84调用9003,此时故意关闭9003微服务提供者,84消费侧自动降级,不会被耗死。

Sentinel持久化规则

一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化。

怎么玩

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效。就是把这些规则持久化到配置中。

步骤

修改cloudalibaba-sentinel-service8401

POM

<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

yml

server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848 #Nacos服务注册中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard地址port: 8719datasource: #<---------------------------关注点,添加Nacos数据源配置ds1:nacos:server-addr: localhost:8848dataId: cloudalibaba-sentinel-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flowmanagement:endpoints:web:exposure:include: '*'feign:sentinel:enabled: true # 激活Sentinel对Feign的支持

Nacos添加规则配置

配置内容解析

[{"resource": "/rateLimit/byUrl","IimitApp": "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:是否集群。

快速访问测试接口 - http://localhost:8401/rateLimit/byUrl - 页面返回Blocked by Sentinel (flow limiting)

Spring Cloud Alibaba (三)Sentinel 简单介绍和使用相关推荐

  1. Spring Cloud Alibaba:Sentinel实现熔断与限流

    摘要 Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案,Sentinel 作为其核心组件之一,具有熔断与限流等一系列服务保护功能,本文将对其用法进行详细介绍. Senti ...

  2. Spring Cloud Alibaba:Sentinel 流控规则

    文章目录 1. 前言 2. 阈值类型 2.1 QPS 2.2 线程数 3. 流控模式 3.1 直接 3.2 关联 3.3 链路 4. 流控效果 4.1 快速失败 4.2 Warm Up 4.3 排队等 ...

  3. spring cloud alibaba系列sentinel规则持久化

    "秋来相顾尚飘蓬,未就丹砂愧葛洪. 痛饮狂歌空度日,飞扬跋扈为谁雄" 总结 我上一篇:spring cloud alibaba sentinel 介绍的sentinel可以定义很多 ...

  4. SpringCloud - Spring Cloud Alibaba 之 Sentinel 流量控制、熔断降级(七)

    一.Sentinel 随着微服务的流行,服务和服务之间的稳定性变得越来越重要. Sentinel 以流量为切入点,从流量控制.流量路由.熔断降级.系统自适应过载保护.热点流量防护等多个维度保护服务的稳 ...

  5. spring cloud alibaba整合sentinel

    1. 添加依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-c ...

  6. Spring Cloud Alibaba:Sentinel 热点参数限流

    文章目录 1. 前言 2. 热点规则配置 3. 参数例外项 1. 前言 Sentinel版本:1.8.0 何为热点?热点即经常访问的数据.很多时候我们希望统计某个热点数据中访问频次最高的 Top K ...

  7. Spring Cloud Alibaba:Sentinel 熔断降级

    1. 前言 Sentinel版本:1.8.0 1.8.0 对熔断特性做了大量升级,低于此版本的谨慎参考 2. 熔断策略 2.1 慢调用比例 最大 RT(即最大的响应时间):请求的响应时间大于RT则统计 ...

  8. 6.Spring Cloud Alibaba教程:Sentinel流量防卫兵的介绍与基本使用

    官网 https://sentinelguard.io/ 服务雪崩 微服务中各个服务之间通过http/rpc互相调用,假设其中1个服务因为网络或自身原因出现问题,调用这个服务就会出现线程堵塞,此时若有 ...

  9. Spring Cloud Alibaba基础教程:Sentinel使用Apollo存储规则

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 上一篇我们介绍了如何通过Nacos的配置功能来存储限流规则.Apollo是国内用户非常多的配置中心,所 ...

  10. Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则

    点击蓝色"程序猿DD"关注我哟 加个"星标",不忘签到哦 通过上一篇<使用Sentinel实现接口限流>的介绍,相信大家对Sentinel已经有了初 ...

最新文章

  1. Python学习(一) 安装,环境搭建,IDE
  2. 超实用:IIS 7.5修改WEB.CONFIG即可实现*.HTML伪静态
  3. 跟我一起写 Makefile(十)
  4. 【SDOI2018】战略游戏【圆方树】【虚树】
  5. oracle sequence last_number,关于oracle序列的LAST_NUMBER
  6. orm对象关系映射演练 一行就是一个对象
  7. DDD实战成绩管理---用户故事
  8. 栈操作与栈帧 (转)
  9. java redis源码分析,慢谈 Redis 实现分布式锁 以及 Redisson 源码解析
  10. 9月18日博文阅读数异常波动公告
  11. 捣鼓PlantUML
  12. 2022年3月15日黑马第三天
  13. 软件测试周刊(第48期):无聊是非常有必要的,一个人在空白时间所做的事,决定了这个人和其他人根本的不同。
  14. 走迷宫爆压,21行突破B站记录(自创,自设地图)
  15. html403禁止访问怎么解决,http403禁止访问错误产生的原因以及解决办法
  16. java编写md5加密解密算法
  17. win7连win10失败解决方法
  18. 解决树莓派鼠标延迟/迟滞问题-转CSDN博主“Deiki”-sunziren
  19. java的setquality值多少_java使用JPEGEncodeParam类的setQuality方法进行高质量图片缩放类(转)...
  20. Debian安装docker全流程

热门文章

  1. 怎么添加自己喜欢的歌曲作为苹果手机铃声
  2. 给孩子买台灯什么牌子好?盘点儿童灯具品牌排行榜
  3. oracle用户锁定,解锁(已解决)
  4. 【图解】一篇搞定ReentrantLock的加锁和解锁过程
  5. 计算机2级可以搞小抄吗,计算机二级excel公式总结小抄
  6. 【S0019】【素材】32套让你脱颖而出的作品集模板,人人必备
  7. ipv6 “无状态地址分配”和 “有状态地址分配” 两种IPV6地址分配方式的区别说明
  8. nginx处理post请求之数据转发
  9. Android手机GPRS上网和彩信设置教程
  10. L1-059 敲笨钟 (20 分) java语言