Sentinel系列之(九)服务熔断

服务熔断

Sentinel整合Ribbon和OpenFeign

@SentinelResource的fallback

1. Ribbon系列
1.1 服务提供者

新建cloudalibaba-provider-payment9003cloudalibaba-provider-payment9004

9003和9004是一样的,以9003为例

  • 建Module【cloudalibaba-provider-payment9003】

  • 改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: 9003
    
    spring:
      application:
        name: nacos-payment-provider
      cloud:
        nacos:
          discovery:
            server-addr: 192.168.59.128:8848 #配置Nacos地址
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    
  • 主启动

    package com.atguigu.springcloud.alibaba;
    
    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);
        }
    }
    
  • 业务类

    package com.atguigu.springcloud.alibaba.controller;
    
    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;
      }
    }
    
1.2 服务消费者
  • 建Module【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>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-openfeign</artifactId>
              </dependency>
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              </dependency>
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
              </dependency>
              <dependency>
                  <groupId>com.atguigu.springcloud</groupId>
                  <artifactId>cloud-api-commons</artifactId>
                  <version>${project.version}</version>
              </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.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: 84
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: 192.168.59.128:8848
        sentinel:
          transport:
            dashboard: 192.168.59.128:8080
            port: 8719
    service-url:
      nacos-user-service: http://nacos-payment-provider
    
  • 主启动

    package com.atguigu.springcloud.alibaba;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @EnableDiscoveryClient
    @SpringBootApplication
    public class OrderNacosMain84
    {
      public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
      }
    }
    
  • 业务类

    ApplicationContextConfig

    package com.atguigu.springcloud.alibaba.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
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    

    CircleBreakerController

    package com.atguigu.springcloud.alibaba.controller;
    
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.atguigu.springcloud.entities.CommonResult;
    import com.atguigu.springcloud.entities.Payment;
    import lombok.extern.slf4j.Slf4j;
    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";
    
        @Resource
        private 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;
      }
    }
    
1.3 测试
  • Sentinel整合Ribbon

    • 启动单机版Nacos

    • 启动Sentinel

    • 启动cloudalibaba-provider-payment9003

    • 启动cloudalibaba-provider-payment9004

    • 启动cloudalibaba-consumer-nacos-order84

    • 多次访问http://localhost:84/consumer/fallback/1,发现轮询负载。

      至此,Nacos + Sentinel + Ribbon整合完成。

  • 不配置fallback和blockHandler

    • 当访问http://localhost:84/consumer/fallback/4或http://localhost:84/consumer/fallback/5时,由于接口手动抛出了异常,返回的是默认的错误页面。

  • 仅配置fallback

    • 修改CircleBreakerController,增加fallback相关配置,这里的fallback就相当于Hystrix中的服务降级。

          package com.atguigu.springcloud.alibaba.controller;
      
          import com.alibaba.csp.sentinel.annotation.SentinelResource;
          import com.alibaba.csp.sentinel.slots.block.BlockException;
          import com.atguigu.springcloud.entities.CommonResult;
          import com.atguigu.springcloud.entities.Payment;
          import lombok.extern.slf4j.Slf4j;
          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";
      
              @Resource
              private RestTemplate restTemplate;
      
          @RequestMapping("/consumer/fallback/{id}")
          @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;
          }
      
          //fallback
          public 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/5,执行了我们自定义的程序异常处理逻辑。

  • 仅配置blockHandler

    • 修改CircleBreakerController,增加blockHandler相关配置,去掉fallback相关配置

      package com.atguigu.springcloud.alibaba.controller;
      
      import com.alibaba.csp.sentinel.annotation.SentinelResource;
      import com.alibaba.csp.sentinel.slots.block.BlockException;
      import com.atguigu.springcloud.entities.CommonResult;
      import com.atguigu.springcloud.entities.Payment;
      import lombok.extern.slf4j.Slf4j;
      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";
      
          @Resource
          private RestTemplate restTemplate;
        
          @RequestMapping("/consumer/fallback/{id}")
          @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;
          }
      
          //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);
          }
      }
      
    • 配置Sentinel,这里以降级规则为例

    • 访问一次http://localhost:84/consumer/fallback/5,由于没有自定义的程序异常处理,抛出了错误页面

    • 多次访问http://localhost:84/consumer/fallback/5,触发降级,执行了我们自定义的降级处理逻辑。

  • 配置fallback和blockHandler

    • 修改CircleBreakerController,同时配置fallback和blockHandler

      package com.atguigu.springcloud.alibaba.controller;
      
      import com.alibaba.csp.sentinel.annotation.SentinelResource;
      import com.alibaba.csp.sentinel.slots.block.BlockException;
      import com.atguigu.springcloud.entities.CommonResult;
      import com.atguigu.springcloud.entities.Payment;
      import lombok.extern.slf4j.Slf4j;
      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";
      
          @Resource
          private RestTemplate restTemplate;
        
          @RequestMapping("/consumer/fallback/{id}")
          @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;
          }
      
          //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);
          }
      }
      
    • 配置Sentinel,这里以流控规则为例

    • 访问一次http://localhost:84/consumer/fallback/4,由于程序本身会抛出异常,执行了自定义程序异常处理逻辑(fallback)。

    • 快速多次访问http://localhost:84/consumer/fallback/4,会直接执行自定义的降级处理逻辑(blockHandler)。

      结论:当接口本身会抛出异常,同时达到了降级条件,最后只会走自定义的降级处理逻辑。

  • exceptionsToIgnore属性

    • 修改CircleBreakerController,增加exceptionsToIgnore属性的配置,当接口抛出了该属性配置的异常类型时,不会走自定义的fallback的处理逻辑。

      package com.atguigu.springcloud.alibaba.controller;
      
      import com.alibaba.csp.sentinel.annotation.SentinelResource;
      import com.alibaba.csp.sentinel.slots.block.BlockException;
      import com.atguigu.springcloud.entities.CommonResult;
      import com.atguigu.springcloud.entities.Payment;
      import lombok.extern.slf4j.Slf4j;
      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";
      
          @Resource
          private RestTemplate restTemplate;
        
          @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) {
                  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);
          }
      }
      
    • 访问http://localhost:84/consumer/fallback/4,没有执行自定义的fallback

    • 访问http://localhost:84/consumer/fallback/5,执行了自定义的fallback

