【微服务】SpringBoot整合Resilience4j使用详解

目录

一、前言

二、熔断器出现背景

2.1 几个核心概念

2.1.1 熔断

2.1.2 限流

2.1.3 降级

2.2 为什么会出现熔断器

2.3 断路器介绍

2.3.1 断路器原理

三、Resilience4j介绍

3.1 Resilience4j概述

3.1.1 Resilience4j是什么

3.1.2 Resilience4j功能特性

3.2 Resilience4j核心组件

3.2.1 Bulkhead

3.3 Resilience4j状态机

3.4 几种服务熔断组件对比

四、springboot整合Resilience4j

4.1 集成过程

4.1.1 导入依赖

4.1.2 添加配置文件

4.1.3 添加配置类

4.1.4 测试业务

4.1.5 接口测试

4.2 参数解读补充

4.2.1 circuitbreaker常用配置参数

4.2.2 ratelimiter常用配置参数

4.2.3 retry常用配置参数

五、写在文末


一、前言

当微服务数量越来越多之后,保证微服务自身的高可用性就变得异常重要了,高可用也是服务治理中的重要一环,在保障高可用解决方案中,服务的熔断、限流与降级可以说是其中非常重要的一种手段。

在Java的微服务生态中,对于服务保护组件,像springcloud的Hystrix,springcloud alibaba的Sentinel,以及当Hystrix停更之后官方推荐使用的Resilience4j。

二、熔断器出现背景

很多同学在学习微服务限流组件时,容易对熔断、限流、降级这几个概念混淆,甚至在什么场景下使用哪种保护措施也缺乏深一定的认识,这一点有必要搞清楚。

2.1 几个核心概念

关于熔断、限流、降级这几个关键词,做一下详细的解释。

2.1.1 熔断

熔断,即Circuit Breaker,熔断是一种故障处理机制,用于防止故障在系统中蔓延。

当一个服务出现故障时,熔断器会立即中断对该服务的请求,防止服务的故障传播到其他的组件。通过熔断机制,系统可以快速发现问题并做出相应的处理,避免系统整体崩溃。

2.1.2 限流

Rate Limiting,限流是一种控制请求流量的机制,用于保护系统免受过载的影响。

通过限制请求的频率或数量,可以确保系统在处理请求时不会超出其承受范围,限流可有效地平滑系统的负载,并防止突发流量对系统的影响。

2.1.3 降级

Degradation,降级是一种在系统负载过高或出现故障时,临时关闭部分功能或服务,以保证系统整体业务的可用性和稳定性机制。

通过降级,可以确保系统在面临异常情况时依然能够正常运行,而不是完全崩溃。降级可以根据不同情况选择关闭不同的服务或功能,以最大程度地保障系统的核心功能。

在这几个概念对应的微服务治理中,服务熔断是基础,同时也是保护系统受到意外冲击最基本也最有效的措施。

2.2 为什么会出现熔断器

在微服务开发中,当微服务数量越来越多的时候,服务之间的调用链路也越来越复杂,当某个服务调用另一个服务时,如果服务提供者出现网络超时或其他故障,过多的请求这时候都打来时,不仅会造成服务提供者的资源被占用过多出现阻塞,同时服务调用者也会出现堆积最终造成无法正常提供服务。

在这种情况下,就需要一种保护机制,通过实现调用熔断,以达到保护服务调用者的目的。如下图,A服务调用B服务,B服务调用C服务,当C服务出现故障或超时时,大量A服务调用请求导致B服务大量超时,最终导致B服务出现故障,这种级联的故障蔓延,最终会导致起始处的服务被冲垮。

2.3 断路器介绍

断路器,也叫熔断器,英文:CircuitBreaker,CircuitBreaker的目的是保护分布式系统免受故障和异常,提高系统的可用性和健壮性。

具体来说,当一个组件或服务出现故障时,CircuitBreaker会迅速切换到开放OPEN状态(保险丝跳闸断电),阻止请求发送到该组件或服务从而避免更多的请求发送到该组件或服务。这可以减少对该组件或服务的负载,防止该组件或服务进一步崩溃,并使整个系统能继续正常运行。同时,CircuitBreaker还可以提高系统的可用性和健壮性,因为它可以在分布式系统的各个组件之间自动切换,从而避免单点故障的问题。

