Sentinel服务熔断功能

Sentinel服务熔断功能

sentinel整合ribbon+openFeign+fallback

1、环境搭建(新建模块)

1.1、启动nacos和sentinel

1.2、新建服务提供者cloudalibaba-provider-payment9003/9004模块

1、引入pom.xml文件

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

    <artifactId>cloudalibaba-provider-payment9004</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

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

2、添加YAML配置文件

两个模块的端口号不同

server:
  port: 9003

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

management:
  endpoints:
    web:
      exposure:
        include: '*'

3、添加主启动类

启动类名称不同

package com.zcl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 描述:主启动类
 *
 * @author zhong
 * @date 2022-10-03 8:52
 */
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9004 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(PaymentMain9004.class, args);
    }
}

4、业务控制器

package com.zcl.springcloud.controller;

import com.zcl.springcloud.entities.CommonResult;
import com.zcl.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;

/**
 * 描述:控制器业务类
 *
 * @author zhong
 * @date 2022-10-03 8:54
 */
@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;
    }
}

5、测试地址

http://localhost:9003/paymentSQL/1

http://localhost:9004/paymentSQL/1

返回的数据内容

{
    
    
  "code": 200,
  "message": "from mysql,serverPort:  9004",
  "data": {
    
    
    "id": 1,
    "serial": "28a8c1e3bc2742d8848569891fb42181"
  }
}

1.3、消费者84

1、新建cloudalibaba-consumer-nacos-order84

2、引入Pom.xml依赖

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

    <artifactId>cloudalibaba-consumer-nacos-order84</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--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>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.zcl.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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>

3、添加YAML配置文件

因为上面创建9004/9003都是创建在nacos-payment-provider服务下的,如果使用负载均衡的话就按照服务名称去查询

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719


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

4、添加主启动

package com.zcl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 描述:主启动类
 *
 * @author zhong
 * @date 2022-10-03 9:03
 */
@SpringBootApplication
@EnableDiscoveryClient
public class OrderNacosMain84 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}

5、负载均衡远程调用配置

package com.zcl.springcloud.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;

/**
 * 描述:负载均衡远程调用
 *
 * @author zhong
 * @date 2022-10-03 9:05
 */
@Configuration
public class ApplicationContextConfig {
    
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
    
    
        return new RestTemplate();
    }
}

6、业务控制器实现

package com.zcl.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.zcl.springcloud.entities.CommonResult;
import com.zcl.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;

/**
 * 描述:业务控制器实现
 *
 * @author zhong
 * @date 2022-10-03 9:07
 */
@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;
    }
}

7、测试访问地址:http://localhost:84/consumer/fallback/2

如果实现了负载均衡就可以进行下面的具体操作

8、修改后请重启微服务

  1. 热部署对java代码级生效及时
  2. 对@SentinelResource注解内属性,有时效果不好

2、Ribbon系列

如下代码都是消费者84上进行修改测试

2.1、目的

  1. fallback管运行异常
  2. blockHandler管配置违规

2.2、如果不进行配置的错误页面不友好

会直接将java中的错误信息返回搭配页面中

2.3、只配置fallback(只赋值业务异常)

1、修改控制

package com.zcl.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.zcl.springcloud.entities.CommonResult;
import com.zcl.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;

/**
 * 描述:业务控制器实现
 *
 * @author zhong
 * @date 2022-10-03 9:07
 */
@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")
    @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 ("IllegalArgumentException,非法参数异常....");
        }else if (result.getData() == null) {
    
    
            throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
        }

        return result;
    }

    /**
     * 兜底方法
     * @param id
     * @param e
     * @return
     */
    public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
    
    
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
    }
}

2、测试fallback

  1. 访问:http://localhost:84/consumer/fallback/4

    {
          
          
      "code": 444,
      "message": "兜底异常handlerFallback,exception内容  IllegalArgumentException,非法参数异常....",
      "data": {
          
          
        "id": 4,
        "serial": "null"
      }
    }
    
  2. 访问:http://localhost:84/consumer/fallback/5

    {
          
          
      "code": 444,
      "message": "兜底异常handlerFallback,exception内容  NullPointerException,该ID没有对应记录,空指针异常",
      "data": {
          
          
        "id": 5,
        "serial": "null"
      }
    }
    

2.4、只配置blockHandler(流控规则异常)

1、控制器修改

@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")
    // @SentinelResource(value = "fallback",fallback = "handlerFallback")
    @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;
    }

    /**
     * Sentinel流控规则异常兜底方法
     * @param id
     * @param blockException
     * @return
     */
    public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
    
    
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.
}

2、添加流控规则

由于上面的代码业务中是没有4和5的id数据,那么查询的时候一定会进行报错提示前端,下面指定异常数作为熔断就可以很好的进行测试了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cS4fL8KA-1670459372969)(image/300、流控规则.png)]

