Alibaba微服务组件Sentinel学习笔记

news2024/11/15 4:24:33

1 .Sentinel 是什么

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel  是面向分布式服务架构的流量控制组件,主要以 流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定 性。
源码地址: https://github.com/alibaba/Sentinel
官方文档: https://github.com/alibaba/Sentinel/wiki
Sentinel 具有以下特征 :
  • 丰富的应用场景 Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控 制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
  • 完备的实时监控 Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态 Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring CloudDubbogRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel
  • 完善的 SPI 扩展点 Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。 例如定制规则管理、适配数据源等。
  • 阿里云提供了 企业级的 Sentinel 服务,应用高可用服务 AHAS

1.1Sentinel和Hystrix对比

https://github.com/alibaba/Sentinel/wiki/Sentinel­%E4%B8%8E­Hystrix­%E7%9A%84%E5%AF%B9%E6%AF%94  

2.Sentinel快速开始

https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8
在官方文档中,定义的 Sentinel 进行资源保护的几个步骤:
1) 定义资源
2) 定义规则
3) 检验规则是否生效
2.1Sentinel资源保护的方式
方式1:API 实现
  • 引入依赖
    <!--sentinel核心库-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.0</version>
        </dependency>
  • 编写测试逻辑
package com.tuling.sentinelnew.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.tuling.sentinelnew.pojo.User;
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;


@RestController
@Slf4j
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";


    // 进行sentinel流控
    @RequestMapping(value = "/hello")
    public String hello() {

        Entry entry = null;
        try {
            // 1.sentinel针对资源进行限制的
            entry = SphU.entry(RESOURCE_NAME);
            // 被保护的业务逻辑
            String str = "hello world";
            log.info("=====" + str + "=====");
            return str;
        } catch (BlockException e1) {
            // 资源访问阻止,被限流或被降级
            //进行相应的处理操作
            log.info("block!");
            return "被流控了!";
        } catch (Exception ex) {
            // 若需要配置降级规则,需要通过这种方式记录业务异常
            Tracer.traceEntry(ex, entry);
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
        return null;
    }


    /**
     * 定义规则
     * <p>
     * spring 的初始化方法
     */
    @PostConstruct
    private static void initFlowRules() {

        // 流控规则
        List<FlowRule> rules = new ArrayList<>();

        // 流控
        FlowRule rule = new FlowRule();
        // 为哪个资源进行流控
        rule.setResource(RESOURCE_NAME);
        // 设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // Set limit QPS to 20.
        rule.setCount(1);
        rules.add(rule);


        // 通过@SentinelResource来定义资源并配置降级和流控的处理方法
        FlowRule rule2 = new FlowRule();
        //设置受保护的资源
        rule2.setResource(USER_RESOURCE_NAME);
        // 设置流控规则 QPS
        rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值
        // Set limit QPS to 20.
        rule2.setCount(1);

        rules.add(rule2);

        // 加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }

    @PostConstruct  // 初始化
    public void initDegradeRule() {
        /*降级规则 异常*/
        List<DegradeRule> degradeRules = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource(DEGRADE_RESOURCE_NAME);
        // 设置规则侧率: 异常数
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 触发熔断异常数 : 2
        degradeRule.setCount(2);
        // 触发熔断最小请求数:2
        degradeRule.setMinRequestAmount(2);
        // 统计时长:  单位:ms    1分钟
        degradeRule.setStatIntervalMs(60 * 1000); //  时间太短不好测

        // 一分钟内: 执行了2次  出现了2次异常  就会触发熔断

        // 熔断持续时长 : 单位 秒
        // 一旦触发了熔断, 再次请求对应的接口就会直接调用  降级方法。
        // 10秒过了后——半开状态: 恢复接口请求调用, 如果第一次请求就异常, 再次熔断,不会根据设置的条件进行判定
        degradeRule.setTimeWindow(10);


        degradeRules.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRules);

        /*
        慢调用比率--DEGRADE_GRADE_RT
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
        degradeRule.setCount(100);
        degradeRule.setTimeWindow(10);
        //请求总数小于minRequestAmount时不做熔断处理
        degradeRule.setMinRequestAmount(2);
        // 在这个时间段内2次请求
        degradeRule.setStatIntervalMs(60*1000*60);   //  时间太短不好测
        // 慢请求率:慢请求数/总请求数> SlowRatioThreshold ,
        // 这里要设置小于1   因为慢请求数/总请求数 永远不会大于1
        degradeRule.setSlowRatioThreshold(0.9);*/

    }


    /**
     * @param id
     * @return
     * @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
     * 怎么使用: 1.添加依赖<artifactId>sentinel-annotation-aspectj</artifactId>
     * 2.配置bean——SentinelResourceAspect
     * value  定义资源
     * blockHandler 设置 流控降级后的处理方法(默认该方法必须声明在同一个类)
     * 如果不想在同一个类中 blockHandlerClass 但是方法必须是static
     * fallback 当接口出现了异常,就可以交给fallback指定的方法进行处理
     * 如果不想在同一个类中 fallbackClass 但是方法必须是static
     * <p>
     * blockHandler 如果和fallback同时指定了,则blockHandler优先级更高
     * exceptionsToIgnore 排除哪些异常不处理
     */
    @RequestMapping("/user")
    @SentinelResource(value = USER_RESOURCE_NAME, fallback = "fallbackHandleForGetUser",
            /*exceptionsToIgnore = {ArithmeticException.class},*/
            /*blockHandlerClass = User.class,*/ blockHandler = "blockHandlerForGetUser")
    public User getUser(String id) {
        int a = 1 / 0;
        return new User("xushu");
    }

    public User fallbackHandleForGetUser(String id, Throwable e) {
        e.printStackTrace();
        return new User("异常处理");
    }

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


    @RequestMapping("/degrade")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME, entryType = EntryType.IN,
            blockHandler = "blockHandlerForFb")
    public User degrade(String id) throws InterruptedException {
        // 异常数\比例
        throw new RuntimeException("异常");

        /* 慢调用比例
        TimeUnit.SECONDS.sleep(1);
        return new User("正常");*/
    }

    public User blockHandlerForFb(String id, BlockException ex) {
        return new User("熔断降级");
    }


}
StartApplication.java
package com.tuling.sentinelnew;

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class StartApplication {
    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class,args);
    }
    /**
     * @description: 注解支持的配置Bean
     * @method: sentinelResourceAspect
     * @author: wang fei
     * @date: 2023/1/22 16:46:26
     * @param: []
     * @return: org.springframework.beans.factory.annotation.Value
    **/
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}