2.3.1 断路器原理

CircuitBreaker的核心原理如下图:

我们可以用状态机来实现 Circuit Breaker,它有以下三种状态:

  • 关闭(Closed)状态;

    • 默认情况下Circuit Breaker是关闭的,此时允许操作执行;

    • Circuit Breaker内部记录着最近失败的次数,如果对应的操作执行失败,次数就会续一次;

    • 如果在某个时间段内,失败次数(或者失败比率)达到阈值,Circuit Breaker会转换到开启(Open)状态;

    • 在开启状态中,CircuitBreaker会启用一个超时计时器,设这个计时器的目的是给集群相应的时间来恢复故障。当计时器时间到的时候,Circuit Breaker会转换到半开启(Half-Open)状态。

  • 开启(Open);

    • 在此状态下,执行对应的操作将会立即失败并且立即抛出异常。

  • 半开启(Half-Open);

    • 在此状态下,Circuit Breaker 会允许执行一定数量的操作,如果所有操作全部成功,Circuit Breaker就会假定故障已经恢复,它就会转换到关闭状态,并且重置失败次数;

    • 如果其中 任意一次 操作失败了,Circuit Breaker就会认为故障仍然存在,所以它会转换到开启状态并再次开启计时器(再给系统一些时间使其从失败中恢复)。

在实际执行过程中,几个状态的切换流程如下图:

三、Resilience4j介绍

3.1 Resilience4j概述

上面了详细解了CircuitBreaker的原理,可以认为CircuitBreaker定义了一套业务实现规则或规范,而具体的实现组件,则是Resilience4j,即CircuitBreaker只是一套规范接口,落地实现者是Resilience4j。

git代码:https://github.com/resilience4j/resilience4j

文档地址:https://resilience4j.readme.io/

3.1.1 Resilience4j是什么

Resilience4j是一款轻量级,易于使用的容错库,其灵感来自于Netflix Hystrix,但是专为Java 8和函数式编程而设计。轻量级,因为库只使用了Vavr,它没有任何其他外部依赖下。相比之下,Netflix Hystrix对Archaius具有编译依赖性,Archaius具有更多的外部库依赖性。

  • Resilience4j是一个轻量级、易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计。

  • Resilience4j提供高阶函数(decorators)来增强任何功能接口、lambda表达式或方法引用,包括断路器、速率限制器、重试或舱壁。可以在任何函数接口、lambda表达式或方法引用上使用多个装饰器。

  • circuitbreaker组件实现了断路器功能,是基于内存的断路器,采用ConcurrentHashMap来实现。

3.1.2 Resilience4j功能特性

  1. 断路器(Circuit Breaker):在服务出现故障时自动熔断,防止请求继续失败导致雪崩效应。

  2. 限流(Rate Limiter):限制请求的并发数或速率,防止系统被过载。

  3. 重试(Retry):在请求失败时自动重试一定次数,增加系统的可靠性。

  4. 超时(Timeout):设置请求的最大执行时间,防止请求长时间阻塞。

  5. Bulkhead:通过限制同时执行的请求数量,保护系统的部分资源不被耗尽。

使用Resilience4j可以通过简单的注解或者编程方式来实现以上功能,从而保护应用程序免受故障的影响,帮助应用程序在面对故障和不稳定性时保持稳定性和可靠性。

3.2 Resilience4j核心组件

要使用Resilience4j,不需要引入所有依赖,只需要选择你需要的,Resilience4j提供了以下的核心模块和拓展模块:

组件名称功能
resilience4j-circuitbreakerCircuit breaking(熔断器)
resilience4j-ratelimiterRate limiting(限流器)
resilience4j-bulkheadBulkheading(隔离器)--依赖隔离&负载保护
resilience4j-retryAutomatic retrying (sync and async)(重试、同步&异步)
resilience4j-cacheResult caching(缓存)
resilience4j-timelimiterTimeout handling(超时处理)

3.2.1 Bulkhead

Bulkhead,即并发控制器(舱壁,Bulkhead),是用来控制并行(parallel)调用的次数。Resilience4j提供了两种舱壁模式的实现,可用于限制并发执行的次数:

  • SemaphoreBulkhead(信号量舱壁,默认),基于Java并发库中的Semaphore实现;

  • FixedThreadPoolBulkhead(固定 线程池舱壁),它使用一个有界队列和一个固定线程池。

