springCloudAlibaba之服务熔断组件---sentinel

sentinel学习

  • 服务雪崩

服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程。
在这里插入图片描述

sentinel容错机制

常见的容错机制有超时机制、服务限流、隔离、服务熔断

  • 超时机制
    在不做任何处理的情况下,服务提供者不可用回导致消费者请求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源较快,一定程度上可以抑制资源耗尽的问题。
  • 服务限流
    某个服务达到QPS设定最大值则抛异常。
  • 服务熔断
    在这里插入图片描述
  • 服务降级
    有服务熔断,必然要有服务降级。
    所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fellback(回退),回调,返回一个缺省值。例如:(备用接口/缓存/mock数据),这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。(服务熔断之后进入客户端降级方法)

使用代码方式进行QPS流控-流控规则初体验

  • 引入sentinel相关包
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--sentinel核心库-->
    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-core</artifactId>
      <version>1.8.0</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>

    <!--sentinel注解方式-->
    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-annotation-aspectj</artifactId>
      <version>1.8.0</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

    <!--sentinel整合dashbaord控制台-->
    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-transport-simple-http</artifactId>
      <version>1.8.0</version>
    </dependency>
  </dependencies>
  • 代码实现
package com.sentinel.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * @author : zhouzhiqiang
 * @date : 2024/6/5 23:25
 * @description :
 */
@Slf4j
@RestController
@RequestMapping("/hello")
public class HelloController {

    private static final String RESOURCE_NAME="hello";
    private static final String USER_RESOURCE_NAME="user";
    private static final String DEGRADE_RESOURCE_NAME="degrade";

    @RequestMapping("/helloSentinel")
    public String hello(){

        Entry entry=null;
        try {
            entry= SphU.entry(RESOURCE_NAME);
            String str="hello world";
            log.info("======="+str+"=======");
            return str;
        } catch (BlockException e) {
            log.info("block");
            return "被限流了!";
        }catch (Exception e){
            Tracer.traceEntry(e,entry);
        }finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return null;
    }

    @PostConstruct
    public static void initFlowRules(){
        //流控规则
        List<FlowRule> rules=new ArrayList<>();

        //流控
        FlowRule rule = new FlowRule();
        //为哪个资源进行流量控制
        rule.setResource(RESOURCE_NAME);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1);
        rules.add(rule);

        //加载流控规则
        FlowRuleManager.loadRules(rules);
    }
}

使用@SentinelResource注解进行流控

要使用这个注解需要引入相关包和配置SentinelResourceAspect的bean

  • 引入包
    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-annotation-aspectj</artifactId>
      <version>1.8.0</version>
    </dependency>
  • 配置bean
  @Bean
    public SentinelResourceAspect sentinelResourceAspect(){
        return new SentinelResourceAspect();
    }
  • 使用注解方式

value:定义资源(接口名称)
blockHandler :设置流控降级后的处理方法,默认该方法,必须声明在一个类中
fallback:接口中出现异常了,就可以交给fellback指定的处理方法

@PostConstruct
    public static void initFlowRules(){
        //流控规则
        List<FlowRule> rules=new ArrayList<>();

        //流控
        FlowRule rule = new FlowRule();
        //为哪个资源进行流量控制
        rule.setResource(USER_RESOURCE_NAME);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1);
        rules.add(rule);

        //加载流控规则
        FlowRuleManager.loadRules(rules);
    }

    @RequestMapping("/user")
    @SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForGetUser",fallback = )
    public User getUser(String id){
        return new User("zzq");
    }

    /**
     * 注意:
     * 1、一定是public
     * 2、返回值一定得和原方法返回值一致,包含方法的参数
     * 3、可以在参数的最后添加BlockException,可以区分是什么规则的处理方法
     * @param id
     * @param e
     * @return
     */
    //降级方法
    public User blockHandlerForGetUser(String id,BlockException e){
        e.printStackTrace();
        return new User("被流控了!");
    }

通过代码方式设置降级规则-降级规则初体验

