Spring Boot - 利用Resilience4j-Circuitbreaker实现断路器模式_防止级联故障

文章目录

  • Pre
  • Resilience4j概述
  • Resilience4j官方地址
  • Resilience4j-Circuitbreaker
  • 应用场景
  • 微服务演示
    • Address service
      • POM
      • Model
      • Repository
      • Service
      • Controller
      • Data Init
      • Properties
      • 测试
    • Order service
      • Model
      • Repository
      • Service
      • Set Up
      • Properties
      • 测试
  • 探究断路器
    • 调用order-service API 2次
    • 关闭address-service,调用order-service API 3
    • 等待5秒,刷新执行器
    • 继续调用 order-service API 3 次
    • 运行address-service,然后继续调用order-service API 3次
  • 小结

在这里插入图片描述


Pre

Spring Boot - 利用Resilience4j-RateLimiter进行流量控制和服务降级


Resilience4j概述

Resilience4J 是一个针对 Java 8 应用程序的轻量级容错和弹性库。它设计用于在分布式系统中的服务之间提供弹性和容错性。Resilience4J 的名字来源于它提供的核心功能,即让系统(服务)能够“弹性”(resilient)地应对各种失败情况,包括网络问题、第三方服务故障等。

Resilience4J 提供了以下功能:

  1. 断路器(Circuit Breaker):当检测到服务异常或超时,断路器会打开,阻止进一步的请求发送到该服务。一段时间后(通常是秒级),断路器会进入半开状态,允许一个测试请求通过以检查服务是否恢复。如果请求成功,断路器关闭;如果失败,断路器会再次打开。
  2. 限流(Rate Limiter):限制进入系统的请求速率,防止系统过载。这可以通过令牌桶算法或滑动窗口算法实现。
  3. 隔离(Isolation):通过信号量或线程池隔离不同的服务调用,防止一个服务的失败影响到其他服务。
  4. 超时(Timeouts):为服务调用设置超时时间,超过时间后会触发超时异常。
  5. 重试(Retry):在遇到特定异常时自动重试服务调用,可以配置重试次数和间隔。
  6. 缓存(Caching):提供缓存机制,以避免重复执行计算密集型或远程调用。

Resilience4J 的一大特点是它的轻量级特性,它只使用了 Vavr 库(一个函数式编程库),没有其他外部库依赖。这使得它在集成到现有系统时非常方便,且性能开销小。

Resilience4J 设计上易于配置,支持通过代码、配置文件或运行时参数进行配置。它也支持通过 actuator 模块与 Spring Boot 的监控和管理特性集成。

由于 Resilience4J 的这些特性和优势,它在现代分布式系统和微服务架构中得到了广泛应用,尤其是在需要高可用性和弹性的环境中。


Resilience4j官方地址

https://resilience4j.readme.io/

在这里插入图片描述

https://github.com/resilience4j/resilience4j

在这里插入图片描述


Resilience4j-Circuitbreaker

Resilience4j中的CircuitBreaker是一个核心工具,用于保护分布式系统免受故障的影响。

其工作原理主要通过一个有限状态机实现,包括CLOSED(关闭)、OPEN(打开)和HALF-OPEN(半开)三种状态。

Resilience4j 是一个为Java 8和函数式编程设计的轻量级容错库。它的主要目的是帮助开发者在分布式系统中实现弹性和容错性。

Resilience4j 提供了多种容错机制,包括断路器(CircuitBreaker)、限流器(RateLimiter)、重试(Retry)、隔离策略(Bulkhead)和超时控制(TimeLimiter)等。其中,CircuitBreaker 是 Resilience4j 的一个重要组成部分。

CircuitBreaker,即断路器,其设计原理来源于电路中的断路器,当电流超过设定值时,断路器会自动断开,以保护电路免受过大电流的损害。在软件系统中,断路器用于保护系统免受某个组件或服务故障的影响。

