springboot + dubbo 整合Sentinel限流

news2024/11/29 18:29:46

一、前言

限流对一个生产环境的系统来说,具有重要的意义,限流的目的是为了保护系统中的某些核心业务资源不被瞬间的大并发流量冲垮而采取的一种措施,因此一个成熟的架构设计方案,限流也需要纳入到架构设计和规划中。

二、常用的限流解决方案

微服务经过多年的发展和沉淀,对于限流来说,也有了一些通用的解决方案,列举常用的供参考

  • sentinel,springcloud-alibaba技术栈下的一个组件,提供了不仅限流,dashboard等在内的灵活的功能;
  • guava,google提供的一款限流SDK,简单易用;
  • 网关限流,nginx,可在nginx中配置一定的限流规则对请求进行限流;
  • 利用限流算法思想的指导,如令牌桶、漏桶算法等自定义限流组件;

可参考之前一篇对于限流方案的总结:常用限流方案总结

三、dubbo中的限流

dubbo作为一款优秀的服务治理框架,在各大中小互联网公司都有使用,在微服务治理中,服务发布者使用dubbo可以发布服务出去,给平台中其他应用调用,使用起来很方便;

可以说所有的应用服务,一旦业务量上去了,应用服务要抗的压力也必然增加,对于服务提供方来说,高频大并发的调用,对于服务治理来说绝对是一项挑战,因此在这个问题上,dubbo官方在dubbo出厂的时候就根据可能遇到的情况提供了一系列配套的服务限流、降级、熔断等策略,可以参考官方的说明,小编这里之前也做了一些总结:dubbo服务限流与降级总结

dubbo官方限流方案的问题

官方提供的限流方案,个人认为其中一个比较大的问题在于运用起来不够灵活,举例来说,看如下的一种限流策略,即线程池限流的配置

<dubbo:service interface="com.congge.service.UserService" ref="userService" 
                   executes="10"
                   cluster="failover"
                   retries="2"
        />

在实际操作的时候,会发现这个 executes的参数值设置多大合适呢?其实很难断定,因为具体到某个服务接口来说,这个跟大环境下接口被调用的频率,接口响应的速度,服务器配置等诸多因素有关,况且来说,默认情况下,dubbo的线程池数量为200个,加上这个前提,对某个服务接口来说,设置这个参数就更难了;

于是,我们思考,从使用的灵活性上面来说,是否有更好的解决办法呢?

四、实验前置准备

为了更好的模拟出实验效果,先预先搭建一个基于springboot整合dubbo的聚合模块工程demo

1、项目结构如下

2、各个模块pom依赖

根pom模块依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.7.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

api模块依赖

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

provider/consumer模块依赖

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.0</version>
        </dependency>

        <dependency>
            <groupId>com.congge</groupId>
            <artifactId>api-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.13.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>2.13.0</version>
        </dependency>

3、api模块主要业务

实体类

@Data
public class User implements Serializable {

    private String id;
    private String userName;
    private String address;

}

服务接口

public interface UserService {

    User getById(String userId);

}

4、provider模块主要业务

provider模块主要提供一个实现api模块服务接口的实现类

import com.congge.entity.User;
import com.congge.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public User getById(String userId) {
        User user = new User();
        user.setId(userId);
        user.setAddress("杭州");
        user.setUserName("zhangsan");
        return user;
    }
}

提供dubbo相关的一个xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://code.alibabatech.com/schema/dubbo
                           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="service-provider"/>

    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

    <dubbo:protocol name="dubbo" port="20880"/>

    <dubbo:service interface="com.congge.service.UserService"
                   ref="userServiceImpl" version="1.0.0"/>

</beans>

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource("classpath:spring/providers.xml")
@SpringBootApplication
public class ProviderApp {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApp.class,args);
    }

}

5、consumer 模块主要业务

提供一个web接口,然后调用provider中的dubbo服务接口进行调用

import com.congge.entity.User;
import com.congge.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/get")
    public User getUser(){
        return userService.getById("1");
    }

}

提供dubbo相关的一个xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://code.alibabatech.com/schema/dubbo
                           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="service-consumer"/>

    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

    <dubbo:reference id="userService" interface="com.congge.service.UserService"
                   version="1.0.0"/>