由于基于信号量的Bulkhead能很好地在多线程和I/O模型下工作,所以选择介绍基于信号量的Bulkhead的使用。

3.3 Resilience4j状态机

Resilience4j是CircuitBreaker的具体实现,因此具备CircuitBreaker涉及到熔断的所有状态,在Resilience4j中,共有6种状态,在实际工作中就是在这6种状态之间转换。

  • CLOSED: 关闭状态,代表正常情况下的状态,允许所有请求通过,能通过状态转换为OPEN;

  • HALF_OPEN: 半开状态,即允许一部分请求通过,能通过状态转换为CLOSED和OPEN;

  • OPEN: 熔断状态,即不允许请求通过,能通过状态转为为HALF_OPEN;

  • DISABLED: 禁用状态,即允许所有请求通过,出现失败率达到给定的阈值也不会熔断,不会发生状态转换。

  • METRICS_ONLY: 和DISABLED状态一样,也允许所有请求通过不会发生熔断,但是会记录失败率等信息,不会发生状态转换。

  • FORCED_OPEN: 与DISABLED状态正好相反,启用CircuitBreaker,但是不允许任何请求通过,不会发生状态转换。

下面再对closed、open和half_open 这三种状态的切换做下补充:

  • closed -> open : 关闭状态到熔断状态,当失败的调用率(比如超时、异常等)默认50%,达到一定的阈值服务转为open状态,在open状态下,所有的请求都被拦截;

  • open-> half_open: 当经过一定的时间后,CircubitBreaker中默认为60s服务调用者允许一定的请求到达服务提供者;

  • half_open -> open: 当half_open状态的调用失败率超过给定的阈值,转为open状态;

  • half_open -> closed: 失败率低于给定的阈值则默认转换为closed状态;

3.4 几种服务熔断组件对比

常用的服务熔断,服务降级框架主要有Spring Cloud Hystrix、Spring Cloud Alibaba Sentinel、resilience4j,下面通过几个维度综合对比一下各自的特点,便于加深对各种组件的认识。

对比项SentinelHystrixResilience4j
开源Apache-2.0 licenseApache-2.0 licenseApache-2.0 license
更新更新频繁(latest:2.1.1, Aug 8th 2022)已停更更新较慢(latest:1.7.1, Jun 25th 2021)
特点轻量级,核心库无多余依赖,性能损耗小---基于Java8和函数式编程,轻量级容错库,无多余依赖
隔离策略信号量隔离信号量/线程池隔离信号量隔离
熔断降级策略异常比率/响应时间/异常数异常比率异常比率/响应时间
控制台实时监控、机器发现、规则管理等能力。提供监控查看不提供
系统自适应支持,结合应用的机器负载、CPU 使用率,整体平均响应时间、入口 QPS 和 并发线程数等维度进行限流操做不支持不支持
基于注解的支持支持支持支持
限流基于QPS、调用关系的限流有限的支持支持简单的Rtate Limiter模式

系统自适应

结合应用的机器负载、CPU 使用率,整体平均响应时间、入口QPS和并发线程数等几个维度的监控指标从而决定是否调用进行限流操做

四、springboot整合Resilience4j

上面详细介绍了Resilience4j的理论,接下来通过案例来看下如何在代码中集成并使用Resilience4j。

4.1 集成过程

4.1.1 导入依赖

Resilience4j与springboot整合提供了两个版本的依赖包,springboot2的版本和springboot3的版本,其中sptingboot2的版本java8就可以支持,本文选择sptingboot2的版本依赖。

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

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

        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-all</artifactId>
            <version>1.7.0</version>
        </dependency>

        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>1.7.0</version>
        </dependency>

4.1.2 添加配置文件

基本上Resilience4j的核心功能都可以在配置文件中通过官方提供的各种配置去完成,参考下面的配置信息,结合里面的注释加深对配置项的理解。