Resilience4j 的 CircuitBreaker 实现原理如下:

  1. 断路器的状态:CircuitBreaker 具有三种正常状态:CLOSED(关闭)、OPEN(打开)和 HALFOPEN(半开),以及两个特殊状态:DISABLED(禁用)和 FORCEDOPEN(强制打开)。这些状态通过有限状态机进行管理。

  2. 打开和关闭逻辑:当被保护的服务或资源发生故障或长时间不可用时,断路器会迅速切换到 OPEN 状态,阻止更多的请求发送到该服务或资源。在 OPEN 状态下,系统会定期发送测试请求,以检查故障是否已经解决。如果测试请求成功,断路器会切换到 HALFOPEN 状态,允许一个请求发送到该服务或资源。如果这个请求成功,断路器会切换到 CLOSED 状态,否则会重新切换到 OPEN 状态。

  3. 故障率计算:为了判断是否打开断路器,需要收集一定数量的请求数据。在 Resilience4j 中,需要至少填充一个环形缓冲区(Ring Bit Buffer),才能开始计算故障率。环形缓冲区的大小决定了需要多少次请求才能进行故障率的计算。

  4. 环形缓冲区:Resilience4j 使用环形缓冲区来存储请求状态的数据结构,这与 Hystrix 使用的滑动窗口不同。环形缓冲区使用位集合(BitSet)实现,每个位代表一个请求的状态(成功或失败)。环形缓冲区的大小决定了能够存储的请求数量。例如,一个大小为 10 的缓冲区可以存储 1024 个请求状态。

  5. 配置选项:Resilience4j 提供了丰富的配置选项,如故障率阈值、打开状态下的等待时间、半开状态下允许的最大请求数等,开发者可以根据需求进行灵活配置。

通过上述原理,Resilience4j 的 CircuitBreaker 能够有效地保护分布式系统免受故障的影响,提高系统的可用性和健壮性。


应用场景

服务 A 调用服务 B,但不幸的是,服务 B 不可用或无法响应。因此,服务 A 可能会等待服务 B 的响应或处理遇到的异常。后续针对服务 B 的请求也会遇到类似的挑战,从而导致糟糕的用户体验。

在这种情况下,断路器可以通过在特定时间内停止请求发送,等待超时结束,启用有限数量的请求来检查服务 B 是否正常工作。如果这些请求成功,微服务就可以继续正常运行。如果没有,它将再次开始超时。

在这里插入图片描述

断路器具有三种状态:Closed、Open 和 Half_Open。

  • 闭合是断路器的初始状态。当微服务运行和交互顺利时,断路器就关闭了。它持续监控配置时间段内发生的故障数量。如果失败率超过指定的阈值,其状态将变为Open状态。如果没有,它将重置失败计数和超时时间
  • 在Open状态下,断路器会阻塞微服务之间的交互流程。请求调用将会失败,并且会抛出异常。 Open 状态保持直到超时结束,然后更改为 Half_Open 状态。
  • 在Half_Open状态下,断路器将允许有限数量的请求通过。如果失败率大于指定阈值,则再次切换到Open状态。否则为Closed状态。

微服务演示

在这里插入图片描述

有 2 个服务,名为地址服务和订单服务

在这里插入图片描述

  • 在购买之前,购物者希望查看订单的详细信息。他们向订单服务发送请求。
  • 订单服务使用邮政编码来调用地址服务以获取送货地址详细信息。
  • 收到送货地址详细信息后,订单服务会更新订单信息,然后将其发送回购物者。

Address service

首先构建地址服务,因为它是一个依赖服务.

在这里插入图片描述

POM

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.edu</groupId><artifactId>address-service</artifactId><version>0.0.1-SNAPSHOT</version><name>address-service</name><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></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><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

Model

package com.edu.addressservice.model;import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "addresses")
public class Address {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;private String postalCode;private String state;private String city;
}

Repository

package com.edu.addressservice.repository;import java.util.Optional;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;import com.edu.addressservice.model.Address;@Repository
public interface AddressRepository extends JpaRepository<Address, Integer> {Optional<Address> findByPostalCode(String postalCode);
}

Service

public interface AddressService {Address getAddressByPostalCode(String postalCode);
}
@Service
public class AddressServiceImpl implements AddressService {@Autowiredprivate AddressRepository addressRepository;public Address getAddressByPostalCode(String postalCode) {return addressRepository.findByPostalCode(postalCode).orElseThrow(() -> new RuntimeException("Address Not Found: " + postalCode));}
}

Controller

package com.edu.addressservice.controller;import org.springframework.beans.factory.annotation.Autowired;
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 com.edu.addressservice.model.Address;
import com.edu.addressservice.service.AddressService;@RestController
@RequestMapping("addresses")
public class AddressController {@Autowiredprivate AddressService addressService;@GetMapping("/{postalCode}")public Address getAddressByPostalCode(@PathVariable("postalCode") String postalCode) {return addressService.getAddressByPostalCode(postalCode);}
}

