当前位置: 代码迷 >> 综合 >> SpringCloud Alibaba Sentinel实现熔断与限流
  详细解决方案

SpringCloud Alibaba Sentinel实现熔断与限流

热度:60   发布时间:2023-10-24 05:11:36.0

服务使用中的各种问题 

  服务雪崩
  服务降级
  服务熔断
  服务限流

安装Sentinel 

sentinel和项目必须在同一个ip下

  sentinel组件由2部分构成
    后台
    前台8080
    运行命令
      前提
        java8环境OK
        8080端口不能被占用
      命令
        java -jar sentinel-dashboard-1.7.1.jar
    访问sentinel管理界面
      http://localhost:8080
      登录账户密码均为 sentinel 

初始化演示工程

启动Nacos8848成功
    http://localhost:8848/nacos/#/login
  创建Module    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>wms</artifactId><groupId>com.hk.wms</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>wms-sentinel-service8401</artifactId>
<dependencies><!-- 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><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.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
</project>


    YML

server:port: 8401spring:application:name: wms-sentinal-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: 192.168.1.131:8848sentinel:transport:#配置Sentin dashboard地址dashboard: localhost:8080# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口port: 8719
service-url:nacos-user-service: http://wms-provider
config-url:config-service: http://wms-config-3377management:endpoints:web:exposure:include: '*'


    主启动

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


    业务类FlowLimitController

package com.hk.wms.wmssentinelservice8401.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class FlowLimitController {@Value("${service-url.nacos-user-service}")String serverUrl ;@Value("${config-url.config-service}")String configUrl;@AutowiredRestTemplate template;@GetMapping("/testA")public String testA() {return "----testA";}@GetMapping("/testB")public String testB() {return "----testB";}@GetMapping("/testC")public String testC() {return template.getForObject(serverUrl+"/provider/",String.class);}
}


  启动Sentinel8080
  启动微服务8401

    空空如也
    Sentinel采用的懒加载说明
      执行一次访问即可
        http://localhost:8401/testA
        http://localhost:8401/testB
      sentinel8080正在监控微服务8401 

sentinel和项目必须在同一个ip下,sentinel才能监控微服务端口8401

流控规则

基本介绍
    进一步解释说明

SpringCloud Alibaba Sentinel实现熔断与限流
  流控模式
    直接(默认)
      直接-> 快速失败
        默认
      配置及说明

SpringCloud Alibaba Sentinel实现熔断与限流
      测试
        快速点击多次http://localhost:8401/testA
        结果

SpringCloud Alibaba Sentinel实现熔断与限流
        思考
          直接调用默认报错原因,技术方面OK,但是应该要有设置对应的报错信息和界面方便管理
    关联
      是什么
        当关联的资源达到阈值时,就限流自己
        当与A关联的资源B达到阈值后,就限流A自己
        B惹事,A挂了
      配置A

SpringCloud Alibaba Sentinel实现熔断与限流
      postman模拟并发密集访问testB
        postman开启20个线程每隔0.3s请求/testB,导致A挂了
      运行后发现testA挂了
   
  流控效果
    直接-> 快速失败(默认的流控处理)

      直接失败,抛出异常
        Blocked by Sentinel(flowing limit)
    预热
      说明
        公式:阈值除以coldFactor(默认值为3),经过预热时长后才会阈值
      官网
        默认ColdFactor为3,即请求QPS从threashold/3开始,经预热时长逐渐升到设定的QPS阈值
        限流冷启动
      WarmUp配置

SpringCloud Alibaba Sentinel实现熔断与限流
      多次点击http://localhost:8401/testB
      应用场景

SpringCloud Alibaba Sentinel实现熔断与限流
    排队等待
      匀速排队,严格控制请求通过的间隔时间

SpringCloud Alibaba Sentinel实现熔断与限流
      官网

SpringCloud Alibaba Sentinel实现熔断与限流
      测试
        postman设置请求发送速率即可 