3、测试

  1. 1秒钟内访问2个异常访问:http://localhost:84/consumer/fallback/5

    {
          
          
      "code": 445,
      "message": "blockHandler-sentinel限流,无此流水: blockException  null",
      "data": {
          
          
        "id": 5,
        "serial": "null"
      }
    }
    

    因为没有配置到fallback所以java代码异常就会直接返回前端页面展示

    fallback管理的是java代码运行异常,blockHandler管理的是Sentinel制定的流控规则出现的异常

2.5、fallback和blockHandler都配置

1、控制的注解修改

@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler") // fallback和blockHandler都配置


/**
 * Sentinel流控规则异常兜底方法
 * @param id
 * @param blockException
 * @return
 */
public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
    
    
    Payment payment = new Payment(id,"null");
    return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
}


/**
 * 业务异常兜底方法
 * @param id
 * @param e
 * @return
 */
public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
    
    
    Payment payment = new Payment(id,"null");
    return new CommonResult<>(444,"兜底异常handlerFallback,exception内容  "+e.getMessage(),payment);
}

2、添加流控规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDT1FjUX-1670459372969)(image/301、流控规则.png)]

3、测试

  1. 访问正常的服务1秒超过1QPS:http://localhost:84/consumer/fallback/1

    {
          
          
      "code": 445,
      "message": "blockHandler-sentinel限流,无此流水: blockException  null",
      "data": {
          
          
        "id": 1,
        "serial": "null"
      }
    }
    
  2. 访问java异常:http://localhost:84/consumer/fallback/5

    {
          
          
      "code": 444,
      "message": "兜底异常handlerFallback,exception内容  NullPointerException,该ID没有对应记录,空指针异常",
      "data": {
          
          
        "id": 5,
        "serial": "null"
      }
    }
    

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

2.6、忽略异常属性

1、控制器的编写

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

3、Feign系列

3.1、修改84模块

1、引入Feign依赖

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

2、激活Sentinel对Feign的支持

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

3、带@FeignClient注解的业务接口

也就是远程调用的地址

package com.zcl.springcloud.service;

import com.zcl.springcloud.entities.CommonResult;
import com.zcl.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 描述:Feign接口
 * 使用 fallback 方式是无法获取异常信息的,
 * 如果想要获取异常信息,可以使用 fallbackFactory参数
 *
 * @author zhong
 * @date 2022-10-03 9:55
 */
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)//调用中关闭9003服务提供者
public interface PaymentService {
    
    
    /**
     * 远程调用消费者
     * @param id
     * @return
     */
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

4、接口实现类

该接口的实现类就是被fallback所使用的

package com.zcl.springcloud.service;

import com.zcl.springcloud.entities.CommonResult;
import com.zcl.springcloud.entities.Payment;
import org.springframework.stereotype.Component;

/**
 * 描述:Feign接口实现类
 *
 * @author zhong
 * @date 2022-10-03 9:59
 */
@Component
public class PaymentFallbackService implements PaymentService{
    
    
    /**
     * 接口实现类
     * @param id
     * @return
     */
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
    
    
        return new CommonResult<>(444,"服务降级返回,没有该流水信息",new Payment(id, "errorSerial......"));
    }
}

5、84消费者添加Feign远程调用控制器接口

@RestController
@Slf4j
public class CircleBreakerController
{
    
    
    public static final String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private PaymentService paymentService;

    /**
     * Feign 远程调用
     * @param id
     * @return
     */
    @GetMapping(value = "/consumer/openfeign/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
    {
    
    
        return paymentService.paymentSQL(id);
    }
}

6、主启动类添加@EnableFeignClients启动Feign的功能

7、测试

  1. 启动访问:http://localhost:84/consumer/openfeign/1

    {
          
          
      "code": 200,
      "message": "from mysql,serverPort:  9004",
      "data": {
          
          
        "id": 1,
        "serial": "28a8c1e3bc2742d8848569891fb42181"
      }
    }
    
  2. 测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死

    访问:http://localhost:84/consumer/openfeign/1

    {
          
          
      "code": 444,
      "message": "服务降级返回,没有该流水信息",
      "data": {
          
          
        "id": 1,
        "serial": "errorSerial......"
      }
    }
    

4、熔断框架比较

Sentinel Hystrix resilience4j
隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离 信号量隔离
熔断降级策略 基于响应时间、异常比例、异常数 基于异常比例 基于异常比例、响应时间
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava) Ring Bit Buffer
动态规则配置 支持多种数据源 支持多种数据源 有限支持
扩展性 多个扩展 插件的形式 接口形式
基于注解的支持 支持 支持 支持
限流 基于QPS、支持基于调用关系的限流 有限支持 Rate Limlter
流量整形 支持预热模式、匀速器模式、预热排队模式 不支持 简单Rate Limiter模式
系统自适应保护 支持 不支持 不支持
控制台 通过开箱即用的控制台,可配置规则、查看秒级监控、及其发现等 简单的监控查看 不提供控制台,可对接其他监控系统

猜你喜欢

转载自blog.csdn.net/baidu_39378193/article/details/128230256