Data Init

@PostConstruct注解的方法。 Spring 将在初始化 bean 属性并填充数据后调用该方法。

package com.edu.addressservice.config;import java.util.Arrays;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;import com.edu.addressservice.model.Address;
import com.edu.addressservice.repository.AddressRepository;import jakarta.annotation.PostConstruct;@Configuration
public class DataSetup {@Autowiredprivate AddressRepository addressRepository;@PostConstructpublic void setupData() {addressRepository.saveAll(Arrays.asList(Address.builder().id(1).postalCode("1000001").state("SD").city("JN").build(),Address.builder().id(2).postalCode("1100000").state("JS").city("NJ").build(),Address.builder().id(3).postalCode("2100001").state("ZJ").city("HZ").build()));}
}

Properties

server:port: 9090
spring:application:name: address-servicejpa:database-platform: org.hibernate.dialect.H2Dialecthibernate:ddl-auto: create-dropdatasource:url: jdbc:h2:mem:address-dbusername: adminpassword: 1111driverClassName: org.h2.Driverh2:console:enabled: true

测试

运行并访问链接http://localhost:9090/addresses/1000001,预期响应应如下所示

在这里插入图片描述

这样就完成了地址服务的构建。


Order service

重点关注是如何配置断路器,并通过执行器监控其状态的。

在这里插入图片描述


Model

public interface Type {
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "orders")
public class Order implements Type {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)Integer id;private String orderNumber;private String postalCode;private String shippingState;private String shippingCity;
}
@Data
public class Failure implements Type {private final String msg;
}

Repository

package com.edu.orderservice.repository;import java.util.Optional;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;import com.edu.orderservice.model.Order;@Repository
public interface OrderRepository extends JpaRepository<Order, Integer> {Optional<Order> findByOrderNumber(String orderNumber);
}

Service

业务逻辑都在这里了。

  • 从“orders”表中检索订单信息
  • 调用外部服务(地址服务)以获取送货地址信息
  • 使用送货地址详细信息更新订单信息,然后返回

如何调用外部API---------------------> Spring 提供的 RestTemplate

package com.edu.orderservice.service;import com.edu.orderservice.model.Type;public interface OrderService {Type getOrderByPostCode(String orderNumber);
}
package com.edu.orderservice.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import com.edu.orderservice.dto.AddressDTO;
import com.edu.orderservice.model.Failure;
import com.edu.orderservice.model.Order;
import com.edu.orderservice.model.Type;
import com.edu.orderservice.repository.OrderRepository;
import com.edu.orderservice.service.OrderService;import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate RestTemplate restTemplate;private static final String SERVICE_NAME = "order-service";private static final String ADDRESS_SERVICE_URL = "http://localhost:9090/addresses/";@Override@CircuitBreaker(name = SERVICE_NAME, fallbackMethod = "fallbackMethod")public Type getOrderByPostCode(String orderNumber) {Order order = orderRepository.findByOrderNumber(orderNumber).orElseThrow(() -> new RuntimeException("Order Not Found: " + orderNumber));HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntity<AddressDTO> entity = new HttpEntity<>(null, headers);ResponseEntity<AddressDTO> response = restTemplate.exchange((ADDRESS_SERVICE_URL + order.getPostalCode()), HttpMethod.GET, entity,AddressDTO.class);AddressDTO addressDTO = response.getBody();if (addressDTO != null) {order.setShippingState(addressDTO.getState());order.setShippingCity(addressDTO.getCity());}return order;}private Type fallbackMethod(Exception e) {return new Failure("Address service is not responding properly");}
}

@CircuitBreaker 属性

  • name”被分配为“order-service”,表名“order-service”实例的每个配置都适用于该方法。
  • fallbackMethod”属性,目的是在依赖服务(地址服务)未正确响应时调用降级方法。

Set Up

@Configuration
public class RestConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
package com.edu.orderservice.config;import java.util.Arrays;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;import com.edu.orderservice.model.Order;
import com.edu.orderservice.repository.OrderRepository;import jakarta.annotation.PostConstruct;@Configuration
public class DataSetup {@Autowiredprivate OrderRepository orderRepository;@PostConstructpublic void setupData() {orderRepository.saveAll(Arrays.asList(Order.builder().id(1).orderNumber("0c70c0c2").postalCode("1000001").build(),Order.builder().id(2).orderNumber("7f8f9f15").postalCode("1100000").build(),Order.builder().id(3).orderNumber("394627b2").postalCode("2100001").build()));}
}