resilience4j.circuitbreaker:
  instances:
    backendA:
      registerHealthIndicator: true   # 是否启用健康检查
      slidingWindowSize: 10       #用于计算失败率的滑动窗口大小为10,即最近10次调用失败的情况会被考虑进去
      permittedNumberOfCallsInHalfOpenState: 3 # 断路器半开时允许最大的请求次数
      slidingWindowType: TIME_BASED #配置用于在CircuitBreaker关闭时记录调用结果的滑动窗口类型。 滑动窗口可以是基于计数或基于时间的。
      minimumNumberOfCalls: 5          # 熔断器开始计算失败率之前,至少需要的调用次数为5次
      waitDurationInOpenState: 5s      # 断路器打开后,尝试等待5秒进入半开状态
      failureRateThreshold: 20          # 当失败率达到20%时,断路器会打开,组织进一步的调用
      eventConsumerBufferSize: 10   #用于存储断路器相关事件的缓冲区大小为10,这些事件可用于被监控

#重试策略相关的配置
resilience4j.retry:
  instances:
    backendA:
      maxAttempts: 3              #最大重试次数
      waitDuration: 2s            #每次重试的时候间隔的等待时间
      enableExponentialBackoff: true
      exponentialBackoffMultiplier: 2
      retryExceptions:
        - java.lang.Exception

resilience4j.bulkhead:
  instances:
    backendA:
      maxConcurrentCalls: 10

resilience4j.thread-pool-bulkhead:
  instances:
    backendC:
      maxThreadPoolSize: 11      #配置最大线程池大小
      coreThreadPoolSize: 1     #配置核心线程池大小
      queueCapacity: 1          #配置队列的容量

#限流的配置
resilience4j.ratelimiter:
  instances:
    backendA:               # 限流器的名字
      limitForPeriod: 1     # 一个限制周期内可访问次数
      limitRefreshPeriod: 1s    # 限制周期,每个周期之后,速率限制器将重置回limitForPeriod值
      timeoutDuration: 10ms     # 线程等待允许执行时间
      registerHealthIndicator: true
      eventConsumerBufferSize: 100

server:
  port: 8081

4.1.3 添加配置类

自定义配置类,将配置文件中配置的每一种熔断器对应的项注册到spring的bean容器中,比如在配置文件中有一个实例名称为backendA;

import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigCustomizer;
import io.github.resilience4j.common.ratelimiter.configuration.RateLimiterConfigCustomizer;
import org.springframework.context.annotation.Bean;

public class ResilienceConfig {

    @Bean
    public CircuitBreakerConfigCustomizer circuitBreakerConfigCustomizer() {
        return CircuitBreakerConfigCustomizer
                .of("backendA", builder -> builder.slidingWindowSize(10));
    }

    @Bean
    public RateLimiterConfigCustomizer rateLimiterConfigCustomizer() {
        return RateLimiterConfigCustomizer
                .of("backendA", builder -> builder.limitForPeriod(1));
    }
}

4.1.4 测试业务

在这里我们测试两个场景,第一个场景为重试,第二个场景为限流,对于下面的两个方法做如下的补充说明:

  • @CircuitBreaker,该注解里面有两个属性,name和fallbackMethod;

    • name,即配置文件中配置的那个实例名称;

    • fallbackMethod,出现错误时使用哪个降级方法;

  • @Retry,与@CircuitBreaker注解相同,也是两个相同的属性,具有类似的含义;

    • 一般来说,@CircuitBreaker注解和@Retry可以搭配使用,也可以单独使用@Retry;

import com.congge.entity.User;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.retry.annotation.Retry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Objects;

@Service
public class UserService {

    private Logger logger = LoggerFactory.getLogger(UserService.class);

    @CircuitBreaker(name = "backendA")
    @Retry(name = "backendA")
//    @RateLimiter(name = "backendA", fallbackMethod = "fallback")
    public User retry() {
        logger.info("backendA findUser");
        String res = new RestTemplate().getForObject("http://localhost:8088", String.class);
        if (Objects.nonNull(res)) {
            return new User("remote user", 18, "Man");
        }
        return new User("default user", 22, "Woman");
    }

    @CircuitBreaker(name = "backendA")
    @RateLimiter(name = "backendA",fallbackMethod = "fallback")
    public User limit() {
        return new User("remote user", 18, "Man");
    }