测试结果: 

缺点:
业务侵入性很强,需要在controller中写入非业务代码.
配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则
方式2:
@SentinelResource 注解实现
@SentinelResource 注解用来标识资源是否被限流、降级。
blockHandler:   定义当资源内部发生了 BlockException 应该进入的方法(捕获的是 Sentinel 定义的异常)
fallback:   定义的是资源内部发生了 Throwable 应该进入的方法
exceptionsToIgnore :配置 fallback 可以忽略的异常
  • 引入依赖
       <!--sentinel启动器-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
  • Controller中编写测试逻辑,添加@SentinelResource,并配置blockHandlerfallback
package com.wang.controller;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: SpringCloudAlibabaLearn
 * @BelongsPackage: com.wang.controllter
 * @Author: wang fei
 * @CreateTime: 2023-01-16  16:48
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @GetMapping("/pay")
    @SentinelResource(value = "pay",blockHandler = "flowBlockHandler")
    public String pay(){
        return "success";
    }

    @GetMapping("/flowThread")
    @SentinelResource(value = "flowThread",blockHandler = "flowBlockHandler")
    public String flowThread() throws InterruptedException {
        TimeUnit.SECONDS.sleep(5);
        return "success";
    }

    //自定义流控处理返回方法
    public String flowBlockHandler(BlockException e){
        return "流控成功 ,阻塞服务";
    }


    @GetMapping("/add")
    public String add(){
        return "下单成功";
    }

    @GetMapping("/get")
    public String get(){
        return "查询成功";
    }


    /**
     * 热点规则,必须使用@SentinelResource
     * @param id
     * @return
     * @throws InterruptedException
     */
    @RequestMapping("/get/{id}")
    @SentinelResource(value = "getById",blockHandler = "HotBlockHandler")
    public String getById(@PathVariable("id") Integer id) throws InterruptedException {

        System.out.println("正常访问");
        return "正常访问";
    }

    public String HotBlockHandler(@PathVariable("id") Integer id,BlockException e) throws InterruptedException {

        return "热点异常处理";
    }
}
  • 编写MyBlockExceptionHandler
