其他分享
首页 > 其他分享> > SpringCloud-Alibaba之Sentinel

SpringCloud-Alibaba之Sentinel

作者:互联网

此文章主要讲解springcloud中的服务熔断Hystrix的替换方案Sentinel的相关知识。

服务熔断Hystrix的替换方案Sentinel

概述

替换方案介绍

Alibaba Sentinel

Resilience4J

Sentinel概述

Sentinel官网

Sentinel是什么

去哪里下

能干嘛

image-20210122130601228

安装和运行Sentinel Dashboard

Sentinel的组成

安装和运行步骤

# 直接启动
java -jar sentinel-dashboard-1.7.1.jar

# -Dserver.port=8080用于指定Sentinel控制台端口为8080
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar

image-20210122131659457

image-20210122131749315

初始化演示工程

启动Nacos服务注册中心和配置中心

startup.cmd -m standalone # 单机启动

# 访问 http://localhost:8848/nacos/

image-20210122132143708

搭建工程

新建模块

cloudalibaba-sentinel-service8401

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>spring_cloud_atguigu_2020</artifactId>
        <groupId>com.itjing.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-sentinel-service8401</artifactId>
    <dependencies>
        <!-- 后续做Sentinel的持久化会用到的依赖 -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!-- sentinel  -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- springcloud alibaba nacos 依赖,Nacos Server 服务注册中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- Open Feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </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>
        <!--日常通用-->
        <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>

        <!-- 引入自己定义的api通用包-->
        <dependency>
            <groupId>com.itjing.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
</project>

YML文件

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 ,如果端口被占用,端口号会自动 +1,提供给 sentinel 的监控端口,直到找到没有被占用的端口
        port: 8719
      # 取消懒加载
      eager: true
      
# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动类

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

业务逻辑

@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA() {
        return "-------testA--------";
    }

    @GetMapping(value = "/testB")
    public String testB() {
        return "~~~~~~~~testB~~~~~~~~";
    }
}

启动 8401。

流控规则

基本介绍

image-20210122133816390

流控模式之直接(默认直接–>快速失败)

配置说明

1597819546992

测试

image-20210122134545204

流控模式之关联

是什么?

配置说明

1597820015308

测试

通过 Postman 并发访问 http://localhost:8401/testB ,然后通过浏览器访问 http://localhost:8401/testA 。B的并发访问,导致A挂了。

image-20210122140129717

image-20210122140310262

流控模式之链路

配置说明

image-20210122140507317

流控效果之预热

概述

配置说明

image-20210122141521701

如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,可慢慢的把流量放进来,慢慢的把阀值增长到设置的阀值。

流控效果之排队等待

概述

image-20210122141858465

配置说明

image-20210122142011184

降级规则

基本介绍

image-20210122142609409

1597820926895

1597820943934

1597820987861

降级规则之RT

概述

平均响应时间超出阈值在时间窗口内通过的请求>=5,两个条件同时满足后触发降级窗口期过后关闭断路器

RT最大4900 (更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

代码

@RestController
@Slf4j
public class FlowLimitController {

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

}

配置

image-20210122144655091

测试

image-20210122145249845

image-20210122150112020

降级规则之异常比例

概述

代码

@GetMapping(value = "/testD")
public String testD() {
    //模拟异常
    int num = 10 / 0;
    return "-------testD--------";
}

配置

image-20210122151059527

当单位统计时长内请求数量大于最小请求数目 5,并且异常比例大于阈值(0.2),则接下来的熔断时间内请求自动会被熔断。经过时间窗口时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

按照上述配置,单独访问一次,必然访问一次,报错一次,因为没有达到服务熔断的要求。开启Jmeter后,直接高并发发送请求,多次调用达到我们的配置条件了,断路器开启,微服务不可用了,不再报错而是服务降级了。

降级规则之异常数

概述

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

1597821525073

1597821548056

配置

image-20210122151628234

第一次访问: http://localhost:8401/testD 肯定报错,因为除数不能为0,但是达到5次报错后,进入熔断后降级。

热点key限流

基本介绍

image-20210122151953672

@SentinelResource注解

应用示例

代码

@GetMapping(value = "/testHotKey")
//通过blockHandler指定熔断降级的方法,通过fallback指定触发异常执行的降级方法
@SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey")
public String testHotKey(
        @RequestParam(value = "p1", required = false) String p1,
        @RequestParam(value = "p2", required = false) String p2) {

    return "testHotKey";
}


/**
 * 熔断降级
 *
 * @param p1
 * @param p2
 * @param exception
 * @return
 */
public String dealTestHotKey(String p1, String p2, BlockException exception) {

    return "--------熔断降级-------";
}

配置

image-20210122153610022

访问: http://localhost:8401/testHotKey?p1=aaa ,当访问频率超出阈值,则进行限流。

image-20210122154029506

如果testHotKey的参数p1的值不是5,那么QPS是1;如果参数p1的值是5,那么QPS是2000。

测试

系统规则

基本介绍

背景

系统规则

系统规则配置界面

image-20210122154848742

@SentinelResource讲解

修改模块

cloudalibaba-sentinel-service8401

新增Controller

@RestController
public class RateLimitController {

    @GetMapping("/byResource")                        //处理降级的方法名
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200, "按照资源名限流测试0K", new Payment(2020L, "serial001"));
    }

    //降级方法
    public CommonResult handleException(BlockException e) {
        return new CommonResult(444, e.getClass().getCanonicalName() + "\t 服务不可用");
    }
}