2. Feign系列

Feign组件一般是用在消费侧的,修改cloudalibaba-consumer-nacos-order84

  • 改POM,增加OpenFeign的依赖

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

    server:
      port: 84
    spring:
      application:
        name: nacos-order-consumer
      cloud:
        nacos:
          discovery:
            server-addr: 192.168.59.128:8848
        sentinel:
          transport:
            dashboard: 192.168.59.128:8080
            port: 8719
    service-url:
      nacos-user-service: http://nacos-payment-provider
    # 激活Sentinel对Feign的支持
    feign:
      sentinel:
        enabled: true
    
  • 主启动,增加@EnableFeignClients

      package com.atguigu.springcloud.alibaba;
    
      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);
          }
      }
    
  • 业务类

    • 创建PaymentService,带@FeignClient注解的业务接口

      package com.atguigu.springcloud.alibaba.service;
      
      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);
      }
      
    • 创建PaymentFallbackService

      package com.atguigu.springcloud.alibaba.service;
      
      import com.atguigu.springcloud.entities.CommonResult;
      import com.atguigu.springcloud.entities.Payment;
      import org.springframework.stereotype.Component;
      
      @Component
      public class PaymentFallbackService implements PaymentService
      {
          @Override
          public CommonResult<Payment> paymentSQL(Long id)
          {
              return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
          }
      }
      
    • 修改CircleBreakerController,增加如下代码

      // OpenFeign
      @Resource
      private PaymentService paymentService;
      
      @GetMapping(value = "/consumer/paymentSQL/{id}")
      public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
          return paymentService.paymentSQL(id);
      }
      
  • 测试

    启动cloudalibaba-provider-payment9003

    启动cloudalibaba-consumer-nacos-order84

    访问http://localhost:84/consumer/paymentSQL/1,成功

    关闭cloudalibaba-provider-payment9003,继续访问http://localhost:84/consumer/paymentSQL/1

    可以看到触发了OpenFeign的服务降级。

3. 熔断框架比较

posted @   刘二水  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示