Properties

server:port: 1010
spring:application:name: order-servicejpa:database-platform: org.hibernate.dialect.H2Dialecthibernate:ddl-auto: create-dropdatasource:url: jdbc:h2:mem:order-dbusername: rootpassword: 123driverClassName: org.h2.Driverh2:console:enabled: true
management:endpoint:health:show-details: alwaysendpoints:web:exposure:include: healthhealth:circuitbreakers:enabled: true
resilience4j:circuitbreaker:instances:order-service:sliding-window-type: COUNT_BASEDfailure-rate-threshold: 50minimum-number-of-calls: 5automatic-transition-from-open-to-half-open-enabled: truewait-duration-in-open-state: 5spermitted-number-of-calls-in-half-open-state: 3sliding-window-size: 10register-health-indicator: true

配置中是针对 Resilience4j 库的配置

下面是对 order-service 断路器实例每个配置项的解释:

  • 滑动窗口类型: COUNT_BASED:这指定了用于跟踪失败的滑动窗口是基于失败的调用次数。Resilience4j 支持两种类型的滑动窗口:COUNT_BASED(基于次数)和 TIME_BASED(基于时间)。
  • 故障率阈值: 50:这设置了断路器打开的故障率阈值。在这种情况下,如果故障率达到 50%,则断路器将打开,并阻止对服务的进一步请求。
  • 最小调用次数: 5:这设置了在计算故障率之前必须执行的调用次数。如果小于 5 次调用,则断路器不会因故障率而打开。
  • 自动从打开状态过渡到半开放状态: true:这配置了断路器在指定的等待周期后自动从打开状态过渡到半开放状态。在半开放状态下,允许有限数量的请求通过到服务。
  • 打开状态下的等待时间: 5s:这是断路器在打开状态下持续的秒数,在此之后它会过渡到半开放状态。
  • 半开放状态下的允许调用次数: 3:这设置了在半开放状态下允许的调用次数。如果在期间有超过 3 次调用失败,则断路器将回到打开状态。
  • 滑动窗口大小: 10:这配置了用于跟踪请求和失败的滑动窗口的大小。滑动窗口的大小必须大于计算故障率所需的最小调用次数。
  • 注册健康指标: true:这个标志指示是否为断路器注册健康指标。健康指标用于提供关于组件健康状况的信息,包括断路器的状态。

综上所述,这些配置定义了 order-service 断路器的行为。断路器将监控故障率和调用次数,以确定何时打开和何时过渡到半开放状态,为系统提供一种自我保护机制,以防止级联失败。


测试

依次运行address-service和order-service,访问链接http://localhost:1010/orders?orderNumber=0c70c0c2

在这里插入图片描述


如果地址服务未正确响应(服务已关闭),我们将收到以下响应

在这里插入图片描述

这样完成了订单服务的构建 。


探究断路器

两个服务确保都已运行,访问链接 http://localhost:1010/actuator/health 查看断路器详细信息
在这里插入图片描述

我们可以看到以下几个关键配置:

  • bufferedCalls(缓冲调用次数)为0,这意味着从order-service到address-service没有发起任何API调用。
  • failedCalls(失败调用次数)也为0,这表示所有调用都成功了,没有失败的调用。
  • failureRate(失败率)为-1.0%,这是一个异常的值,因为失败率不可能是负数。通常,失败率应该是成功率的一部分,即0%表示100%的成功率。这里的情况可能是因为计算失败率时使用了0作为分母,导致了负数的产生。
  • state(状态)为"CLOSED"(关闭),这表明circuitBreaker(断路器)目前是关闭的,没有触发熔断机制。

调用order-service API 2次

调用order-service API 2次http://localhost:1010/orders?orderNumber=0c70c0c2

然后刷新actuator链接
在这里插入图片描述

  • “bufferedCalls”: 2, // Total number of API calls now is 2

关闭address-service,调用order-service API 3

关闭address-service,调用order-service API 3次http://localhost:1010/orders?orderNumber=0c70c0c2,然后刷新执行器链接

在这里插入图片描述

我们注意到Circuit Breaker被触发。原因是“failureRate”现在大于“failure-rate-threshold”

  • “status”: “CIRCUIT_OPEN”, // CircuitBreaker is triggered
  • “failureRate”: “60.0%”, // this rate now is greater than “failureRateThreshold”
  • “bufferedCalls”: 5, // Total number of API calls from order-service to address-service
  • “failedCalls”: 3, // Number of failed API calls from order-service to address-service