package com.wang.exception;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wang.domain.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @author 飞
 */
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {

    Logger log= LoggerFactory.getLogger(this.getClass());

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
        // getRule() 资源  规则的详细信息
        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,"授权规则不通过");
        }

        //返回json数据
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(), r);
    }
}

Result.java

package com.wang.domain;


/**
 * @author 飞
 */
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 Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public static Result error(Integer code, String msg){
        return new Result(code,msg);
    }
}
server:
  port: 8070
spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
      web-context-unify: false  # 默认将调用链路收敛, 导致链路流控效果无效
      datasource:
        flow-rule:
          nacos:
            server-addr: 127.0.0.1:8848 # nacos地址
            username: nacos
            password: nacos
            dataId: order-sentinel-flow-rule
            rule-type: flow

3.启动 Sentinel 控制台

下载控制台  jar  包并在本地启动:可以参见 此处文档
https://github.com/alibaba/Sentinel/releases
 java ‐jar sentinel‐dashboard‐1.8.0.jar
  • 用户可以通过如下参数进行配置:
-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel
-Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456 ;如果省略这两个参数,默认用户和密码均为
sentinel
-Dserver.servlet.session.timeout=7200 用于指定  Spring Boot  服务端  session  的过期时间,如 7200 表示  7200  秒; 60m 表示  60  分钟,
默认为  30  分钟;
java ­Dserver.port=8858 ­Dsentinel.dashboard.auth.username=xushu ­Dsentinel.dashboard.auth.password=123456  ­jar sentinel
dashboard­1.8.0.jar
  • 为了方便快捷启动可以在桌面创建.bat文件
java ‐Dserver.port=8858 ‐Dsentinel.dashboard.auth.username=xushu Dsentinel.dashboard.auth.password=123456 ‐jar D:\s
erver\sentinel‐dashboard‐1.8.0.jar
pause
访问 http://localhost:8080/#/login  , 默认用户名密码:  sentinel/sentinel

Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,所以要确保客户端有访问量;

 4. Spring Cloud Alibaba整合Sentine

    2.2步骤

5.Sentinel控制台规则配置详解

5.1 实时监控
监控接口的通过的 QPS 和拒绝的 QPS  
5.2 簇点链路  
用来显示微服务的所监控的 API  

 

5.3 流控规则
  • 流量控制flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,
以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。    ====  FlowRule            RT( 响应时间 )   1/0.2s  =5 

 

同一个资源可以创建多条限流规则。 FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍
历完毕。一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果。

参考文档: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6 

  • 限流阈值类型
QPS(Query Per Second):每秒请求数,就是说服务器在一秒的时间内处理了多少个请求。
进入簇点链路选择具体的访问的 API ,然后点击流控按钮  

 

 

  • 并发线程数
并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应 延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线 程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程 池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是
对低延时的调用有比较大的影响。 Sentinel  并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目 (正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端
进行配置。

 

 

  • BlockException异常统一处理
springwebmvc 接口资源限流入口在 HandlerInterceptor 的实现类 AbstractSentinelInterceptor 的preHandle方 法中,对异常的处理是 BlockExceptionHandler 的实现类      
sentinel 1.7.1 引入了 sentinel-spring-webmvc-adapter.jar
自定义BlockExceptionHandler 的实现类统一处理BlockException
package com.wang.exception;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wang.domain.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * @author 飞
 */
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {

    Logger log= LoggerFactory.getLogger(this.getClass());

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
        // getRule() 资源  规则的详细信息
        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,"授权规则不通过");
        }

        //返回json数据
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(), r);
    }
}
  • 流控模式
基于调用关系的流量控制。调用关系包括调用方、被调用方;一个方法可能会调用其它方法,形成一个调用链路的层次
关系:
直接
资源调用达到设置的阈值后直接被流控抛出异常  
关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写 操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本 身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和
write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy
RuleConstant.STRATEGY_RELATE 同时设置 refResource write_db 。这样当写库操作过于频繁时,读数据的请求会被限流。

 

链路
根据调用链路入口限流。
下面 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 getUser 的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示:
上图中来自入口 /order/test1  /order/test2 的请求都调用到了资源 getUser Sentinel  允许只根据某个入口的统计信息对 资源限流。

 