</beans>

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource("classpath:spring/consumers.xml")
@SpringBootApplication
public class ConsumerApp {

    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class,args);
    }

}

6、整合测试

启动本地zookeeper服务,再分别启动provider和consumer的服务,通过浏览器调用一下,看到下面的结果后,说明demo搭建完成;

五、通用限流方案之 —— guava

guava是谷歌提供的一款限流SDK组件,使用起来简单灵活,既可以用作web接口的限流,也可以用作dubbo接口的限流,本文以dubbo接口限流为例进行说明,在provider模块新增如下依赖:

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.0.1-jre</version>
        </dependency>

使用guava对dubbo接口进行限流思路

这里提供两种思路供给参考,前提是基于自定义注解

思路1:

  • 自定义限流注解;
  • 在需要进行限流的dubbo实现类的方法级别上添加自定义注解(对provider来说的);
  • 通过一个aop的类,配置切点表达式为自定义注解,在环绕通知(around)中进行限流;

思路2:

  • 自定义限流注解;
  • 在需要进行限流的dubbo实现类的方法级别上添加自定义注解(对provider来说的);
  • 通过前置aop通知或者工程启动加载的时读取自定义限流注解的方法(资源),并配置到容器;
  • 通过dubbo的Filter逻辑中匹配特定的服务接口进行限流;

思路1和思路2都可以落地实现,看个人的需求,本文以思路2为例做一下代码层面的实现

1、自定义限流注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LxRateLimit {

    //资源名称
    String name() default "默认资源";

    //限制每秒访问次数,默认为3次
    double perSecond() default 1;

    /**
     * 限流Key类型
     * 自定义根据业务唯一码来限制需要在请求参数中添加 String limitKeyValue
     */
    LimitKeyTypeEnum limitKeyType() default LimitKeyTypeEnum.IPADDR;

}

2、限流工具类

该类用于提供一个全局使用的限流工具类,即被限流的资源使用到的工具类;

public class LxRateLimitUtil {

    private static int PER_SECOND_COUNT = 2;

    public static LoadingCache<String, RateLimiter> caches = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(1, TimeUnit.DAYS)
            .build(new CacheLoader<String, RateLimiter>() {
                @Override
                public RateLimiter load(String key) throws Exception {
                    // 新的IP初始化 (限流每秒两个令牌响应)
                    return RateLimiter.create(PER_SECOND_COUNT);
                }
            });

}

3、自定义加载自定义限流注解的类

在实际开发中,扫描的包路径可以根据自己的实际情况指定;

import org.reflections.Reflections;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class InitTargetRatelimitClassMethods {

    public static final Map<String,List<String>> rateLimitMethodMap = new ConcurrentHashMap<>();

    private static final String SCAN_PACKAGES = "com.congge.service.impl";

    @PostConstruct
    public void initLoadConfig(){
        handleInitRateLimitAnnoation();
    }

    public void handleInitRateLimitAnnoation() {
        Reflections reflections = new Reflections(SCAN_PACKAGES);
        Set<Class<?>> restController = reflections.getTypesAnnotatedWith(Service.class);
        restController.forEach(aClass -> {
            String className = aClass.getName();
            List<String> fullNames = Arrays.asList(className.split("\\."));
            String mapKey = fullNames.get(fullNames.size()-1);
            Method[] methods = aClass.getDeclaredMethods();
            List<String> targetMethodNames = new ArrayList<>();
            for (int i = 0; i < methods.length; i++){
                if(methods[i].isAnnotationPresent(LxRateLimit.class)){
                    targetMethodNames.add(methods[i].getName());
                }
            }
            if(!CollectionUtils.isEmpty(targetMethodNames)){
                rateLimitMethodMap.put(mapKey,targetMethodNames);
            }
        });
    }
}

集合中的数据结构如下:

 4、自定义dubbofilter,在自定义filter中进行限流

import com.congge.ratelimit.InitTargetRatelimitClassMethods;
import com.congge.ratelimit.LxRateLimitUtil;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.*;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