降级规则


  基本介绍
    进一步说明
    Sentinel的断路器是没有半开状态的
      半开的状态,系统会自动检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,具体可以参考Hystrix
    RT
      是什么

SpringCloud Alibaba Sentinel实现熔断与限流
   SpringCloud Alibaba Sentinel实现熔断与限流

  测试
        代码

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


        配置

SpringCloud Alibaba Sentinel实现熔断与限流
        jmeter压测
        结论

SpringCloud Alibaba Sentinel实现熔断与限流
    异常比例
      是什么

SpringCloud Alibaba Sentinel实现熔断与限流
SpringCloud Alibaba Sentinel实现熔断与限流     

配置

SpringCloud Alibaba Sentinel实现熔断与限流
      jmeter设置每秒10个请求即可
      结论

SpringCloud Alibaba Sentinel实现熔断与限流
    异常数
      是什么

SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流
      异常数是按照分钟统计的
      测试
        代码

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


        配置

SpringCloud Alibaba Sentinel实现熔断与限流
SpringCloud Alibaba Sentinel实现熔断与限流     

  jmeter 

热点key限流


  基本介绍
    是什么

SpringCloud Alibaba Sentinel实现熔断与限流
  SpringCloud Alibaba Sentinel实现熔断与限流

SpringCloud Alibaba Sentinel实现熔断与限流
    @SentinelResource


  代码

@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) {return "----testHotKey";
}public String deal_testHotKey(String p1, String p2, BlockException exception) {return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
}


    com.alibaba.csp.sentinel.slots.block.BlockException
  配置

SpringCloud Alibaba Sentinel实现熔断与限流
    1
      @SentinelResource(value = "testHotKey")
      异常会直接打印到前台用户界面,提示不太友好
    2
      @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
      方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
      用了我们自己定义的

  参数例外项
    上诉案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
    特例情况
      普通
        超过1秒钟一个后,达到阈值1后马上被限流
      我们期望p1参数当它是某一个特殊值时,它的限流值和平时不一样
      特例
        假如当p1的值等于5时,它的QPS可以为200
    配置

SpringCloud Alibaba Sentinel实现熔断与限流
    测试
      http://localhost:8401/testHotKey?p1=1&p2=2
      http://localhost:8401/testHotKey?p1=5&p2=2
      当p1为5的时候,阈值QPS变为了200
    前提条件
      热点参数的注意点,参数必须为基本类型或者String
  其他
    添加一个java运行异常试试 

SpringCloud Alibaba Sentinel实现熔断与限流

系统规则


  是什么

SpringCloud Alibaba Sentinel实现熔断与限流
 

@SentinelResource

配置@SentinelResource后如果想使用blockHandler配置流控规则的资源名就必须使用@SentinelResource的value值


  按资源名称限流+后续处理
    启动Nacos成功
    启动Sentinel成功
    Module
      cloudalibaba-sentinel-service8401
      业务类RateLimitController

 @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) {return "----testHotKey";}public String deal_testHotKey(String p1, String p2, BlockException exception) {return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)}


    配置流控规则

SpringCloud Alibaba Sentinel实现熔断与限流
    
      一秒点击一次,ok,疯狂点击,返回自己定义的限流处理信息,限流发生
    额外问题
      此时关闭服务8401
      Sentinel控制台,流控规则消失了??
        所以说规则是跟着微服务的生命周期的


  上面兜底方案面临的问题

SpringCloud Alibaba Sentinel实现熔断与限流
  客户自定义限流处理逻辑
    创建CustomerBlockHandler类用于自定义限流处理逻辑
    自定义限流处理类
      CustomerBlockHandler

public class CustomerBlockHandler {public static String customBlockHandler(BlockException e){return "限流!!!!!";}
}

注意:customBlockHandler方法必须是静态的,否则无法被调用
    RateLimitController

 @GetMapping("/customBlockHandler")@SentinelResource(value="customBlockHandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler")public String customBlockHandler(){return "测试成功";}


    启动微服务后先调用一次
    Sentinel控制台配置