测试会发现链路规则不生效
注意,高版本此功能直接使用不生效,如何解决?
从1.6.3 版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。
1.7.0 版本开始(对应SCA的2.1.1.RELEASE),官方在CommonFilter 引入了
WEB_CONTEXT_UNIFY 参数,用于控制是否收敛context。将其配置为 false 即可根据不同的URL 进行链路限流。
SCA 2.1.1.RELEASE之后的版本,可以通过配置spring.cloud.sentinel.web-context-unify=false即可关闭收敛 
spring . cloud . sentinel . web context unify : false
测试,此场景拦截不到BlockException,对应 @SentinelResource 指定的资源必须在 @SentinelResource 注解中指定
blockHandler处理BlockException
总结: 为了解决链路规则引入ComonFilter的方式,除了此处问题,还会导致更多的问题,不建议使用ComonFilter的方式。 流控链路模
式的问题等待官方后续修复,或者使用AHAS。
  • 快速失败
RuleConstant.CONTROL_BEHAVIOR_DEFAULT )方式是默认的流量控制方式, QPS 超过任意规则的阈值后,新的请求就会被 立即拒绝,拒绝方式为抛出FlowException 这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统 的准确水位时。
Warm Up (激增流量)
Warm Up RuleConstant.CONTROL_BEHAVIOR_WARM_UP )方式,即预热 / 冷启动方式。当系统长期处于低水位的情况下,当流量 突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过" 冷启动 " ,让通过的流量缓慢增加,在一定时间内逐渐 增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
冷加载因子 : codeFactor  默认是 3 ,即请求  QPS   threshold / 3  开始,经预热时长逐渐升至设定的  QPS  阈值。
通常冷启动的过程系统允许通过的  QPS  曲线如下图所示

 编辑流控规则

 jmeter测试

查看实时监控,可以看到通过QPS存在缓慢增加的过程

 

 

匀速排队(脉冲流量)
匀速排队(`RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER`)方式会严格控制请求通过的间隔时间,也即是让请 求以均匀的速度通过,对应的是漏桶算法。
该方式的作用如下图所示

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下 来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的 请求。

 

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
jemeter压测

 

查看实时监控,可以看到通过QPS为5,体现了匀速排队效果
  • 降级规则
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。我们需要对不稳定 的弱依赖服务调用 进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身 的手段,通常在客户端(调用端)进行配置。

 

 

  • 熔断策略
慢调用比例
慢调用比例  ( SLOW_REQUEST_RATIO ) 选择以慢调用比例作为阈值,需要设置允许的慢调用  RT (即最大的响应时间), 请求的响应时间大于该值则统计为慢调用 当单位统计时长( statIntervalMs )内请求数目大于设置的最小请求数目,并 且慢调用的比例大于阈值 ,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态 (HALF­OPEN  状态),若接下来的一个请求响应时间小于设置的慢调用  RT  则结束熔断,若大于设置的慢调用  RT  则会 再次被熔断。

 

jemeter压测/test接口,保证每秒请求数超过配置的最小请求数
查看实时监控,可以看到断路器熔断效果
异常比例
异常比例  ( ERROR_RATIO ) 当单位统计时长( statIntervalMs )内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。 经过熔断时长后熔断器会进入探测恢复状( HALF­OPEN  状 态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]
代表  0% ­ 100%

 查看实时监控,可以看到断路器熔断效果

 