    public User fallback(Throwable e) {
        return new User("降级的用户", 18, "D");
    }
}

控制器类用于测试使用

@RestController
public class UserController {

    @Resource
    private UserService userService;

    @GetMapping("/retry")
    public User retry() {
        return userService.retry();
    }

    @GetMapping("/limit")
    public User limit() {
        return userService.limit();
    }
}

4.1.5 接口测试

重试功能测试,在第一个方法中,远程调用一个不存在的链接,理论上会失败,按照参数配置,调用失败后,会重试3次,如果3次之后仍然失败,则接口抛出异常(或使用降级的方法的返回结果)。

重试接口测试

接口经过一段时间之后失败

同时查看控制台日志输出,不难发现,重试了3次,3次时候输出了异常的堆栈信息。

限流接口测试

调用限流接口,在配置文件中默认的是每秒允许通过一个请求,当我们正常请求接口时,可以正常得到结果。

快速刷接口时,由于方法中添加了降级方法,将会得到降级的响应结果。

4.2 参数解读补充

通过上面的案例演示了使用Resilience4j进行接口的重试或限流操作,只需配置好核心的参数,并且在方法上添加相关的注解即可。

在实际开发中,对Resilience4j的使用,也是重点落在对配置文件中3个模块配置参数的调整,了解这些配置参数的具体含义才能结合实际场景合理使用,具体来说,主要是下面几部分:

  • circuitbreaker,断路器配置;

  • retry,重试配置;

  • ratelimiter,限流配置;

4.2.1 circuitbreaker常用配置参数

circuitbreaker的常用可配置参数项如下

配置参数默认值描述
failureRateThreshold50熔断器关闭状态和半开状态使用的同一个失败率阈值
ringBufferSizeInHalfOpenState10熔断器半开状态的缓冲区大小,会限制线程的并发量,例如缓冲区为10则每次只会允许10个请求调用后端服务
ringBufferSizeInClosedState100熔断器关闭状态的缓冲区大小,不会限制线程的并发量,在熔断器发生状态转换前所有请求都会调用后端服务
waitDurationInOpenState60(s)熔断器从打开状态转变为半开状态等待的时间
automaticTransitionFromOpenToHalfOpenEnabledfalse如果置为true,当等待时间结束会自动由打开变为半开,若置为false,则需要一个请求进入来触发熔断器状态转换
recordExceptionsempty需要记录为失败的异常列表
ignoreExceptionsempty需要忽略的异常列表
recordFailurethrowable -> true自定义的谓词逻辑用于判断异常是否需要记录或者需要忽略,默认所有异常都进行记录
slowCallRateThreshold100以百分比配置阈值。当呼叫持续时间大于等于或大于阈值时,CircuitBreaker 将呼叫视为慢速呼叫。 当慢速呼叫的百分比等于或大于阈值时,CircuitBreaker 转换为打开并开始短路呼叫
slowCallDurationThreshold60000(ms)配置持续时间阈值,超过该阈值呼叫被视为慢速并提高慢速呼叫率。
permittedNumberOfCallsInHalfOpenState10配置 CircuitBreaker 半开时允许的调用次数。
maxWaitDurationInHalfOpenState100配置在 CircuitBreaker 计算错误率或慢速调用率之前所需的最小调用次数(每个滑动窗口周期)。
waitDurationInOpenState100断路器在从打开转换为半打开之前应等待的时间
slidingWindowSize100如果滑动窗口是 COUNT_BASED,则记录并汇总最后一次调用。 如果滑动窗口是TIME_BASED,则记录并汇总最后几秒的调用。slidingWindowSize配置用于在 CircuitBreaker 关闭时记录调用结果的滑动窗口的大小。
slidingWindowTypeCOUNT_BASED配置用于在CircuitBreaker关闭时记录调用结果的滑动窗口类型。 滑动窗口可以是基于计数或基于时间的。

4.2.2 ratelimiter常用配置参数

ratelimiter的常用可配置参数项如下

配置参数默认值描述
timeoutDuration5s线程等待权限的默认等待时间
limitRefreshPeriod500ms权限刷新的时间,每个周期结束后,RateLimiter将会把权限计数设置为limitForPeriod的值
limiteForPeriod一个限制刷新期间的可用权限数

