俩贼能熬夜的人!! -

​ —-2022.1.7 3:00:00

[toc]

Sentinel介绍及下载安装

Sentinel 是什么?

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

  • 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。

  • 完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

    官网:这里

Sentinel-features-overview

下载安装可视端

这里

启动

http://localhost:8080 账号密码为 sentinel 启动命令为 java -jar jar包名

创建项目

  • pom.xml

    <!--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>
  • yaml

    server:
    port: 8401

    spring:
    application:
    name: cloudalibaba-sentinel-service
    cloud:
    nacos:
    discovery: # http://1.116.182.96:8888/files
    server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
    transport:
    dashboard: localhost:8080 #配置Sentinel dashboard地址
    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: '*'

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

  • 主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    public class MainApp8401
    {
    public static void main(String[] args) {
    SpringApplication.run(MainApp8401.class, args);
    }
    }

  • Controller测试类

    package com.atguigu.springcloud.alibaba.controller;

    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;

    /**
    * @auther zzyy
    * @create 2020-02-24 16:26
    */
    @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";
    }


    @GetMapping("/testD")
    public String testD()
    {
    // try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
    // log.info("testD 测试RT");

    log.info("testD 异常比例");
    int age = 10/0;
    return "------testD";
    }

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

    @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)
    }

    }

  • 访问 http://localhost:8401/testB 看看控制台实时监控

image-20220106113510870

Sentinel流控规则

image-20220106114105601

image-20220106114428849

流控模式—–直接

image-20220106114617589

image-20220106114642362

每秒三下触发默认限流规则 抛出异常

流控模式—–关联

image-20220106115313850

测试:

image-20220106115534159

PostMan中新建多线程集合组

image-20220106164744324

后台运行 /testB 前台访问 /testA

效果: (被限流)

image-20220106164828302

流控模式—–链路

image-20220106165030947

Sentinel降级规则

Sentinel 1.8.0 及以上版本。1.8.0 版本对熔断降级特性进行了全新的改进升级,请使用最新版本以更好地利用熔断降级的能力。

支持半开了

熔断策略

Sentinel 提供以下几种熔断策略:

  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

Sentinel热点规则

返回自定义异常显示

@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)
}

设定热点规则

image-20220106170458989

image-20220106171507571

关于该资源内部出错

@SentinelResource

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

RuntimeException

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

总结

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

Sentinel系统规则(了解即可)

image-20220106171728394

@SentinelResource

关键属性:

  • value:资源名称,必需项(不能为空)

  • fallback:fallback函数名称,可选项,用于在抛出异常的时候提供 fallback处理逻辑。fallback函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback函数签名和位置要求: 返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable类型的参数用于接收对应的异常。

    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl",fallback ="runTimeException")
    public CommonResult byUrl()
    {

    int age = 10/0;

    return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
    }
    public CommonResult runTimeException(Throwable exception) //这里不能用Exception
    {

    return new CommonResult(666,exception.getClass().getCanonicalName()+"\t 运行时异常");
    }
  • blockHandler:实现处理函数,该函数的传参必须与资源点的传参一样,并且最后加上BlockException异常参数;同时,返回类型也必须一样。

    @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 服务不可用");
    }
  • blockHandlerClass: 自定义限流处理类

  • fallbackClass: 自定义异常类

同时启用blockHander 和 fallback

给出结论: 单次运行异常走 fallback的处理 方法 触发QPS走blockHander的处理方法

整合Ribbon+openFeign+fallback实现服务熔断

  • 客户端引入依赖

    <!--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>
  • 主启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableFeignClients
    public class OrderNacosMain84
    {
    public static void main(String[] args) {
    SpringApplication.run(OrderNacosMain84.class, args);
    }
    }

  • 配置RestTemplate 负载均衡

    @Configuration
    public class ApplicationContextConfig
    {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
    return new RestTemplate();
    }
    }
    @Configuration
    public class ApplicationContextConfig
    {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
    return new RestTemplate();
    }
    }

  • Controller测试

    //测试RestTemplate
    @Value("${service-url.nacos-user-service}")
    public String SERVICE_URL;

    @GetMapping("/consumer2/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQ2(@PathVariable("id") Long id)
    {
    return restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class);
    }

image-20220107022504883

image-20220107022518753

可以看见是轮询访问服务端

 
//测试OpenFeign
//==================OpenFeign
@Resource
private PaymentService paymentService;

@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
{
return paymentService.paymentSQL(id);

}
=============================================================================
//service
@FeignClient(value = "${service-url.nacos-user-service}",fallback = PaymentFallbackService.class)
public interface PaymentService
{
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

这里就不截图了 同样效果

  • 实现熔断

    @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",
    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) {
    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);
    }
    //本例是blockHandler
    public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
    Payment payment = new Payment(id,"null");
    return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
    }
  • 关闭远程服务试试

    image-20220107023947406

持久化配置

引出: 一旦重启引用 sentinel配置规则会消失

image-20220107024214451

实现步骤

  • <!--SpringCloud ailibaba sentinel-datasource-nacos -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    
     
    

    + yaml

    ```yaml
    spring:
    application:
    name: cloudalibaba-sentinel-service
    cloud:
    nacos:
    discovery:
    server-addr: IP:8848 #Nacos服务注册中心地址
    sentinel:
    transport:
    dashboard: localhost:8080 #配置Sentinel dashboard地址
    port: 8719
    datasource:
    ds1:
    nacos:
    server-addr: IP:8848
    dataId: cloudalibaba-sentinel-service
    groupId: DEFAULT_GROUP
    data-type: json
    rule-type: flow
  • 在Nacos可视化面板添加规则

  • resource:资源名称;

    limitApp:来源应用;

    grade:阈值类型,0表示线程数,1表示QPS;

    count:单机阈值;

    strategy:流控模式,0表示直接,1表示关联,2表示链路;

    controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;

    clusterMode:是否集群。

    [

    {

    "resource": "/rateLimit/byUrl",

    "limitApp": "default",

    "grade": 1,

    "count": 1,

    "strategy": 0,

    "controlBehavior": 0,

    "clusterMode": false

    }

    ]

image-20220107024706012

image-20220107025355371

成功!