异常数
异常数  ( ERROR_COUNT ) 当单位统计时长内的异常数目超过阈值之后会自动进行熔断 。经过熔断时长后熔断器会进入探 测恢复状态(HALF­OPEN  状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。
配置降级规则

 

 

  •  整合openfeign进行降级

 

引入
<!--        openfeig 远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--sentinel依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
application.yml
feign:
  sentinel:
    # openfeign整合sentinel
    enabled: true
openfegin接口
package com.wang.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(value="stock-nacos",path = "/stock",fallback = StockFeignServiceFallback.class)
public interface StockFeignService {

    @RequestMapping("/reduct")
    public String reduct2();
}
openfegin的fallback实现类

 

package com.wang.feign;

import org.springframework.stereotype.Component;

@Component
public class StockFeignServiceFallback implements StockFeignService {
    @Override
    public String reduct2() {
        return "降级啦!!!";
    }
}
  • 热点参数限流
热点识别流控
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的数据,并对其访问进行限 制。比如:

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。 热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

 

注意:
1. 热点规则需要使用@SentinelResource("resourceName")注解,否则不生效
2. 参数必须是7种基本数据类型才会生效

 单机阈值: 针对所有参数的值进行设置的一个公共的阈值
 1.  假设当前 参数 大部分的值都是热点流量,  单机阈值就是针对热点流量进行设置,   额外针对普通流量进行参数值流控
 2.  假设当前 参数 大部分的值都是普通流量,  单机阈值就是针对普通流量进行设置,   额外针对热点流量进行参数值流控
配置热点参数规则
注意: 资源名必须是@SentinelResource(value="资源名")中 配置的资源名,热点规则依赖于注解

具体到参数值限流,配置参数值为3,限流阈值为1
  • 系统规则 

Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口  QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让
系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
Load  自适应 (仅对  Linux/Unix­like  机器生效):系统的  load1  作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护( BBR  阶段)。系统
容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
https://www.cnblogs.com/gentlemanhai/p/8484839.html
CPU usage 1.5.0+  版本):当系统  CPU  使用率超过阈值即触发系统保护(取值范围  0.0­1.0 ),比较灵敏。
平均  RT :当单台机器上所有入口流量的平均  RT  达到阈值即触发系统保护,单位是毫秒。
并发线程数 :当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口  QPS :当单台机器上所有入口流量的  QPS  达到阈值即触发系统保护。

 

 测试结果

  • 授权控制规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用  Sentinel  的来源访问控制(黑白名单
控制)的功能。来源访问控制根据资源的请求来源( origin )限制资源是否通过,若配置白名单则只有请求来源位于白名
单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
来源访问控制规则( AuthorityRule )非常简单,主要有以下配置项:
resource :资源名,即限流规则的作用对象。
limitApp :对应的黑名单 / 白名单,不同  origin  , 分隔,如 appA,appB
strategy :限制模式, AUTHORITY_WHITE 为白名单模式, AUTHORITY_BLACK 为黑名单模式,默认为白名单模式。

 配置授权规则

 

第一步:实现 com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser 接口,在parseOrigin 方法中区分来源 ,并交给spring管理 注意:如果引入CommonFilter,此处会多出一个。
package com.wang.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;


@Component
public class MyRequestOriginParser implements RequestOriginParser {
    /**
     * 通过request获取来源标识,交给授权规则进行匹配
     * @param request
     * @return
     */
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 标识字段名称可以自定义
        String origin = request.getParameter("serviceName");
//        if (StringUtil.isBlank(origin)){
//            throw new IllegalArgumentException("serviceName参数未指定");
//        }
        return origin;
    }
}

  • 集群规则
为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个  API  的总  QPS   50 ,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个  server  来专门来统计总的调用量,其它的实例都与这台  server  通信来判 断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有  10  台机器,我们给每台机器设置单机 限流阈值为 10 QPS ,理想情况下整个集群的限流阈值就为  100 QPS 。不过实际情况下流量到每台机器可能会不均匀, 会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流 控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
https://github.com/alibaba/Sentinel/wiki/%E9%9B%86%E7%BE%A4%E6%B5%81%E6%8E%A7
集群流控中共有两种身份:
Token Client :集群流控客户端,用于向所属  Token Server  通信请求  token 。集群限流服务端会返回给客户端
结果,决定是否限流。
Token Server :即集群流控服务端,处理来自  Token Client  的请求,根据配置的集群规则判断是否应该发放
token (是否允许通过)。
Sentinel  集群流控支持限流规则和热点规则两种规则,并支持两种形式的阈值计算方式:
集群总体模式 :即限制整个集群内的某个资源的总体  qps  不超过此阈值。
单机均摊模式 :单机均摊模式下配置的阈值等同于单机能够承受的限额, token server  会根据连接数来计算总 的阈值(比如独立模式下有 3   client  连接到了  token server ,然后配的单机均摊阈值为  10 ,则计算出的集群总量
就为  30 ),按照计算出的总的阈值来进行限制。这种方式根据当前的连接数实时计算总的阈值,对于机器经常进行 变更的环境非常适合。
启动方式
Sentinel 集群限流服务端有两种启动方式:
独立模式(Alone),即作为独立的 token server 进程启动,独立部署,隔离性好,但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。

 