SpringCloud Alibaba Sentinel实现熔断与限流
  
  更多注解属性说明

SpringCloud Alibaba Sentinel实现熔断与限流
    多说一句

SpringCloud Alibaba Sentinel实现熔断与限流
    Sentinel主要有三个核心API
      SPhU定义资源
      Tracer定义统计
      ContextUtil定义了上下文 

服务熔断功能

  Ribbon系列


    启动nacos和sentinel
    提供者9002/9001
      POM ,YML,主启动,没有变化
      业务类

@GetMapping("/getStr/{id}")public String getStr(@PathVariable("id")int id){return id+":"+serverPort;}


     
    消费者8401
        ApplicationContextConfig 

package com.hk.wms.wmscustom83.config;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

@GetMapping("/testF/{id}")@SentinelResource(value="testF",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler1",fallback = "handlerFallBack")public String CircleBreakerController(@PathVariable("id")int id){String str=template.getForObject(serverUrl+"/getStr/"+id,String.class);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}

 


          修改后请重启微服务
            热部署对java代码级生效及时  ,对@SentinelResource注解内属性,有时效果不好
         
            fallback管运行异常,

 public String handlerFallBack(@PathVariable int id,Throwable e){return "异常";}


            blockHandler管配置违规

 public static String customBlockHandler1(@PathVariable int id, BlockException e){return "限流1!!!!!";}

注意:fallback方法和blockHandler方法必须有和@SentinelResource注解的方法具有同样的形参,否则无法生效


          同时配置fallback和blockHandler
          异常忽略
            SpringCloud Alibaba Sentinel实现熔断与限流
  Feign系列
    修改8401模块
      8401消费者调用提供者9002
      Feign组件一般是消费端
    POM

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


    YML

server:port: 8401spring:application:name: wms-sentinal-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: 192.168.1.131:8848sentinel:transport:#配置Sentin dashboard地址dashboard: localhost:8080      #8080端口将会监控8401# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口port: 8719datasource:ds1:nacos:server-addr: 192.168.1.131:8848dataId: wms-sentinal-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow
service-url:nacos-user-service: http://wms-provider
config-url:config-service: http://wms-config-3377management:endpoints:web:exposure:include: '*'
#激活sentinel对feign的支持
feign:sentinel:enabled: true


    业务类
      带@FeignClient注解的业务接口
      fallback = FeignFallbackService.class

package com.hk.wms.wmssentinelservice8401.service;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value="wms-provider",fallback = FeignFallbackService.class)
public interface FeignService {@GetMapping(value="/getStr/{id}")public String getString(@PathVariable("id") int id);
}

用于处理服务方的异常错误 

