我是靠谱客的博主 哭泣蜻蜓,最近开发中收集的这篇文章主要介绍SpringCloud Alibaba服务熔断降级与限流之【Sentinel】Sentinel: 分布式系统的流量防卫兵,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在这里插入图片描述

Sentinel: 分布式系统的流量防卫兵

下载sentinel-dashboard-1.7.2.jar,地址:https://github.com/alibaba/Sentinel/tags
运行jar包,访问localhost:8080,来到sentinel界面,账号密码都是sentinel

微服务整合sentinel

  1. 建模块cloudalibaba-sentinel-service8401
  2. 导依赖
<!-- 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>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 写yml文件
server:
  port: 8401
spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848   #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080     #配置sentinel dashboard地址
        port: 8719                    #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        #sentinel会启动一个http server与dashboard进行通信,这个http server要占用8719这个端口

management:
  endpoints :
    web:
      exposure:
         include: "*"
  1. 主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class, args);
    }
}
  1. controller
@RestController
public class FlowLimitController {

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

    @GetMapping("/testB")
    public String testB() {
        return "-----testB";
    }
}
  1. 启动8401
    此时我们到sentinel中查,发现并8401的任何信息,是因为sentinel是懒加载,需要我们执行一次访问才会有信息,访问localhost:8401/testA,再刷新sentinel的dashboard页面,可以看到已经开始监听

sentinel的流控规则和流控模式

在这里插入图片描述

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

sentinel的降级规则

Sentinel 降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。
在这里插入图片描述

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

sentinel的热点规则

如何自定义降级方法,而不是默认的抛出异常?
使用@SentinelResource直接实现降级方法,它等同Hystrix的@HystrixCommand

定义一个测试方法

@GetMapping("/testHotKey") 
// value = "testHotKey"定义一个资源名,blockHandler指定降级方法
@SentinelResource(value = "testHotKey", 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) {
   //sentinel系统默认的提示:BLocked by Sentin
   return "------deal_testHotKey,o(T-T)o";
}

在这里插入图片描述
测试:localhost:8401/testHotKey?p1=1
一秒钟内多次访问会发生降级,走的是我们自己定义的降级方法
在这里插入图片描述
有一点需要注意,如果是带第二个请求参数的请求,热点规则不会限制,因为我们配置的是第一个参数
在这里插入图片描述

sentinel的热点规则的高级选项

参数例外项,可以进行一些高级设置,比如p1为某个特殊值的时候,有单独的限流规则

在这里插入图片描述
当参数值为5的时候,有特殊的热点规则

注意
上面的配置中如果程序出现异常,是不会走blockHander的降级方法的,因为这个@SentinelResource注解的方法只配置了热点规则,这里配置的降级方法是sentinel针对热点规则配置的,只有触发热点规则才会降级

那如何配置异常的降级方法呢,也很简单,在@SentinelResource注解的注解后加上fallback参数进行定义

sentinel的系统规则

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

系统规则支持以下的模式:

  • Load自适应(仅对Linux/Unix-like机器生效):系统的load1作为启发指标,进行自适应系统保护。当系统load1超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR阶段)。系统容量由系统的maxQps*minRt估算得出。设定参考值一般是CPU cores*2.5
  • CPU usage(1.5.d版本):当系统CPU使用率超过阈值即触发系统保护(取值范围0.0-1.0),比较灵敏。
  • 平均RT:当单台机器上所有入口流量的平均RT达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口QPS:当单台机器上所有入口流量的QPS达到阈值即触发系统保护。

@SentinelResource注解

  1. 在模块cloudalibaba-sentinel-service8401中引入自定义的common依赖
 <dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
   <groupId>com.kk.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>
  1. 额外创建一个controller类
@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. 启动
  2. 配置限流规则,这里资源名可以是@SentinelSource的value属性值,也可以是@GetMapping的value值
    在这里插入图片描述
  3. 访问测试,可看到超过限流规则,会进入我们定义的限流方法

现在我们关闭8401服务,会发现我们配置的限流规则也消失了,关闭服务,规则就没有了。
出现了Hystrix中出现的问题,降级方法与业务方法耦合,一个服务需要配置一个降级方法,并且配置没有实现持久化

自定义限流处理逻辑

  1. 单独创建一个类,用于处理限流,里面定义了handlerException1handlerException2
public class CustomerBlockHandler {
    public static CommonResult handlerException1(BlockException exception) {
        return new CommonResult(4444, "按客户自定义,global handlerException---1");
    }
    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(4444, "按客户自定义,global handlerException----2");
    }
}
  1. 在controller中,指定使用自定义类中的方法作为降级方法
    关键点:
    blockHandlerClass = CustomerBlockHandler.class,指定处理类
    blockHandler = "handlerException2",指定降级方法
@GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
    public CommonResult customerBlockHandler() {
        return new commonResult(code:200, message:"按客户自定义", new Payment(id:2020L, serial:"serial003"));
    }
  1. 启动服务,配置限流规则,然后测试方法是否会按我们配置的那样,由我们指定的处理类和方法进行降级
    在这里插入图片描述
  2. 测试成功
    在这里插入图片描述

注意:降级方法必须与原方法参数一致,并且多一个BlockException exception参数