等待5秒,刷新执行器

在这里插入图片描述

HALF_OPEN状态下,允许““permitted-number-of-calls-in-half-open-state”请求(我们将其值配置为3),然后再次计算失败率,如果失败率仍然大于“failure-rate-threshold”,断路器将再次被触发。


继续调用 order-service API 3 次

继续调用 order-service API 3 次 http://localhost:1010/orders?orderNumber=0c70c0c2,然后刷新执行器链接

在这里插入图片描述

  • “status”: “CIRCUIT_OPEN”, // // CircuitBreaker is triggered again

运行address-service,然后继续调用order-service API 3次

运行address-service,然后继续调用order-service API 3次http://localhost:1010/orders?orderNumber=0c70c0c2,刷新执行器链接
在这里插入图片描述

断路器已关闭 。


小结

Resilience4j的circuit breaker模式特别有用,它能够在服务调用失败达到一定次数后,自动断开电路,避免进一步的调用,从而保护应用程序不受故障服务的拖累。当服务恢复后,电路会重新闭合,允许正常的调用再次发生。

在这里插入图片描述

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

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

相关文章

卷积和滤波对图像操作的区别

目录 问题引入 解释 卷积 滤波 问题引入 卷积和滤波是很相似的&#xff0c;都是利用了卷积核进行操作 那么他们之间有什么区别呢&#xff1f; 卷积&#xff1a;会影响原图大小 滤波&#xff1a;不会影响原图大小 解释 卷积 我们用这样一段代码来看 import torch.nn as …

【AI接口】语音版、文心一言大模型和AI绘图、图片检测API

文章目录 一、语音版大模型AI1、接口2、请求参数3、请求参数示例4、接口返回示例 二、AI图片鉴黄合规检测API1、接口2、请求参数3、请求参数示例4、接口返回示例5、报错说明6、代码开源 三、人工智能AI绘画API1、接口2、请求参数3、请求参数示例4、接口返回示例5、AI绘画成果展…

Gin 框架之用户密码加密

文章目录 一、引入二、密码加密位置三、如何加密四、bcrypt 库加密4.1 介绍4.2 优点&#xff1a;4.3 使用 五、小黄书密码加密实践 一、引入 Gin是一个用Go语言编写的Web框架&#xff0c;而用户密码的加密通常是在应用程序中处理用户身份验证时的一个重要问题。 通常敏感信息…

3D可视化:陶瓷烧制的未来之路

陶瓷&#xff0c;这一古老的艺术形式&#xff0c;见证了中华文明的辉煌。然而&#xff0c;随着时代的变迁&#xff0c;传统的陶瓷烧制过程正面临着诸多挑战。如何将这门千年技艺传承下去&#xff0c;并在现代社会中焕发新的光彩&#xff1f;3D可视化技术为我们打开了一扇通往未…

基于springboot+vue的蜗牛兼职网的设计与实现系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

第02章_变量与运算符(关键字,标识符,变量,基本数据类型,进制,运算符,字符集)

文章目录 第02章_变量与运算符本章专题与脉络1. 关键字&#xff08;keyword&#xff09;2. 标识符( identifier)3. 变量3.1 为什么需要变量3.2 初识变量3.3 Java中变量的数据类型3.4 变量的使用3.4.1 步骤1&#xff1a;变量的声明3.4.2 步骤2&#xff1a;变量的赋值 4. 基本数据…

Elastic Stack 8.12:通过对 ES|QL 等的改进增强了向量搜索

作者&#xff1a;来自 Elastic Tyler Perkins, Shani Sagiv, Gilad Gal, Ninoslav Miskovic Elastic Stack 8.12 构建于 Apache Lucene 9.9&#xff08;有史以来最快的 Lucene 版本&#xff09;之上&#xff0c;基于我们对标量量化和搜索并发性的贡献&#xff0c;为文本、向量和…

仿“今日头条”的开源多媒体资讯发布系统

伴随着互联网的普及、用户的时间碎片化&#xff0c;使得信息传播方式发生了巨大变化&#xff0c;因此我们看到微信、微博、今日头条、抖音、快手等平台迅速崛起。这些新的信息传播方式给广大用户带来了极大的便利&#xff0c;同时也给每个人的思想和生活带来了潜移默化的影响。…

代码随想录 Leetcode459. 重复的子字符串(KMP算法)