4.2.3 retry常用配置参数

retry的常用可配置参数项如下

配置参数默认值描述
maxAttempts3最大重试次数
waitDuration500ms固定重试间隔
intervalFunctionnumberOfAttempts -> waitDuration用来改变重试时间间隔,可以选择指数退避或者随机时间间隔
retryOnResultPredicateresult -> false自定义结果重试规则,需要重试的返回true
retryOnExceptionPredicatethrowable -> true自定义异常重试规则,需要重试的返回true
retryExceptionsempty需要重试的异常列表
ignoreExceptionsempty需要忽略的异常列表

五、写在文末

熔断、限流与降级作为微服务治理中的重要一环,有必要对其引起重视和关注,而Resilience4j吸收了主流熔断器的优势,具备更为灵活的特点,适用于普通的springboot项目,也能很方便的集成到微服务框架中,值得深入的学习和研究。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用关系运算符和表达式比较大小、_Bool类型、for循环

本文参考C Primer Plus第六章进行C语言学习 文章目录 用关系运算符和表达式比较大小 其他真值新的_Bool类型for循环总结 一.用关系运算符和表达式比较大小 虽然关系运算符也可以用来比较浮点数&#xff0c;但是&#xff0c;要注意的是&#xff1a;尽量只使用<和>。因为浮…

【Linux】gcc与make、makefile

文章目录 1 gcc/g1.1 预处理1.2 编译1.3 汇编1.4 链接1.4.1 静态链接1.4.2 动态链接 2 make和makefile2.1 依赖关系2.2 依赖方法2.3 伪目标 3 总结 1 gcc/g 当我们创建一个文件&#xff0c;并向里面写入代码&#xff0c;此时&#xff0c;我们该如何使我们的代码能够运行起来呢&…

Vivado Repository IP Catalog 释疑

Vivado软件自带了一个IP核仓库&#xff0c;可以在IP Catalog界面查看。 在IP目录界面&#xff0c;依次给出了每个IP核的Name(名称)&#xff0c;Interface(接口)&#xff0c;State(状态)&#xff0c;License(许可证)和VLNV&#xff08;标识符&#xff09;。 Interface表示IP核的…

C++指针(五)完结篇

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 前言 相关文章&#xff1a;C指针&#xff08;一&#xff09;、C指针&#xff08;二&#xff09;、C指针&#xff08;三&#xff09;、C指针&#xff08;四&#xff09;万字图文详解&#xff01; 本篇博客是介…

c++0305习题

一、求下面表达式的值 1&#xff0e;0 2&#xff0e;-1 3&#xff0e;1 4&#xff0e;&#xff08;1&#xff09;1 &#xff08;2&#xff09;3.2 &#xff08;3&#xff09;0 &#xff08;4&#xff09;7.0 5.&#xff08;1&#xff09;0&#xff08;2&#xff09;300.005&a…

[力扣 Hot100]Day49 二叉树的最近公共祖先

题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以…

服务器与文件内数据的 LENGTH_IN_CHAR 参数不匹配

导入数据库数据的时候出现这个 怎么解决&#xff1a;重建数据库实例 下面是达梦的工具 使用DM数据库配置助手 新建、删除实例 新建实例时的配置需要注意的选项 主要是字符集、大小写、和VARCHAR类型以字符为单位 出现【LENGTH_IN_CHAR 参数不匹配】勾选【VARCHAR类型以字…

NUC980开发板CAN开发笔记

一、内核开启CAN CAN 设置 NUC980 系列带有2个CAN(Controller Area Network), 可以分别独立设置。 请按以下的说明来使能CAN功能. 每个CAN可以单独的开关. CAN0有多组管脚可以选择, 需要一并设置。 使用者也可以设置CAN的唤醒功能。步骤如下&#xff1a; 进入 NUC980-linux-4.…

Linux系统安装及简单操作

目录 一、Linux系统安装 二、Linux系统启动 三、Linux系统本地登录 四、Linux系统操作方式 五、Linux的七种运行级别&#xff08;runlevel&#xff09; 六、shell 七、命令 一、Linux系统安装 场景1&#xff1a;直接通过光盘安装到硬件上&#xff08;方法和Windows安装…

1.初学docker