嵌入模式(Embedded),即作为内置的 token server 与服务在同一进程中启动。在此模式下,集群中各个 实例都是对等的,token server 和 client 可以随时进行转变,因此无需单独部署,灵活性比较好。但是隔离性不 佳,需要限制 token server 的总 QPS,防止影响应用本身。嵌入模式适合某个应用集群内部的流控。

 

 云上版本 AHAS Sentinel 提供开箱即用的全自动托管集群流控能力,无需手动指定/分配 token server 以及管理连接状 态,同时支持分钟小时级别流控、大流量低延时场景流控场景,同时支持 Istio/Envoy 场景的 Mesh 流控能力。

链接:http://note.youdao.com/noteshare?
id=fc7d801d1a8213fc4d1b691302e82a62&sub=675ACF22A59841AFAECD58A0A9B4E151

6.Sentinel规则持久化

  • Sentinel持久化模式
Sentinel 规则的推送有下面三种模式 :
原始模式 :API 将规则推送至客户端并直接更新到内存中,扩展写数据源
(WritableDataSource) 简单,无任何依赖
Pull 模式:扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规 则,这个规则中心可以是 RDBMS、文 件 等 简单,无任何依赖;规则持久化
Push 模式 :规则持久化;一致性;快速引入第三方依赖
6.1  原始模式
如果不做任何修改, Dashboard  的推送规则方式是通过  API  将规则推送至客户端并直接更
新到内存中:

这种做法的好处是简单,无依赖;坏处是应用重启规则就会消失,仅用于简单测试,不能

用于生产环境。
6.2  拉模式
pull  模式的数据源(如本地文件、 RDBMS  等)一般是可写入的。使用时需要在客户端注册
数据源:将对应的读数据源注册至对应的  RuleManager ,将写数据源注册至  transport 
WritableDataSourceRegistry 中。
6.3 推模式
生产环境下一般更常用的是  push  模式的数据源。对于  push  模式的数据源 , 如远程配置中心
ZooKeeper, Nacos, Apollo 等等),推送的操作不应由  Sentinel  客户端进行,而应该经控
制台统一进行管理,直接进行推送,数据源仅负责获取配置中心推送的配置并更新到本
地。因此推送规则正确做法应该是 配置中心控制台 /Sentinel  控制台  →  配置中心  → 
Sentinel  数据源  → Sentinel ,而不是经  Sentinel  数据源推送至配置中心。这样的流程就非
常清晰了

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

引入依赖
     <!--sentinel启动器-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!--基于Nacos配置中心控制台实现推送-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

nacos配置中心中配置流控规则
[
 {
 "resource": "/order/pay",
 "controlBehavior": 0,
 "count": 3.0,
 "grade": 1,
"limitApp": "default",
 "strategy": 0
 }
 ]

yml文件

 

server:
  port: 8070
spring:
  application:
    name: order-sentinel
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080
      web-context-unify: false  # 默认将调用链路收敛, 导致链路流控效果无效
      datasource:
        flow-rule:
          nacos:
            server-addr: 127.0.0.1:8848 # nacos地址
            username: nacos
            password: nacos
            dataId: order-sentinel-flow-rule
            rule-type: flow
OrderController .java
package com.wang.controller;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @BelongsProject: SpringCloudAlibabaLearn
 * @BelongsPackage: com.wang.controllter
 * @Author: wang fei
 * @CreateTime: 2023-01-16  16:48
 * @Description: TODO
 * @Version: 1.0
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @GetMapping("/pay")
//    @SentinelResource(value = "pay",blockHandler = "flowBlockHandler")
    public String pay(){
        return "success";
    }

    @GetMapping("/flowThread")
    @SentinelResource(value = "flowThread",blockHandler = "flowBlockHandler")
    public String flowThread() throws InterruptedException {
        TimeUnit.SECONDS.sleep(5);
        return "success";
    }

    //自定义流控处理返回方法
    public String flowBlockHandler(BlockException e){
        return "流控成功 ,阻塞服务";
    }


    @GetMapping("/add")
    public String add(){
        return "下单成功";
    }

    @GetMapping("/get")
    public String get(){
        return "查询成功";
    }


    /**
     * 热点规则,必须使用@SentinelResource
     * @param id
     * @return
     * @throws InterruptedException
     */
    @RequestMapping("/get/{id}")
    @SentinelResource(value = "getById",blockHandler = "HotBlockHandler")
    public String getById(@PathVariable("id") Integer id) throws InterruptedException {

        System.out.println("正常访问");
        return "正常访问";
    }

    public String HotBlockHandler(@PathVariable("id") Integer id,BlockException e) throws InterruptedException {

        return "热点异常处理";
    }
}