题目&#xff1a; 代码&#xff08;首刷看解析 KMP算法 2024年1月18日&#xff09;&#xff1a; class Solution { public:void getNext(string& s,vector<int>& next) {int j 0;next[0] j;for (int i 1; i < s.size(); i) {while (j > 0 && s…

三大3D引擎对比,直观感受AMRT3D渲染能力

作为当前热门的内容呈现形式&#xff0c;3D已经成为了广大开发者、设计师工作里不可或缺的一部分。 用户对于3D的热衷&#xff0c;源于其带来的【沉浸式体验】和【超仿真视觉效果】。借此我们从用户重点关注的四个3D视觉呈现内容&#xff1a; 材质- 呈现多元化内容水效果- 展…

【Git】常用的Git操作集合

常用的Git操作集合 1. 分支操作1.1 查看本地所有分支git branch 1.2 查看所有分支&#xff08;包含本地远程仓库&#xff09;git branch -a 1.3 切换分支git checkout test 2. 常用基本操作2.1 查看 git 各存储区内(文件)状态git status 2.2 查看工作区与暂存区文件差异git dif…

揭开UI设计的神秘面纱:如何打造一款让用户爱不释手的移动APP

文章目录 一、目标用户分析二、设计风格和色彩搭配三、布局和导航设计四、交互设计五、视觉元素设计六、响应式设计七、测试和优化八、持续更新和迭代九、团队协作和沟通十、学习和成长《移动APP UI设计与制作(微课版)》编辑推荐内容简介目录 《Flutter入门经典&#xff08;移动…

【vue】ant-col多列栅格式的表单排列方式布局异常:

文章目录 一、效果&#xff1a;二、解决&#xff1a;三、问题&#xff1a; 一、效果&#xff1a; 二、解决&#xff1a; 在row中添加布局类型&#xff1a;type“flex” 三、问题&#xff1a; 后期正式环境还是存在该问题 >>>.ant-form-item {max-height: 32px; }多…

搭建web网站

要求 1.基于域名[www.openlab.com](http://www.openlab.com)可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[www.openlab.com/student](http://www.openlab.com/student) 网站访问学生信…

【Linux 命令】tree 对目录进行树形展示

目录 1、tree 命令功能展示 2、tree 命令安装 3、tree 命令语法及其参数功能 4、终止 tree 展开树命令 1、tree 命令功能展示 在 Linux 中&#xff0c;我们使用 ll 命令对目录的展示并不太方便我们查看&#xff0c;不太清晰明了&#xff0c;所以我们可以使用 tree 命令以…

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测,基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测&#xff0c;基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测 目录 分类预测 | Matlab实现KPCA-EBWO-SVM分类预测&#xff0c;基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测分类效果基本描述程序设计参…

GO基础进阶篇 (十四)、Http编程

Web基础概念 web应用程序 web程序可以提供浏览器访问的程序。Web应用程序通常采用客户端-服务器模型。客户端是用户使用的Web浏览器或其他Web客户端&#xff0c;而服务器是存储和处理数据的远程计算机。 我们能访问到的任何一个页面或资源&#xff0c;都存在于世界的某一个角落…

TDengine 创始人陶建辉在汽车 CIOCDO 论坛发表演讲,助力车企数字化转型

当前&#xff0c;汽车行业的数字化转型如火如荼。借助数字技术的充分利用&#xff0c;越来越多的车企进一步提升了成本优化、应用敏捷性、高度弹性和效率。这一转型使得业务应用的开发和管理模式发生了颠覆性的创新&#xff0c;赋予了汽车软件快速响应变化和动态调度资源的能力…

仓储的未来:为叉车配备智能设备

近年来&#xff0c;数字化和自动化极大地重塑了仓储行业。叉车是仓库的主力&#xff0c;正在配备智能设备以简化操作。 点击下载Dynamsoft最新版https://www.evget.com/product/3691/download 智能叉车的序列化艺术 序列化是为每个商品或托盘分配唯一标识符&#xff08;通常采…

文件夹里的文件消失了?3个方法轻松找回文件!

“我在电脑上建了个文件夹&#xff0c;用来保存比较重要的文件和数据&#xff0c;但是不知道为什么&#xff0c;我文件夹里的文件莫名其妙就消失了&#xff0c;有什么方法可以找回消失的文件吗&#xff1f;” 为了更好的给文件进行分类&#xff0c;很多用户会选择将文件放置到不…