@Slf4j
public class DubboAccessFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Class<?> anInterface = invoker.getInterface();
        String methodName = invocation.getMethodName();
        String remoteAddress = RpcContext.getContext().getRemoteAddressString();
        Result result = invoker.invoke(invocation);

        doApiAccessLimit(anInterface, methodName, remoteAddress);

        return result;
    }

    private void doApiAccessLimit(Class<?> anInterface, String methodName, String remoteAddress) {
        String simpleClassName = anInterface.getName();
        List<String> fullNames = Arrays.asList(simpleClassName.split("\\."));
        String mapKey = fullNames.get(fullNames.size() - 1) + "Impl";
        Map<String, List<String>> rateLimitMethodMap = InitTargetRatelimitClassMethods.rateLimitMethodMap;
        if (rateLimitMethodMap.containsKey(mapKey) && rateLimitMethodMap.get(mapKey).contains(methodName)) {
            String cacheKey = remoteAddress + ":" + simpleClassName + ":" + methodName;
            RateLimiter rateLimiter = null;
            try {
                rateLimiter = LxRateLimitUtil.caches.get(cacheKey);
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            if (!rateLimiter.tryAcquire()) {
                throw new RuntimeException("【被限流了】您调用的速度太快了,请慢点操作");
            }
        }
    }

}

然后分别在配置文件中将上述的自定义filter添加到dubbo的spi配置文件中

最好将该filter配置到xml文件中

<dubbo:provider loadbalance="leastactive" filter="dubboAccessFilter" />

5、限流业务测试

provider端的工作准备完毕,为了模拟出效果,我们可以手动将QPS的值调到1个,再对消费端的接口做如下改造

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/get")
    public User getUser(){
        for(int i=0;i<10;i++){
            userService.getById("1");
        }
        return userService.getById("1");
    }

}

再次启动provider和consumer端的服务,浏览器做如下的调用,观察控制台输出效果

当然在实际运用中,消费端可以捕获异常,然后以更友好的方式将结果展现给客户端,对这种实现方式可以优化改进的地方如下:

  • 扫描的包路径可以写到配置文件,或者用其他的方式定义;
  • 每秒限制的访问次数的值设定可以从配置文件读取,或者用其他方式定义;

六、通用限流方案之 —— Sentinel

sentinel 简介

Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。

sentinel属于springcloud-alibaba微服务体系下的一款用于限流,熔断,降级等一体的组件,还提供了dashboard用于对接口资源使用的精准控制,功能强大,简单已用,既可以在整个springcloud-alibaba微服务架构中使用,也可以单独拿来使用,关于单独限流使用时可以参考:java使用sentinel

dubbo整合sentinel限流思路

  • 自定义全局的dubbo filter,用于拦截dubbo api被调用时的信息;
  • 使用sentinel自定义限流规则;
  • 在自定义Filter中加载限流规则,并对dubbo api进行规则校验和限流;

1、自定义dubbo filter

在这段代码中,基本上完成了整合思路中的所有步骤,其实用的就是Sentinel的原生的api对资源进行的限流;

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.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog;
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 com.congge.sentinel.fallback.DubboFallbackRegistry;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.ArrayList;
import java.util.List;

@Activate(group = "provider")
public class SentinelDubboProviderFilter implements Filter {

    public SentinelDubboProviderFilter() {
        RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
    }

    public static void initRule(String resourceName) {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource(resourceName);
        //使用QPS的方式
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String resourceName = DubboUtils.getResourceName(invoker, invocation);
        String interfaceName = invoker.getInterface().getName();

        //加载定义的限流规则
        initRule(resourceName);

        // Get origin caller.
        String application = DubboUtils.getApplication(invocation, "");
        Entry interfaceEntry = null;
        Entry methodEntry = null;
        try {
            ContextUtil.enter(resourceName, application);
            interfaceEntry = SphU.entry(interfaceName, EntryType.IN);
            methodEntry = SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());

            Result result = invoker.invoke(invocation);
            if (result.hasException()) {
                Throwable e = result.getException();
                // Record common exception.
                Tracer.traceEntry(e, interfaceEntry);
                Tracer.traceEntry(e, methodEntry);
            }
            return result;
        } catch (BlockException e) {
            return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
        } catch (RpcException e) {
            Tracer.traceEntry(e, interfaceEntry);
            Tracer.traceEntry(e, methodEntry);
            throw e;
        } finally {
            if (methodEntry != null) {
                methodEntry.exit(1, invocation.getArguments());
            }
            if (interfaceEntry != null) {
                interfaceEntry.exit();
            }
            ContextUtil.exit();
        }
    }

}

DubboUtils

import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;