@RestController
@RequestMapping("/test")
public class TestController {

    private static final String DEGRADE_RESOURCE_NAME="degrade";

    @RequestMapping("/degrade")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME,entryType = EntryType.IN,blockHandler = "blockHandlerForJob")
    public User degrade(String id){
        int i=1;
        int sum=i/0;
        return new User("成功");
    }

    public User blockHandlerForJob(String id, BlockException e){
        e.printStackTrace();
        return new User("触发降级规则");
    }

    @PostConstruct
    public void initDegradeRule(){
        List<DegradeRule> rules=new ArrayList<>();

        DegradeRule rule = new DegradeRule();
        rule.setResource(DEGRADE_RESOURCE_NAME);
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        //触发熔断异常数
        rule.setCount(2);
        //触发熔断的最小请求数
        rule.setMinRequestAmount(2);
        //统计时长:单位ms  1分钟
        rule.setStatIntervalMs(60*1000);
        //一分钟内:执行了两次 ,出现两次异常 就会触发熔断

        /**
         * 熔断持续时间,单位秒。一旦触发了熔断,再次请求接口则直接调用降级方法。
         * 10秒后,---半开状态,恢复接口调用。如果再次请求,则会熔断,不再根据熔断规则进入熔断,而是直接进入熔断
         */
        rule.setTimeWindow(10);


        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }
}

流控规则一般设置在服务生产方,而降级规则一般设置在服务消费方

sentinel控制台部署

下载dashboard--https://github.com/alibaba/Sentinel/releases

运行jar包:java -jar sentinel-dashboard-1.8.0.jar。运行完毕进行访问,默认端口号8080。用户名和密码默认sentinel
在这里插入图片描述

客户端整合服务端

客户端需要引入Transport模块来与sentinel控制台进行通信

<!--sentinel整合dashbaord控制台-->
    <dependency>
      <groupId>com.alibaba.csp</groupId>
      <artifactId>sentinel-transport-simple-http</artifactId>
      <version>1.8.0</version>
    </dependency>
  • 配置启动
    客户端启动时需要加入JVM参数-Dcsp.sentinel.dashboard.server=192.168.1.15:8080,指定控制台地址和端口
    在这里插入图片描述

springcloud整合sentinel

  • 添加依赖
     <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
  • yaml配置文件
server:
  port: 8081
spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.184.15:8080
        port: 8719
        client-ip: 192.168.184.1

在虚拟机上部署dashboard的时候,主机和虚拟机一直网络不通,解决方法:
1、关闭本机和虚拟机的防火墙
在这里插入图片描述
2、本机不动,配置虚拟机ip,网段和本地vmnet8保持一致,网关查看虚拟网络编辑器
在这里插入图片描述

ONBOOT="yes"
IPADDR="192.168.184.15"
NETMASK="255.255.255.0"
GATEWAY="192.168.184.2"
DNS1="8.8.8.8"
DNS2="8.8.4.4"

3、最重要的一步,设置网络连接模式为net模式
在这里插入图片描述
原因:解释: 1:当我们安装VMware Workstation后,在宿主机(物理电脑)上会多出两个网卡,VMNet1、VMNet8。 2:vmnet1是为host-only方式服务的,vmnet8是为NAT方式服务的

QPS流控规则

添加流控规则:QPS为2,超过2触发流控降级
在这里插入图片描述
在这里插入图片描述

  • 代码中指定自定义降级方法,dashboard定义流控规则,代码自定义降级方法
    /**
     * 流控测试接口
     * @return
     */
    @RequestMapping("/flow")
    @SentinelResource(value = "flow",blockHandler = "blockHandlerForFlow")
    public String flow(){
        return "正常访问";
    }

    public String blockHandlerForFlow(BlockException e){
        e.printStackTrace();
        System.out.println("触发流控,快速失败");
        return "触发流控,快速失败";
    }