登录sentinel 

 

查看nacos配置的配置信息。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/176756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一篇读懂图神经网络

来源&#xff1a;投稿 作者&#xff1a;张宇 编辑&#xff1a;学姐 近年来&#xff0c;作为一项新兴的图数据学习技术&#xff0c;图神经网络&#xff08;GNN&#xff09;受到了非常广泛的关注&#xff0c;在各大顶级学术会议上&#xff0c;图神经网络相关的论文也占了相当可观…

https://app.diagrams.net/在线画图的一些技巧

最近工作需要,实践了在线画图的case, 下面就把使用心得记录一下: 关于diagrams 的一些小技巧: 登入的网页是:Flowchart Maker & Online Diagram Software 1: 利用group 的选项,这个可以整体移动,不用担心会漏掉一个: 就是选中一个图标,然后,看右边arrange 下面…

20230123使AIO-3568J开发板在Android12下永不休眠

20230123使AIO-3568J开发板在Android12下永不休眠 2023/1/23 13:59 1、 Z:\android12-rk3568-new\device\rockchip\common\device.mk # Bluetooth HAL PRODUCT_PACKAGES \ libbt-vendor \ android.hardware.bluetooth1.0-impl \ android.hardware.bluetooth1.0-se…

Hadoop基础之《(1)—大数据基本概念》

一、Hadoop 1、Hadoop大数据框架&#xff0c;处理分布式环境下数据存储和计算 2、Hadoop的HDFS处理存储 3、Hadoop的MapReduce处理计算 map让任务数据拆分到每一台去执行 reduce处理后的任务合并 4、Hive作用是在Hadoop上能够让用户来写SQL处理数据 Hive的执行引擎&#xff0c;…

深度学习TensorFlow—GPU2.4.0版环境配置,一文简单易懂详细大全,CUDA11.0、cuDNN8.0

深度学习TensorFlow—GPU2.4.0版环境配置&#xff0c;一文简单易懂详细大全&#xff0c;CUDA11.0、cuDNN8.0 前提&#xff1a;电脑拥有英伟达独立显卡!!!&#xff0c;并且安装了anaconda&#xff01;&#xff01;&#xff01; 前提&#xff1a;电脑拥有英伟达独立显卡!!!&…

vue事件车之兄弟组件之间传值

目录前言一&#xff0c;全局事件总线介绍1.1 原理介绍1.2 x需要满足的条件二&#xff0c;知识点的复习2.1 vc是什么2.2 vm管理vc如何体现2.3 原型2.4 上述知识的串联三&#xff0c;实现需求3.1 x的编写及讲解3.2 使用x四&#xff0c;标准写法4.1 写法改动4.2 销毁五 关键代码后…

兔年首文迎新春-Cesium橘子洲烟花礼赞

兔年新春今天是兔年大年初二&#xff0c;神州大地&#xff0c;在经历了疫情的三年后迎来开放的一个春节。大家都沉浸在欢乐幸福的春节气氛中。玉兔迎新春&#xff0c;祝福齐送到&#xff1a;白兔祝你身体安康&#xff0c;黑兔祝你薪水高涨&#xff0c;灰兔送你梦想如意&#xf…

Maven高级

Maven高级 1&#xff0c;分模块开发 1.1 分模块开发设计 (1)按照功能拆分 我们现在的项目都是在一个模块中&#xff0c;比如前面的SSM整合开发。虽然这样做功能也都实现了&#xff0c;但是也存在了一些问题&#xff0c;我们拿银行的项目为例来聊聊这个事。 网络没有那么发…

Java多线程03——等待唤醒机制(and阻塞队列实现)