public class DubboUtils {


    public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication";

    public static String getApplication(Invocation invocation, String defaultValue) {
        if (invocation == null || invocation.getAttachments() == null) {
            throw new IllegalArgumentException("Bad invocation instance");
        }
        return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
    }

    public static String getResourceName(Invoker<?> invoker, Invocation invocation) {
        StringBuilder buf = new StringBuilder(64);
        buf.append(invoker.getInterface().getName())
                .append(":")
                .append(invocation.getMethodName())
                .append("(");
        boolean isFirst = true;
        for (Class<?> clazz : invocation.getParameterTypes()) {
            if (!isFirst) {
                buf.append(",");
            }
            buf.append(clazz.getName());
            isFirst = false;
        }
        buf.append(")");
        return buf.toString();
    }

    private DubboUtils() {}
}

2、将自定义的filter配置到spi以及xml文件

<dubbo:provider loadbalance="leastactive" filter="providerFilter" />

3、整合测试

为了模拟出效果,将规则方法在的QPS调整为1,然后分别启动provider和consumer服务,浏览器做如下调用,观察到下面的效果,说明接口被限流了;

关于该实现方案的优化改进

尽管也能实现限流,但发现这个限流是针对所有的dubbo api,显然这个范围有点大了,可以在filter 中,对限流的接口来源做一下缩小,仍然可以利用第一个思路中,针对特定的那些添加了限流注解的api接口进行限流即可,有兴趣的同学可自行研究下,限于篇幅,这里就不再过多赘述了。

需要源码的同学可前往下载:源码

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

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

相关文章

【linux】进程控制详述

进程控制一、进程创建1.1 fork 的补充1.2 写时拷贝1.3 fork 的使用场景二、进程终止2.1 退出码2.2 退出情况2.3 退出方式三、进程等待3.1 进程等待的原因3.2 进程等待的方法3.2.1 status 位图结构3.2.2 阻塞等待和非阻塞等待四、进程程序替换4.1 程序替换的原理4.2 程序替换函数…

[Spring]第五篇:AOP面向切面编程概念

AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现 日志处理,权限控制,性能检测,事务控制等 AOP实现的原理就是动态代理,在有接口的情况下,使用JDK动态代理,在没有接口的情况下使用cglib动态代理 为Dao层所有的add方法添加一个性能记录功…

jupyter中配置多种虚拟环境

Microsoft Windows [版本 10.0.19044.2251] © Microsoft Corporation。保留所有权利。 C:\Users\ThinkStation>conda activate pytorch(pytorch) C:\Users\ThinkStation>conda install ipykernelCollecting package metadata (current_repodata.json): done Solvin…

CAS:956748-40-6|Biotin-PEG-Azide|Biotin-PEG-N3|生物素PEG叠氮供应

由抗生物素或链霉亲和素结合PEG衍生物化学试剂Biotin-PEG-Azide&#xff08;Biotin-PEG-N3&#xff09;&#xff0c;其中文名为生物素-聚乙二醇-叠氮&#xff0c;它所属分类为Azide PEG Biotin PEG。CAS编号为956748-40-6。 该PEG试剂的分子量均可定制&#xff0c;有&#xff…

POSIX线程(一)

目录 一、前言 二、What Is a Thread? 三、Advantages and Drawbacks of Threads 四、A First Threads Program 1、A Simple Threaded Program&#xff08;thread1.c&#xff09; 五、Simultaneous Execution 1、Simultaneous Execution of Two Threads&#xff08;thre…

Caffeine 源码、架构、原理(史上最全,10W超级字长文)

文章很长&#xff0c;而且持续更新&#xff0c;建议收藏起来&#xff0c;慢慢读&#xff01;疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 &#xff1a; 免费赠送 :《尼恩Java面试宝典》 持续更新 史上最全 面试必备 2000页 面试必备 大厂必备 涨薪必备 免费赠送 经典…

IbBBX24–IbTOE3–IbPRX17模块通过清除甘薯中的活性氧来增强甘薯对非生物胁迫耐受性

文章信息 题目&#xff1a;The IbBBX24–IbTOE3–IbPRX17 module enhances abiotic stress tolerance by scavenging reactive oxygen species in sweet potato 刊名&#xff1a;New Phytologist 作者&#xff1a;Huan Zhang&#xff0c;Shaozhen He et al. 单位&#xff1…