并发线程数-流控规则

  • dashboard配置
    在这里插入图片描述
  • 代码
    @RequestMapping("/flowThread")
    @SentinelResource(value = "flowThread",blockHandler = "blockHandlerForFlow")
    public String flowThread(){
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "正常访问";
    }

    public String blockHandlerForFlow(BlockException e){
        e.printStackTrace();
        System.out.println("触发流控,快速失败");
        return "触发流控,快速失败";
    }
  • 测试
    开两个浏览器模拟两个线程
  • 测试结果
    在这里插入图片描述

BlockExceptionHandler统一异常处理,此时可以不加@SentinelResource注解

  • 代码
@Data
public class Result <T>{
    private Integer code;
    private String msg;
    private T data;

    public Result(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public Result(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static Result error(Integer code,String msg){
        return new Result(code,msg);
    }
}
@Component
@Slf4j
public class MyBlockExceptionHandler implements BlockExceptionHandler {

    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
        //规则的详细信息
        log.info("BlockExceptionHandler BlockException=============="+e.getRule());
        Result r = null;
        if (e instanceof FlowException) {
            r=Result.error(100,"接口被限流了!");
        }else if (e instanceof DegradeException){
            r=Result.error(101,"服务降级了!");
        }else if (e instanceof ParamFlowException){
            r=Result.error(102,"热点参数限流了!");
        }else if (e instanceof SystemBlockException){
            r=Result.error(103,"触发系统保护规则!");
        }else if (e instanceof AuthorityException){
            r=Result.error(104,"授权规则不通过!");
        }

        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(),r);
    }
}
  • 测试代码
    此时在dashboar的为这个接口设置流控规则后,可以看到设置的同一异常起效果了
 @RequestMapping("/exceptionHandler")
    public String exceptionHandler(){
        return "正常访问";
    }

关联流控模式

流控模式有三种直接、关联、链路
在这里插入图片描述

关联模式:当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写的速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢。

  • dashboard设置
    当生成订单QPS>=2的时候,查询订单接口就被限流
    在这里插入图片描述
  • 代码
 @RequestMapping("/add")
    public String add(){
        System.out.println("下单成功");
        return "生成订单";
    }

    @RequestMapping("/get")
    public String get(){
        return "查询订单";
    }
  • 生成接口和查询接口测试
    生成接口和查询接口不好一起测试,生成接口使用apifox进行压测,查询接口手动点
    在这里插入图片描述
  • 测试结果
    查询接口触发流控规则
    在这里插入图片描述
  • 或者使用JMeter去做压测
    在这里插入图片描述
    在这里插入图片描述

链路流控模式

可以对资源进行流控
在这里插入图片描述

  • 代码
public interface IUserService {
    User getUser();
}

对getUser添加@SentinelResource注解

@Service
public class IUserServiceImpl implements IUserService{

    @SentinelResource(value = "getUser",blockHandler = "blockHandlerForUser")
    public User getUser() {
        User user = new User();
        user.setId("001");
        user.setName("针对业务方法进行流控");
        return user;
    }

    public User blockHandlerForUser(BlockException e){
        e.printStackTrace();
        User user = new User();
        user.setId("001");
        user.setName("ddddd");
        return new User();
    }
}

test1和test2调用getUser(),当getUser(),达到QPS2的时候,对test1或者test2做流控

@RestController
@RequestMapping("/link")
public class LinkController {

    @Autowired
    private IUserService userService;

    @RequestMapping("/test1")
    public User test1(){
        return userService.getUser();
    }

    @RequestMapping("/test2")
    public User test2(){
        return userService.getUser();
    }
}
  • 配置文件配置
    web-context-unify: false # 默认将调用链路收敛了
server:
  port: 8081
spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
      transport:
        dashboard: 192.168.184.15:8080
        port: 8719
        client-ip: 192.168.184.1
      web-context-unify: false  # 默认将调用链路收敛了
  • dashboar配置
    在这里插入图片描述
  • 测试结果
    当test1qps达到2的时候触发流控
    在这里插入图片描述

流控效果介绍

流控效果有快速失败、预热、排队等待。预热:适用于激增流量的情况
在这里插入图片描述