目录1.等待唤醒机制1.ThreadDemo2.Desk3.Cook4.Foodie2.等待唤醒机制&#xff08;阻塞队列方式实现&#xff09;1.ThreadDemo022.Cook023.Foodie023.线程的状态1.等待唤醒机制 生产者和消费者 桌子上有食物&#xff0c;消费者吃&#xff0c;桌子上没有食物&#xff0c;消费者等…

ElasticSearch 索引模板 组件模板 组合模板详细使用介绍

索引模板_template 文章目录索引模板_templateTemplate 介绍索引模板Index Template参数说明创建一个索引模板 Index Template测试不存在的索引直接添加数据创建索引总结组合索引模板 Index Template 7.8版本之后引入创建基于组件模板的索引模板 Index Template创建组件模板模拟…

LeetCode103_ 103. 二叉树的锯齿形层序遍历

LeetCode103_ 103. 二叉树的锯齿形层序遍历 一、描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 示例 1&…

【头歌】顺序表的基本操作

第1关&#xff1a;顺序表的插入操作任务描述本关任务&#xff1a;编写顺序表的初始化、插入、遍历三个基本操作函数。相关知识顺序表的存储结构顺序表的存储结构可以借助于高级程序设计语言中的数组来表示&#xff0c;一维数组的下标与元素在线性表中的序号相对应。线性表的顺序…

YOLOv5/v7 引入 YOLOv8 的 C2f 模块

YOLOv8 项目地址&#xff1a;https://github.com/ultralytics/ultralytics YOLOv8 Ultralytics YOLOv8 是由 Ultralytics 开发的一个前沿的 SOTA 模型。它在以前成功的 YOLO 版本基础上&#xff0c;引入了新的功能和改进&#xff0c;进一步提升了其性能和灵活性。YOLOv8 基于快…

C 指针变量 取地址符的用法 *指针变量名的用法

文章目录IntroCode图示Intro C语言中有一类特殊的变量&#xff1a;指针变量(pointer variable)&#xff0c;用于存储某个变量的内存地址的值。 要打印指针变量一般用%p格式符&#xff0c;会打印出该指针变量的值&#xff0c;即一个内存值。 Code // Created by wuyujin1997 …

【Linux】进程概念一

进程概念一 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截止目前为止&#xff0c; 我们所认识的计算机&#xff0c;都是一个个的硬件组成 输入设备&#xff1a;包括键盘&#x…

如何使用JDBC操作数据库?JDBC API的使用详细解读

文章目录1. DriverManager1.1 注册驱动1.2 获取连接2. Connection2.1 获取执行sql的对象2.2 事务管理3. Statement4. ResultSet5. PreparedStatement5.1 sql注入问题5.2 preparedStatement 原理6. 总结Java编程基础教程系列1. DriverManager DriverManager &#xff0c;驱动管…

C++模板不支持分离编译的问题

目录前言分离编译模式普通函数的分离编译(正常)模板函数的分离编译(出错)分析解决方式拓展--extern关键字extern"C"extern变量extern模板--控制实例化前言 分离编译模式 一个项目如果有多个源文件.c组成&#xff0c;每个源文件单独编译&#xff0c;形成目标文件。最…

Kubernetes:分享一个可以展示资源视图的 K8s开源 Web/桌面 客户端工具 Octant

写在前面 博文内容主要为 Octant 介绍以及 桌面/Web 端的安装教程涉及 Linux/Windows 的安装。理解不足小伙伴帮忙指正 其实当你什么都经历了&#xff0c;会发现&#xff0c;人生无论你怎么精心策划都抵不过一场命运的安排。 Octant 不是仪表板&#xff0c;Octant 是一个带有仪…

数据结构 最短路径课设(源码+实验报告+视频讲解)(不要钱、用了自取)

XIAN TECHNOLOGICAL UNIVERSITY 课程设计报告 实验课程名称 算法与数据结构 专 业&#xff1a; 班 级&#xff1a; 姓 名&#xff1a; 学 号&#xff1a; 实验学时&#xff1a; 指导…

Linux Debian11安装QT6开发环境

从Qt5.14开始&#xff0c;官方不提供离线安装包&#xff0c;只提供源码包和在线安装器。但是清华为我们提供了快速的在线安装方式。 一.下载清华提供的在线安装器 在线安装器下载链接 二、给在线安装器文件赋予执行权限 三、配置镜像地址运行安装器 清华源&#xff1a; ./…