终于有阿里p8进行了大汇总(Redis+JVM+MySQL+Spring)还有面试题解全在这里了!

Redis特性 Redis是一直基于键值对的NoSQL数据库&#xff1b; Redis支持5种主要数据结构&#xff1a;string、hash、list、set、zset以及bitmaps、hyperLoglog、GEO等特化的数据结构&#xff1b; Redis是内存数据库&#xff0c;因此它有足够好的读写性能&#xff1b; Redis支持…

[附源码]Python计算机毕业设计SSM临港新片区招商引资项目管理系统的设计与实现(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

jmeter性能测试使用梯度测试的方法

JMeterPlugins-Standard.jar下载地址&#xff1a;Download :: JMeter-Plugins.org 项目验收&#xff0c;用jmeter做并发测试&#xff0c;测试单位不通过&#xff0c;我们可以 仔细一问测试单位&#xff0c;嘿&#xff0c;jmeter写个并发数做测试不算并发测试&#xff0c;要做…

Vue3 —— 利用vite创建一个vue3项目

前言 本文主要讲解如何利用vitevue创建第一个项目以及vue3的基础知识点 一、创建一个vue3项目 这里我们主要介绍如何利用 vitevue3创建项目 1.有关vite Vite&#xff08;法语意为 "快速的"&#xff0c;发音 /vit/&#xff0c;发音同 "veet"&#xff09;是…

Editor扩展常用API

如图 效果图 代码&#xff1a; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class Mybianyi : EditorWindow { string PasswordField ""; string m_textArea ""; float …

力扣hot100——第1天:1两数之和、2两数相加、3无重复字符的最长子串

文章目录1.1两数之和【代码随想录已刷】2.2两数相加2.1.题目2.2.解答3.3无重复字符的最长子串3.1.题目3.2.解答1.1两数之和【代码随想录已刷】 参考&#xff1a;力扣题目链接&#xff1b;自己的博客题解 2.2两数相加 参考&#xff1a;力扣题目链接&#xff1b;参考题解 2.1…

【附源码】计算机毕业设计JAVA众筹平台网站

【附源码】计算机毕业设计JAVA众筹平台网站 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybati…

什么是 游戏引擎 ?各个主流引擎的区别

在之前的元宇宙中有提及到游戏主播这个行业&#xff0c;那对应的就会有游戏开发者&#xff0c;对于开发最快捷的方式当然是使用游戏引擎进行开发了呀&#xff0c;那接下来我们就来说说这方面的内容吧 &#xff01; 本篇文章主要讲解&#xff0c;游戏开发中常用的5个游戏引擎及其…

C++——二叉搜索树

目录 二叉搜索树 二叉搜索树实现 非递归插入|非递归查找 删除 推导阶段 非递归删除代码 递归查找 递归插入 递归删除 析构函数 拷贝构造 赋值重载 完整代码 二叉搜索树的应用 Key/Value模型 二叉搜索树 二叉搜索树又称二叉排序树&#xff0c;它或者是一…

第3部分 静态路由

目录 3.1 静态路由与默认路由 3.1.1 静态路由介绍 3.1.2 默认路由介绍 3.2 静态路由命令汇总 转发数据包是路由器的最主要功能。路由器转发数据包时需要查找路由表&#xff0c;管理员可以通过手工的方法在路由器中直接配置路由表&#xff0c;这就是静态路由。虽然静态路由不…

Dialog的IDE搭建systermView的方法步骤(DA1469X)

1.背景 SystemView 是一个可以在线调试嵌入式系统的工具&#xff0c;它可以分析有哪些中断、任务执行了&#xff0c;以及这些中断、任务执行的先后关系。还可以查看一些内核对象持有和释放的时间点&#xff0c;比如信号量、互斥量、事件、消息队列等。这在开发和处理具有多个线…

大规模 MIMO 检测的近似消息传递 (AMP)附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

神经架构搜索的综合调查:挑战和解决方案(一)

神经架构搜索的综合调查&#xff1a;挑战和解决方案 paper题目&#xff1a;A Comprehensive Survey of Neural Architecture Search: Challenges and Solutions paper是西北大学发表在ACM Computing Surveys 2021的工作 paper地址&#xff1a;链接 ABSTRACT 深度学习以其强大的…