预热流控效果

warm up:预热冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

  • dashboard配置
    qps最大为10,10秒内达到10
    在这里插入图片描述

排队等待

激增流量:长时间处于低水平,某个时间段处于高水平,可以使用预热的方式进行处理。

脉冲流量:一段时间处于低水平某个时刻到达高水平,然后又变为低水平,循环往复

匀速排队:严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过

  • 测试
    设置流控效果为快速失败,单机QPS阈值为5,使用JMeter测试,一秒执行10次循环5次
    在这里插入图片描述
    在这里插入图片描述
    测试结果:可以看到一秒内10个QPS5个通过,5个拒绝
    在这里插入图片描述
  • 使用排队等待流控效果
    dashboard配置:单机阈值5,超时时间5秒。比如有10个线程,每次进去5个,其他5个等待。执行完一个进去一个,如果5个线程在5秒内没有执行完则直接拒绝
    在这里插入图片描述
  • 测试结果
    所有请求都执行成功
    在这里插入图片描述

熔断降级规则

慢调用比例:慢调用占调用总数的比例

  • dashboard配置
    最大RT:单位毫秒,不能超过1秒,超过1秒则触发降级。比例阈值:慢调用占请求次数的比例。最小请求数:配合比例阈值,10次请求有一次触发慢调用则触发降级
    在这里插入图片描述
  • 使用Jmetter进行测试
    在这里插入图片描述
  • 测试结果
    触发降级
    在这里插入图片描述

sentinel-整合openfeign降级

nacos服务注册必须是一个web项目
按此步骤完成微服务搭建后,发现服务并没有注册到nacos注册中心,在官方文档查看springboot和springcloud版本也并没有冲突,尝试引入nacos管理依赖后还是不行,最后想到要在nacos注册中心注册的微服务应该必须是一个web项目,而在springboot环境下开发web项目需要引入springMvc的相关启动依赖,加上后发现服务成功注册到了nacos注册中心

  • 在消费端引入依赖
<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>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
  • 消费端配置yml
    特别加上:server: port: 8084 spring: application: name: product-service cloud: nacos: username: zzq password: 1w2e3r4! server-addr: 192.168.184.15:8848 discovery: namespace: 31a14d31-cda4-4a37-a4ba-7717aaddd97d feign: sentinel: enabled: true
server:
  port: 8084
spring:
  application:
    name: product-service
  cloud:
    nacos:
      username: zzq
      password: 1w2e3r4!
      server-addr: 192.168.184.15:8848
      discovery:
        namespace: 31a14d31-cda4-4a37-a4ba-7717aaddd97d
feign:
  sentinel:
    enabled: true

消费端feing调用

  • feing
@FeignClient(value = "stock-service",path = "/person",fallback = IPersonServiceFallback.class)
public interface IPersonService {

    @RequestMapping("/getPerson")
    public Person getPerson();
}

必须要有一个feingclint的实现类,并且注入进spring

@Component
public class IPersonServiceFallback implements IPersonService {
    public Person getPerson() {
        Person person = new Person();
        person.setId("001");
        person.setName("未找到用户");
        return person;
    }
}

消费端调用服务端

   @RestController
@RequestMapping("/product")
public class ProductController {

    @Autowired
    private IPersonService personService;

    @RequestMapping("/getProduct")
    public String getProduct(){
        Person person = personService.getPerson();
        return "调用成功:"+person.getName();
    }
}

  • 测试结果
    调用异常执行降级方法
    在这里插入图片描述

热点参数流控

场景:接口参数对某些值的访问QPS比其他高。典型的电商系统中,商品查询接口,某些商品的访问QPS要远远高于其他商品,比如商品ID为1的商品,访问请求远远高于其他商品。

  • dashboard配置
    在这里插入图片描述

sentinel规则持久化

在这里插入图片描述
结合nacos配置中心使用推模式进行规则的持久化

基于Nacos配置中心控制台实现推送

  • 引入依赖
  <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
   </dependency>
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值