这是在centos7上的基本操作用法。 一、基本操作 # 安装yum源 yum install -y yum-utils # 配置yum源 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装docker yum install -y docker-ce-cli containerd.io docker-buildx-plu…

计算机网络-数据链路层

一、认识以太网 "以太网" 不是⼀种具体的网络&#xff0c;而是一种技术标准; 既包含了数据链路层的内容, 也包含了⼀些物理 层的内容。 例如&#xff1a;规定了网络拓扑结构&#xff0c;访问控制方式&#xff0c;传输速率等; 例如&#xff1a;以太网中的网线必须使用…

mysql的trace追踪SQL工具,进行sql优化

trace是MySQL5.6版本后提供的SQL跟踪工具&#xff0c;通过使用trace可以让我们明白optimizer&#xff08;优化器&#xff09;如何选择执行计划。 注意&#xff1a;开启trace工具会影响mysql性能&#xff0c;所以只适合临时分析sql使用&#xff0c;用完之后请立即关闭。 测试数…

MQ高可用相关设置

文章目录 前言MQ如何保证消息不丢失RabbitMQRocketMQKafkaMQ MQ如何保证顺序消息RabbitMQRocketMQKafka MQ刷盘机制/集群同步RabbitMQRocketMQKafka 广播消息&集群消息RabbitMQRocketMQ MQ集群架构RabbitMQRocketMQKafka 消息重试RabbitMQRockeMqKafka 死信队列RocketMQKaf…

基于springboot实现中小企业财务管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现中小企业财务管理系统演示 摘要 自改革开放一来&#xff0c;我国的经济发展进入到了快速发展时期&#xff0c;大城市的经济水平不断提高&#xff0c;吸引了很多的人不愿千里来大城市奋斗。一个企业的发展离不开相关的规定流程。信息化到来的今天在我们的生活…

计算机网络(基础篇)复习笔记——体系结构/协议基础(持续更新中......)

目录 1 计算机网络基础相关技术Rip 路由更新操作 2 体系结构(OSI 7层, TCP/IP4层)应用层运输层网络层IPv4无分类域间路由选择 CIDRIPV6 数据链路层循环冗余校验CRC协议设备 物理层传输媒体信道复用技术宽带接入技术数据通信 3 网络局域网(以太网Ethernet) 4 通信过程编码:信道极…

uniapp让输入框保持聚焦状态,不会失去焦点

使用场景&#xff1a;当输入框还有发送按钮的时候&#xff0c;点击发送希望软键盘不消失&#xff0c;还可以继续输入&#xff0c;或者避免因输入图片标签造成的屏闪问题 多次尝试后发现一个很实用的方法&#xff0c;适用input输入框和editor输入框 解决办法&#xff1a;把cli…

operator-sdk入门(mac)

1. 安装operator-sdk brew install operator-sdk 2. 安装kubebuilder brew install kubebuilder 3.初始化一个operator脚手架 3.1 新建一个文件夹 redis-operator 3.2 执行初始化 operator-sdk init --domain lyl.com --repo github.com 参数介绍 可以通过operator-sdk --…

Windows下同一电脑配置多个Git公钥访问不同的账号

前言 产生这个问题的原因是我在Gitee码云上有两个账号,为了方便每次不用使用http模式推拉代码,于是我就使用了ssh的模式,起初呢我用两台电脑分别连接两个账号,用起来也相安无事,近段时时间台式机在家里,我在外地出差了,就想着把ssh公钥同时添加到不同的账号里,结果却发现不能用…

《探索自动驾驶技术的前景与挑战》

自动驾驶技术,作为现代科技的一大突破,正逐渐改变着我们的交通方式、生活方式以及整个社会结构。本文将围绕自动驾驶技术的现状、优势、局限性以及未来发展趋势展开探讨。 自动驾驶技术的现状概述 自动驾驶技术作为当今科技领域的一项前沿技术,已经取得了巨大的进展并在不同…

Python算法题集_在排序数组中查找元素的第一个和最后一个位置

Python算法题集_在排序数组中查找元素的第一个和最后一个位置 题34&#xff1a;在排序数组中查找元素的第一个和最后一个位置1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【二分法两次左边界】2) 改进版一【二分法左右边界】3) 改进版二【第三…