package com.hk.wms.wmssentinelservice8401.service;import org.springframework.stereotype.Component;@Component
public class FeignFallbackService implements FeignService{@Overridepublic String getString(int id) {return "服务降级返回";}
}


      Controller

 @GetMapping("/testE/{id}")@SentinelResource(value="testE",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler2",fallback = "handlerFallBack")public String getStrController(@PathVariable("id")int id){String str=feignService.getString(id);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}

fallback管运行异常,

 public String handlerFallBack(@PathVariable int id,Throwable e){return "异常";}

  blockHandler管配置违规

 public static String customBlockHandler2(@PathVariable int id, BlockException e){return id+"限流1!!!!!";}

注意:fallback方法和blockHandler方法必须有和@SentinelResource注解的方法具有同样的形参,否则无法生效

 

主启动
      添加@EnableFeignClients启动Feign的功能

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

代码

package com.hk.wms.wmssentinelservice8401.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.hk.wms.wmssentinelservice8401.myhandler.CustomerBlockHandler;
import com.hk.wms.wmssentinelservice8401.service.FeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;@RestController
@Slf4j
public class FlowLimitController {@ResourceFeignService feignService;@Value("${service-url.nacos-user-service}")String serverUrl ;@Value("${config-url.config-service}")String configUrl;@AutowiredRestTemplate template;@GetMapping("/testA")@SentinelResource(value="testA",blockHandler = "deal_testA")public String testA(@RequestParam(value="p1",required = false)String p1) {System.out.println(Thread.currentThread().getName()+".................testA");return "----testA";}@GetMapping("/testB")@SentinelResource(value="testB")public String testB() {try {Thread.sleep(800L);} catch (InterruptedException e) {e.printStackTrace();}finally {return "----testA";}}@GetMapping("/testC")public String testC() {return template.getForObject(serverUrl+"/provider/",String.class);}@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) {return "----testHotKey";}@GetMapping("/customBlockHandler")@SentinelResource(value="customBlockHandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler")public String customBlockHandler(){return "测试成功";}public String deal_testHotKey(String p1, String p2, BlockException exception) {return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)}public String deal_testA(String p1,BlockException e){return "----testA+";}@GetMapping("/testE/{id}")@SentinelResource(value="testE",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler2",fallback = "handlerFallBack")public String getStrController(@PathVariable("id")int id){String str=feignService.getString(id);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}@GetMapping("/testF/{id}")@SentinelResource(value="testF",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler1",fallback = "handlerFallBack")public String CircleBreakerController(@PathVariable("id")int id){String str=template.getForObject(serverUrl+"/getStr/"+id,String.class);if(id<0){throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");}if(str==null){throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");}return str;}public String handlerFallBack(@PathVariable int id,Throwable e){return "异常";}}

 自定义 blockHandler管配置违规

package com.hk.wms.wmssentinelservice8401.myhandler;import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.PathVariable;/****/
public class CustomerBlockHandler {public static String customBlockHandler(BlockException e){return "限流!!!!!";}public static String customBlockHandler1(@PathVariable int id, BlockException e){return "限流1!!!!!";}public static String customBlockHandler2(@PathVariable int id, BlockException e){return id+"限流1!!!!!";}
}

 

 熔断框架比较

SpringCloud Alibaba Sentinel实现熔断与限流

Sentinel规则持久化


    一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化
  将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
  步骤
    修改cloudalibaba-sentinel-service8401
    POM

 

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

  YML

server:port: 8401spring:application:name: wms-sentinal-servicecloud:nacos:discovery:#Nacos服务注册中心地址server-addr: 192.168.1.131:8848sentinel:transport:#配置Sentin dashboard地址dashboard: localhost:8080      #8080端口将会监控8401# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口port: 8719
#datasource是sentinel的一个属性datasource:ds1:nacos:server-addr: 192.168.1.131:8848dataId: wms-sentinal-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow
service-url:nacos-user-service: http://wms-provider
config-url:config-service: http://wms-config-3377
#暴露所有接口
management:endpoints:web:exposure:include: '*'
#激活sentinel对feign的支持
feign:sentinel:enabled: true
#datasource是sentinel的一个属性datasource:ds1:nacos:server-addr: 192.168.1.131:8848dataId: wms-sentinal-servicegroupId: DEFAULT_GROUPdata-type: jsonrule-type: flow

注意:datasource是sentinel的一个属性,properties写法:spring.cloud.sentinel.datasource

   

添加Nacos业务规则配置

SpringCloud Alibaba Sentinel实现熔断与限流

[{"resource": "testF","limitApp": "default","grade": 1,"count": 1,"strategy": 0,"controlBehavior": 0,"clusterMode": false}
]


      内容解析

SpringCloud Alibaba Sentinel实现熔断与限流
注意:resource的写法有两种,一种是按照路径的写法,就是@requestMapping的value,一种是写 @SentinelResource 的value   

  这一步的作用是每次消费者微服务启动时在nacos中定义sentinel的流控规则,从而做到持久化的效果
    启动8401后刷新sentinel发现业务规则有了
    停止8401再看sentinel
      发现流控规则没有了
    重新启动8401再看sentinel
      咋一看没有,但是你调用 /rateLimit/byUrl后它就会出现 

 

  相关解决方案