很明显,上面虽然自定义了兜底方法,但是耦合度太高,下面要解决这个问题。

自定义全局BlockHandler处理类

写一个 CustomerBlockHandler 自定义限流处理类:

1597903188558

Sentinel整合Ribbon系列

准备工作

服务提供者

新建模块

cloudalibaba-provider-payment9003

cloudalibaba-provider-payment9004

POM文件

<dependencies>
    <!-- springcloud alibaba nacos 依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </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.mybatis.spring.boot</groupId>
         <artifactId>mybatis-spring-boot-starter</artifactId>
     </dependency>-->
    <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>

    <!-- 引入自己定义的api通用包-->
    <dependency>
        <groupId>com.itjing.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

YML文件

注意改端口号就行

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # 配置Nacos的地址

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

主启动类

编写主启动类。

业务类

@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    //模拟sql查询的数据
    public static HashMap<Long, Payment> hashMap = new HashMap<>();

    static {
        hashMap.put(1L, new Payment(1L, "xcxcxcxcxcxcxcxcxcxcxcxcxc11111111"));
        hashMap.put(2L, new Payment(2L, "xcxcxcxcggggggggg2222222222222222"));
        hashMap.put(3L, new Payment(3L, "xcxcxcxccxxcxcfafdgdgdsgdsgds33333"));
    }


    @GetMapping("/payment/{id}")
    public CommonResult paymentSql(@PathVariable("id") Long id) {
        Payment payment = hashMap.get(id);
        CommonResult result = new CommonResult(200, "from mysql, server port : " + serverPort + " ,查询成功", payment);
        return result;
    }
}

服务消费者

新建模块

cloudalibaba-consumer-nacos-order84

POM文件

<dependencies>
	<!-- 后续做Sentinel的持久化会用到的依赖 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!-- sentinel  -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!-- springcloud alibaba nacos 依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </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>

    <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>

    <!-- 引入自己定义的api通用包-->
    <dependency>
        <groupId>com.itjing.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

yaml文件

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # 配置Nacos的地址

    sentinel:
      transport:
        # 配置 Sentinel Dashboard 的地址
        dashboard: localhost:8080
        # 默认8719 ,如果端口被占用,端口号会自动 +1,提供给 sentinel 的监控端口,直到找到没有被占用的端口
        port: 8719
      # 取消懒加载
      eager: true
      
# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

# 消费者将要去访问的微服务名称(成功注册进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

主启动类

业务类

配置类

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Controller

@RestController
@Slf4j
public class OrderController {

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consutomer/fallback/{id}")
    public CommonResult fallback(@PathVariable("id") Long id) {
        if (id >= 4) {
            throw new IllegalArgumentException("非法参数异常...");
        } else {
            return restTemplate.getForObject(serverURL + "/payment/" + id, CommonResult.class);
        }
    }
}

上面只实现了 以 nacos 作为服务注册中心,消费者使用 ribbon 实现负载均衡调用提供者的效果。

进行相关配置

在消费者中操作

只配置 fallback

@GetMapping("/consutomer/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handleFallback") //fallback只处理业务异常
public CommonResult fallback(@PathVariable("id") Long id) {
    CommonResult result = restTemplate.getForObject(serverURL + "/payment/" + id, CommonResult.class);
    if (id >= 4) {
        throw new IllegalArgumentException("非法参数异常...");
    } else if (result == null) {
        throw new NullPointerException("没有记录");
    }
    return result;
}

//兜底方法
public CommonResult handleFallback(@PathVariable("id") Long id, Throwable e) {
    return new CommonResult(414, "---fallback--", e);
}

业务异常会被 fallback 处理,返回我们自定义的提示信息,而如果给它加上流控,并触发阈值,只能返回sentinel默认的提示信息

只配置 blockHandler