sentinel的熔断

  1. 建模块cloudalibaba-provider-payment9003cloudalibaba-provider-payment9004
  2. 导依赖
<!-- 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>
 <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 改yml文件
server:
  port: 9003

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

management:
  endpoints:
    web:
      exposure:
        include: "*"
  1. 主启动类加@EnableDiscoveryClient注解
  2. controller
@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,"28a8cle3bc2742d8848569891fb42181"));
		hashMap.put(2L,new Payment(2L,"bba8cle3bc2742d8848569891ac32182"));
		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,"frommysql,serverPort:"+serverPort,payment);
		return result;
	 }
 }

  1. 新建一个cloudalibaba-consumer-nacos-order84消费者模块
  2. 导依赖,(nacos-discovery中自动集成了Ribbon)
<!-- SpringCloud ailibaba nacos-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 写yml文件
server:
  port: 84
spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery :
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider
  1. 主启动类加@EnableDiscoveryClient
  2. 添加RestTemplate配置类
@Configuration
public class ApplicationContextConfig{
	@Bean
	@LoadBalanced
	public RestTemplate getRestTemplate(){
		return new RestTemplate();
	}
}
  1. controller,为业务方法添加fallback来指定降级方法
@RestController
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("rllegalArgumentException,非法参数异常......");
        } else if (result.getData() == null) {
            throw new NullPointerException("NullpointerException,该ID没有对应记录,空指针异常");
        }
        return result;
    }

    public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
        Payment payment = new Payment(id, "null");
        return new CommonResult<>(444, "兜底异常handlerFallback,exception内容" + e.getMessage(), Payment.class);
    }
}
  1. 启动cloudalibaba-consumer-nacos-order84服务
  • 访问http://localhost:84/consumer/fallback/1,发现调到了9003和9004服务,并且实现了轮询。
  • 访问http://localhost:84/consumer/fallback/4,会报我们定义的运行时异常,此时会走我们自定义的fallback降级方法。

如果去掉fallback方法,为业务方法添加blockHandler降级方法,看看是什么效果?

①定义blockHandler方法

public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
	Payment payment = new Payment(id, " null ");
	return new CommonResult<>(445, "blockHandler-sentinel限流,无此流水:blockException" + blockException.getStackTrace());
}

②在@SentinelSource注解后加上blockHandler = "blockHandler"
③重新启动84服务,并且定义限流规则或者降级规则
在这里插入图片描述
④访问http://localhost:84/consumer/fallback/4,发现由于去掉了fallback方法,运行时异常没人管了,直接Error Page显示给用户了。但是如果快速访问,QPS达到我们设置的限流规则的话,就能跳转到blockHandler的降级方法
如果是fallback和blockHandler都定义的话会如何呢
测试可以看到,若blockHandler和fallback都进行了配置,快速点击进行访问导致错误的,会由blockhandler降级方法处理

fallback管运行异常、blockHandler管配置违规

sentinel整合ribbon+openFeign+fallback

  1. 84模块中导入OpenFeign的依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 配置yml文件,激活sentinel对feign的支持
#激活sentinel对feign的支持
feign :
  sentinel:
    enabled: true
  1. 主启动类上加@EnableFeignClients注解
  2. 创建远程调用pay模块的接口
@FeignClient(value = "nacos-payment-provider")
public interface PaymentService {
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
  1. 创建这个接口的实现类,用于降级
@Component
public class PaymentFallbackService implements PaymentService {
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(444, "服务降级返回,--PaymentFallbackservice");
    }
}
  1. 再次修改接口,指定降级类,就是@FeignClient注解后添加fallback= PaymentFallbackService.class属性
  2. controller添加远程调用
 @Resource
    private PaymentService paymentservice;
    @GetMapping(value="/consumer/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        return paymentservice. paymentSQL(id);
    }
  1. 启动9003和84,关闭9003,测试84服务是否会降级,测试成功

sentinel的规则持久化

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

  1. 在pom文件中导入相关依赖
<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
  1. yml配置文件
    实际上就是指定,我们的规则要保证在哪个名称空间的哪个分组下,这里没有指定namespace,但是是可以指定的,注意这里的dataid要与服务名一致
spring:
  cloud:
    sentinel: 
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service(服务名)
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow
  1. 在nacos中创建一个配置文件,dataId就是上面配置文件中配置的dataId:cloudalibaba-sentinel-service(不用加yaml),将规则配入,以json格式
[{
	"resource": "访问的路径",
	"limitApp": "default",
	"grade": 1,
	"count": 1,
	"strategy": 0,
	"controlBehavior": 0,
	"clusterMode": false
}]

resource:资源名称;(说白了就是ip:端口后面的那一串controller定义的访问uri)
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。

在这里插入图片描述

这样配置后,服务重启,可看到所配置的规则会恢复,从而实现规则配置的持久化

最后

以上就是哭泣蜻蜓为你收集整理的SpringCloud Alibaba服务熔断降级与限流之【Sentinel】Sentinel: 分布式系统的流量防卫兵的全部内容,希望文章能够帮你解决SpringCloud Alibaba服务熔断降级与限流之【Sentinel】Sentinel: 分布式系统的流量防卫兵所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部