@GetMapping("/consutomer/fallback/{id}")
//@SentinelResource(value = "fallback", fallback = "handleFallback") //fallback只处理业务异常
@SentinelResource(value = "fallback", blockHandler = "handleblockHandler") //只负责sentinel控制台配置违规
public CommonResult fallback(@PathVariable("id") Long id) {
    CommonResult result = restTemplate.getForObject(serverURL + "/payment/" + id, CommonResult.class);
    if (id >= 4) {
        throw new IllegalArgumentException("非法参数异常...");
    } else if (result == null) {
        throw new NullPointerException("没有记录");
    }
    return result;
}

public CommonResult handleblockHandler(@PathVariable("id") Long id, BlockException e) {
    return new CommonResult(414, "---blockHandler--", e);
}

这时候的效果就是,运行异常直接报错错误页面。在sentinel上添加一个降级规则,设置2s内触发异常2次,触发阈值以后,返回的是我们自定义的 blockhanlder 方法返回的内容。

两者都配置

@GetMapping("/consutomer/fallback/{id}")
//@SentinelResource(value = "fallback", fallback = "handleFallback") //fallback只处理业务异常
//@SentinelResource(value = "fallback", blockHandler = "handleblockHandler")
@SentinelResource(value = "fallback", blockHandler = "handleblockHandler",fallback = "handleFallback")
public CommonResult fallback(@PathVariable("id") Long id) {
    CommonResult result = restTemplate.getForObject(serverURL + "/payment/" + id, CommonResult.class);
    if (id >= 4) {
        throw new IllegalArgumentException("非法参数异常...");
    } else if (result == null) {
        throw new NullPointerException("没有记录");
    }
    return result;
}

//====fallback
public CommonResult handleFallback(@PathVariable("id") Long id, Throwable e) {
    return new CommonResult(414, "---fallback--", e);
}

//====blockHandler                                          blockHandler的方法必须有这个参数
public CommonResult handleblockHandler(@PathVariable("id") Long id, BlockException e) {
    return new CommonResult(414, "---blockHandler--", e);
}

明显两者都是有效的,可以同时配置。

若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。

异常忽略

抛出某异常时,不需要进行兜底处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4A3NnpW-1622078335648)(https://gitee.com/xiaojinggege/blogImage/raw/master/img/image-20210122172327682.png)]

Sentinel整合Feign系列

修改 84 模块

84 消费者调用提供者 9003

Feign组件一般是消费侧

POM文件

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

YML文件

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

主启动类

添加注解 :@EnableFeignClients 激活 open-feign

Service接口

@FeignClient(value = "nacos-payment-provider", fallback = PaymentFallbackService.class) // 实现降级
public interface PaymentService {

    @GetMapping("/payment/{id}")
    public CommonResult paymentSql(@PathVariable("id") Long id);
}

Service接口实现类实现降级

@Component // 不要忘记
public class PaymentFallbackService implements PaymentService {
    @Override
    public CommonResult paymentSql(Long id) {
        return new CommonResult(414, "open-feign 整合 sentinel 实现的全局服务降级策略", null);
    }
}

Controller

@Resource
private PaymentService paymentService;

@GetMapping("/consumer/payment/{id}")
public CommonResult paymentSql(@PathVariable("id") Long id) {
    return paymentService.paymentSql(id);
}

测试

启动相关组件和微服务。

正常访问。

测试,关闭提供者9003微服务,会触发自动降级,不会被耗死。

Sentinel规则持久化

是什么?

怎么玩?

步骤(修改服务消费者)

POM文件

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

YML文件

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 # 配置Nacos的地址

    sentinel:
      transport:
        # 配置 Sentinel Dashboard 的地址
        dashboard: localhost:8080
        # 默认8719 ,如果端口被占用,端口号会自动 +1,提供给 sentinel 的监控端口,直到找到没有被占用的端口
        port: 8719
      # 将Sentinel的规则持久化到Nacos中
      datasource:
        ds1:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data_type: json
            rule_type: flow
      # 取消懒加载
      eager: true

# 暴露监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'

# 消费者将要去访问的微服务名称(成功注册进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

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

在Nacos中添加业务规则配置

去nacos上创建一个dataid ,名字和 yml 配置的一致,比如我这里是 nacos-order-consumer ,json格式,内容如下

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

启动应用,然后刷新 Sentinel,发现存在 关于 /testA 请求路径的流控规则。

停止 8401 在看sentinel,发现里面没有内容,当多次调用接口,关于接口的流控规则又出现了,持久化验证成功。

标签:CommonResult,阈值,SpringCloud,id,熔断,Sentinel,Alibaba,public
来源: https://blog.csdn.net/qq_43144261/article/details/117320113