Spring Cloud LoadBalancer 原理与实践

背景

当前我们的微服务架构基于Spring Cloud Alibaba体系,通过定制NacosRule实现了跨集群访问和灰度发布功能。但随着Spring Cloud与Nacos版本升级,官方已弃用Ribbon转向LoadBalancer,这要求我们完成以下技术升级:

  1. 负载均衡机制迁移:将原有Ribbon规则适配到LoadBalancer
  2. 功能继承保障:保持跨集群路由和灰度能力
  3. 技术风险控制:深入理解底层机制以提升问题排查效率

技术选型对比

特性RibbonLoadBalancer
维护状态停止更新官方维护
响应式支持不支持原生支持
配置灵活性XML/注解全Java配置
扩展性中等
服务发现集成需要适配深度整合

负载均衡

什么是负载均衡?简单来说,负载均衡就是将网络流量(负载)分摊到不同的网络服务器(可以平均分配,也可以不平均),系统就可以实现服务的水平横向扩展。

服务端负载均衡

服务器端负载均衡指的是存放在服务器端的负载均衡器,例如 Nginx、HAProxy、F5 等。

在这里插入图片描述

客户端负载均衡

客户端负载均衡指的是嵌套在客户端的负载均衡器,例如 Ribbon、Loadbalancer。

在这里插入图片描述

Spring cloud loadbalancer

在介绍loadbalancer之前,如果让你来设计一个负载均衡组件,你会怎么设计?

我们可能需要考虑以下这几个问题:

  • 如何获取服务器列表?
  • 服务器列表发生变更如何监听同步?
  • 如何将客户端请求进行拦截然后选择服务器进行转发?
  • 如何将负载进行分摊?

带着这些问题,我们来深入了解Spring Cloud LoadBalancer的实现机制。

服务器列表获取

ServiceInstanceListSupplier

public interface ServiceInstanceListSupplierextends Supplier<Flux<List<ServiceInstance>>> {// 服务idString getServiceId();// 构造器static ServiceInstanceListSupplierBuilder builder() {return new ServiceInstanceListSupplierBuilder();}//用于创建一个 固定的 ServiceInstanceListSupplier(实例列表不会变)。//允许从 Spring Environment 读取配置来构造 FixedServiceInstanceListSupplier。static FixedServiceInstanceListSupplier.Builder fixed(Environment environment) {return new FixedServiceInstanceListSupplier.Builder(environment);}//直接返回 serviceId 对应的 FixedServiceInstanceListSupplier。static FixedServiceInstanceListSupplier.SimpleBuilder fixed(String serviceId) {return new FixedServiceInstanceListSupplier.SimpleBuilder(serviceId);}//实现 ServiceInstanceListSupplier,用于返回一个固定的实例列表,不会动态更新。//适用于测试环境或者静态服务发现场景。class FixedServiceInstanceListSupplier implements ServiceInstanceListSupplier {private final String serviceId;private List<ServiceInstance> instances;@Deprecatedpublic static Builder with(Environment env) {return new Builder(env);}//构造 FixedServiceInstanceListSupplier,接受服务 ID 和实例列表。private FixedServiceInstanceListSupplier(String serviceId,List<ServiceInstance> instances) {this.serviceId = serviceId;this.instances = instances;}//返回当前 FixedServiceInstanceListSupplier 所管理的 serviceId。@Overridepublic String getServiceId() {return serviceId;}//返回固定的实例列表,不会随 Nacos 或 Eureka 变化。@Overridepublic Flux<List<ServiceInstance>> get() {return Flux.just(instances);}}

该接口提供符合条件的实例列表,并提供了 builder 方法返回 ServiceInstanceListSupplierBuilder 实例用来构造 ServiceInstanceListSupplier

同时FixedServiceInstanceListSupplier,它返回固定的服务实例列表。它主要用于

  • ** 测试负载均衡逻辑**(在不依赖 Nacos/Eureka 的情况下提供固定实例)。**
  • 静态配置服务实例(比如在某些特殊场景下,不使用注册中心,而是固定 IP+端口)。

示例

List<ServiceInstance> instances = List.of(new DefaultServiceInstance("id1", "my-service", "127.0.0.1", 8080, false),new DefaultServiceInstance("id2", "my-service", "127.0.0.2", 8081, false)
);ServiceInstanceListSupplier supplier = ServiceInstanceListSupplier.fixed("my-service").withInstances(instances).build();// 获取实例列表
supplier.get().subscribe(list -> list.forEach(instance ->System.out.println(instance.getHost() + ":" + instance.getPort())
));

整个 ServiceInstanceListSupplier 的实现类都是 rx式 编程风格,但核心逻辑不难看懂,下面就贴出几个实现类简单了解下

DiscoveryClientServiceInstanceListSupplier

...	
// 普通mvc项目获取获取实例列表
public DiscoveryClientServiceInstanceListSupplier(DiscoveryClient delegate,Environment environment) {this.serviceId = environment.getProperty(PROPERTY_NAME);resolveTimeout(environment);this.serviceInstances = Flux.defer(() -> Mono.fromCallable(() -> delegate.getInstances(serviceId))).timeout(timeout, Flux.defer(() -> {logTimeout();return Flux.just(new ArrayList<>());}), Schedulers.boundedElastic()).onErrorResume(error -> {logException(error);return Flux.just(new ArrayList<>());});}// webflux项目获取获取实例列表public DiscoveryClientServiceInstanceListSupplier(ReactiveDiscoveryClient delegate,Environment environment) {...}
...

主要逻辑在构造方法中,等价于 this.serviceInstances = discoveryClient.getInstances(serviceId),不难理解:从注册中心拉去实例列表

DelegatingServiceInstanceListSupplier

public abstract class DelegatingServiceInstanceListSupplierimplements ServiceInstanceListSupplier, InitializingBean, DisposableBean {protected final ServiceInstanceListSupplier delegate;public DelegatingServiceInstanceListSupplier(ServiceInstanceListSupplier delegate) {Assert.notNull(delegate, "delegate may not be null");this.delegate = delegate;}...

装饰层,内嵌一个代理对象,一般就是 DiscoveryClientServiceInstanceListSupplier 来获取实例列表,装饰逻辑就是过滤对应的列表

其下面有n个实现子类,暂不贴代码了

主要功能为:

  • ZonePreferenceServiceInstanceListSupplier:区域优先选择,优先选择与当前客户端在相同 zone(可用区)的实例,提高访问效率,降低跨区域流量消耗。
  • CachingServiceInstanceListSupplier:缓存 ServiceInstanceListSupplier 的返回结果,减少注册中心查询次数,提升性能。
  • SameInstancePreferenceServiceInstanceListSupplier:尽量路由到之前选中的实例,减少服务间的切换,提高请求一致性。
  • HealthCheckServiceInstanceListSupplier:在负载均衡前,过滤掉不健康的服务实例,确保请求不会路由到故障实例。

选择实例以及转发

ReactorLoadBalancer

public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {/*** Choose the next server based on the load balancing algorithm.* @param request - an input request* @return - mono of response*/@SuppressWarnings("rawtypes")Mono<Response<T>> choose(Request request);default Mono<Response<T>> choose() {return choose(REQUEST);}}

该接口从 ServiceInstanceListSupplier 返回的实例中选择最终目标,其中也分为普通mvc项目和webflux项目,spring-cloud-loadbalancer 默认只提供了两个实现:

  • RandomLoadBalancer:随机选择
  • RoundRobinLoadBalancer:轮询

如果不明白这两区别,可以看文章最后的部分

服务装配

LoadBalancerClientConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;//实例选择规则:默认轮询@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);}//WebFlux 环境下的默认 ServiceInstanceListSupplier 配置@Configuration(proxyBeanMethods = false)@ConditionalOnReactiveDiscoveryEnabled@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER)public static class ReactiveSupportConfiguration {@Bean@ConditionalOnBean(ReactiveDiscoveryClient.class)@ConditionalOnMissingBean@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations",havingValue = "default", matchIfMissing = true)public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {return ServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().build(context);}...}//普通 web 环境下的配置@Configuration(proxyBeanMethods = false)@ConditionalOnBlockingDiscoveryEnabled@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)public static class BlockingSupportConfiguration {...@Bean@ConditionalOnBean(DiscoveryClient.class)@ConditionalOnMissingBean@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations",havingValue = "health-check")public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withHealthChecks().build(context);}...}}

现在回过头来看每个 LoadBalancerClient 容器实例下默认注册的配置类 LoadBalancerClientConfiguration,如代码所示:

默认的实例选择规则是 轮询
对应的实例列表获取规则取决于 spring.cloud.loadbalancer.configurations 属性配置

LoadBalancerAutoConfiguration

@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@EnableConfigurationProperties(LoadBalancerProperties.class)
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,LoadBalancerBeanPostProcessorAutoConfiguration.class,ReactiveLoadBalancerAutoConfiguration.class })
public class LoadBalancerAutoConfiguration {private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations;public LoadBalancerAutoConfiguration(ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {this.configurations = configurations;}@Bean@ConditionalOnMissingBeanpublic LoadBalancerZoneConfig zoneConfig(Environment environment) {return new LoadBalancerZoneConfig(environment.getProperty("spring.cloud.loadbalancer.zone"));}@ConditionalOnMissingBean@Beanpublic LoadBalancerClientFactory loadBalancerClientFactory() {LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));return clientFactory;}}

等上述所有实例加载完,最后整体装配

执行最终实例

BlockingLoadBalancerClient

public class BlockingLoadBalancerClient implements LoadBalancerClient {...@Overridepublic <T> T execute(String serviceId, LoadBalancerRequest<T> request)throws IOException {ServiceInstance serviceInstance = choose(serviceId);if (serviceInstance == null) {throw new IllegalStateException("No instances available for " + serviceId);}return execute(serviceId, serviceInstance, request);}//执行request请求@Overridepublic <T> T execute(String serviceId, ServiceInstance serviceInstance,LoadBalancerRequest<T> request) throws IOException {try {return request.apply(serviceInstance);}catch (IOException iOException) {throw iOException;}catch (Exception exception) {ReflectionUtils.rethrowRuntimeException(exception);}return null;}@Overridepublic URI reconstructURI(ServiceInstance serviceInstance, URI original) {return LoadBalancerUriTools.reconstructURI(serviceInstance, original);}@Overridepublic ServiceInstance choose(String serviceId) {//获取 serviceId 容器中的 ReactiveLoadBalancer 实例ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);if (loadBalancer == null) {return null;}Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose()).block();if (loadBalancerResponse == null) {return null;}return loadBalancerResponse.getServer();}}

最后回到 BlockingLoadBalancerClient#execute 逻辑(容器中默认装配的 LoadBalancerClient):

逻辑无非就是选择最终的实例来执行请求
底层逻辑就是从隔离好的 容器 中获取对应的 ServiceInstanceListSupplier 和 ReactiveLoadBalancer 来选择实例

实践指南

配置类

  • 注册中心维度,相当于每个client端隔离一份配置,都走自己定义的逻辑,通过@LoadBalancerClients注解整合到一份配置类中,具体配置一内部类的形式维护,缺点就是,client端规则发生变化时,需要修改对应配置类
  • 通用策略配置,每种策略配置隔离一份,通过@LoadBalancerClients注解整合到一份配置类中,client端选择对应的配置即可,缺点就是,组合会很多,比较复杂,但是好处时,如果发生变更的话,只需要更改@LoadBalancerClients的属性值,拓展性也比较好

注册中心维度

@LoadBalancerClients({@LoadBalancerClient(value = "eureka-client-1", configuration = ClietConfig.EurekaClient1Config.class), @LoadBalancerClient(value = "eureka-client-2", configuration = ClietConfig.EurekaClient2Config.class)
})
public class ClietConfig {//如果这里面的规则发生变化,就得改这里面的东西static class EurekaClient1Config {// ... }//如果这里面的规则发生变化,就得改这里面的东西static class EurekaClient2Config {// ...}
}

通用策略维度

@LoadBalancerClients({@LoadBalancerClient(value = "eureka-client-1", configuration = LoadBalanceConfig.RandomLoadBalancerConfig.class), @LoadBalancerClient(value = "eureka-client-2", configuration = LoadBalanceConfig.ZonePerferServiceListConfig.class)
})
public class LoadBalanceConfig {//这些策略不用更改,一般时通用static class RandomLoadBalancerConfig {// ...}//这些策略不用更改,一般时通用static class HintServiceListConfig {// ... }// ...
}

灰度切入

目前根据loadbalancer提供的类来看,可以实现灰度切入的有两个地方,分别为:

  • ReactiveLoadBalancer:选择实例
  • ServiceInstanceListSupplier:过滤实例

严格意义上来说,我的理解是灰度功能是来选择示例的,并不是过滤实例的,所以我可能更倾向于通过ReactiveLoadBalancer来实现,实现他的choose方法;

但是这里有个坑是啥?

如果你想在ReactiveLoadBalancer层面实现基于请求头的路由决策,你需要在调用choose方法时传递一些上下文信息。Spring Cloud LoadBalancer中的Request接口可以携带这些信息。然而,Request对象需要在调用choose之前构建,并且Spring Cloud LoadBalancer并没有提供一个内置的方式来根据传入的HTTP请求构建这个Request对象。

不过这个可以通过上下文来传递下来,需要自己来做一些封装

本文暂时不细讲灰度的实现,后续有时间的话,我专门出一篇文章来说

自定义负载策略

通过"选择实例以及转发"章节的介绍,我们可以发现,loadbalancer主要提供了两个默认策略:

  • RandomLoadBalancer:随机选择
  • RoundRobinLoadBalancer:轮询

同时,他们又是被 @ConditionalOnMissingBean修饰,所以,如果我们想自定义自己的策略规则,我们直接通过 @Configuration和@Bean,注入自己的策略就行,以下是一个简单示例

@Configuration
@LoadBalancerClients(defaultConfiguration = MyBalancerConfiguration.class)
public class MyBalancerConfiguration {/*** 自定义负载均衡器*/@Beanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new MyLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
}

其中MyLoadBalancer是我们自定义的规则

@Slf4j
public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer {private final String serviceId;//服务列表private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;public MyLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> supplier, String serviceId) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = supplier;}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {/*** 进行路由选择*/...}
}

拓展

ReactiveLoadBalancer 和ServiceInstanceListSupplier 区别

概念区分
组件名称作用
ServiceInstanceListSupplier负责提供某个服务的所有可用实例列表(获取并缓存服务实例列表)。
ReactiveLoadBalancer负责基于负载均衡策略,从 ServiceInstanceListSupplier 提供的实例列表中选择一个合适的实例。

简单来说:

  • ServiceInstanceListSupplier 负责提供候选实例列表
  • ReactiveLoadBalancer 负责从这些候选实例中挑选一个最终的实例
核心职责

ServiceInstanceListSupplier

  • 主要作用:提供目标服务的可用 ServiceInstance 列表。

  • 底层实现:它会通过 DiscoveryClient(如 Nacos、Eureka 等)获取服务实例列表,并可能会对实例进行缓存、筛选、排序等操作。

  • 接口定义

    public interface ServiceInstanceListSupplier {Flux<List<ServiceInstance>> get();
    }
    
    • 该接口返回一个 Flux<List<ServiceInstance>>,代表着它是响应式的,能够动态推送服务实例列表更新(比如当 Nacos 服务实例变更时)。
    • 其默认实现 DiscoveryClientServiceInstanceListSupplier 会基于 DiscoveryClient 获取实例信息。
  • 可以自定义过滤、排序规则

    • 例如,你可以扩展 ServiceInstanceListSupplier,让它优先返回健康检查通过的实例,或者特定版本的实例

    • 代码示例:

      @Component
      public class MyCustomInstanceSupplier implements ServiceInstanceListSupplier {private final DiscoveryClient discoveryClient;public MyCustomInstanceSupplier(DiscoveryClient discoveryClient) {this.discoveryClient = discoveryClient;}@Overridepublic Flux<List<ServiceInstance>> get() {return Flux.defer(() -> {List<ServiceInstance> instances = discoveryClient.getInstances("my-service");// 自定义筛选逻辑,比如过滤掉某些状态的实例List<ServiceInstance> filteredInstances = instances.stream().filter(instance -> instance.getMetadata().get("status").equals("UP")).collect(Collectors.toList());return Flux.just(filteredInstances);});}
      }
      

ReactiveLoadBalancer

  • 主要作用:根据某种负载均衡算法,从 ServiceInstanceListSupplier 提供的实例中挑选一个。

  • 底层实现:它的核心方法是:

    public interface ReactiveLoadBalancer<T> {Mono<Response<T>> choose(Request request);
    }
    
    • choose(Request request): 选择一个具体的 ServiceInstance
    • 其中 T 通常是 ServiceInstance,也可以是 Response<ServiceInstance>(包含 metadata)。
  • 默认实现

    • RoundRobinLoadBalancer:基于轮询算法选择实例。
    • RandomLoadBalancer:随机选择一个实例。
    • CachingServiceInstanceListSupplier:基于缓存提高性能,避免频繁查询服务列表。
  • 示例:自定义 ReactiveLoadBalancer

    • 例如,你可以自定义负载均衡算法,优先选择 CPU 负载最低的实例:

      @Component
      public class MyCustomLoadBalancer implements ReactiveLoadBalancer<ServiceInstance> {private final ServiceInstanceListSupplier supplier;public MyCustomLoadBalancer(ServiceInstanceListSupplier supplier) {this.supplier = supplier;}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {return supplier.get().map(instances -> {// 选择 CPU 负载最低的实例ServiceInstance selectedInstance = instances.stream().min(Comparator.comparing(instance -> getCpuLoad(instance))).orElse(null);return new DefaultResponse(selectedInstance);});}private double getCpuLoad(ServiceInstance instance) {// 这里假设实例 metadata 里包含 cpu_loadreturn Double.parseDouble(instance.getMetadata().getOrDefault("cpu_load", "100"));}
      }
      
完整负载均衡流程
  1. 客户端发起请求
    • 例如,Spring Cloud Gateway 或 RestTemplate 发起对 my-service 的调用。
  2. 获取可用实例列表
    • ServiceInstanceListSupplier.get() 从注册中心(Nacos、Eureka)获取 my-service 的所有实例。
  3. 选择负载均衡策略
    • ReactiveLoadBalancer.choose(request) 使用 RoundRobinLoadBalancer 或自定义策略,选择一个实例。
  4. 最终返回一个实例
    • ReactiveLoadBalancer 选出具体的 ServiceInstance,并返回给 WebClientRestTemplate 进行请求。

示意图:

请求 -> ServiceInstanceListSupplier 获取实例列表 -> ReactiveLoadBalancer 选择实例 -> 返回实例给调用方

总结
组件主要作用关键方法关系
ServiceInstanceListSupplier提供某个服务的可用实例列表Flux<List<ServiceInstance>> get()负责获取候选服务实例
ReactiveLoadBalancer依据负载均衡策略选择具体实例Mono<Response<ServiceInstance>> choose(Request request)负责选择最终的服务实例
  • ServiceInstanceListSupplier 负责从服务发现组件(如 Nacos)获取所有可用实例,可能还会做缓存、排序、过滤
  • ReactiveLoadBalancer 负责根据负载均衡策略(轮询、随机、自定义策略)从 ServiceInstanceListSupplier 获取的实例中挑选一个最终的实例。
  • ReactiveLoadBalancer 依赖 ServiceInstanceListSupplier,它无法直接从 Nacos 之类的服务注册中心获取实例,而是必须由 ServiceInstanceListSupplier 提供候选实例。

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

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

相关文章

TMS320F28P550SJ9学习笔记13: 软件I2C_驱动AT24Cxx存储芯片

今日尝试配置软件I2C通信&#xff0c;我的目标通信芯片是AT24C64&#xff0c;相较于AT24C02这样的8位寻址&#xff0c;它是16位寻址的&#xff0c;所以有些不同 文章提供测试代码讲解、完整工程下载、测试效果图 目录 软件I2C引脚初始化&#xff1a; C内联函数改变SCL与SDA的输…

电子电气架构 --- 分布到集中的动カ系统及基于域控制器的架构

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

1216走迷宫

1216走迷宫 ⭐️难度&#xff1a;简单 &#x1f31f;考点&#xff1a;bfs &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {public static void main(String[] …

【TMS570LC4357】之相关问题及解决

背景&#xff1a; 第一次接触TI的芯片&#xff0c;对其中遇见的问题或者不清楚的地方做个记录。 问题及解决方法 1.头文件未包含 添加对应头文件 解决方法 2. error #10008-D: cannot find file “C:/ti/Hercules/SafeTI Diagnostic Library/2.4.0/libs/SafeTILib_TMS570LC…

Vue 中 this 使用指南与注意事项

文章目录 1. this 的基本概念1.1 Vue 实例中的 this1.2 this 指向问题 2. 常见问题与解决方案2.1 生命周期钩子中的 this2.2 方法中的 this2.3 回调函数中的 this 3. 高级用法与技巧3.1 使用箭头函数3.2 绑定 this3.3 使用闭包 4. 性能优化与调试4.1 性能优化策略4.2 调试技巧 …

odbus TCP转Modbus RTU网关快速配置案例

Modbus TCP 转Modbus RTU网关快速配置案例 在工业自动化领域&#xff0c;Modbus 协议以其简洁和高效而著称&#xff0c;成为众多设备通信的首选。 随着技术的发展和应用场景的变化&#xff0c;Modbus 协议也发展出了不同的版本&#xff0c;其中 Modbus TCP 和 Modbus RTU 是两种…

共享内存通信效率碾压管道?System V IPC原理与性能实测

个人主页&#xff1a;敲上瘾-CSDN博客 进程通信&#xff1a; 匿名管道&#xff1a;进程池的制作&#xff08;linux进程间通信&#xff0c;匿名管道... ...&#xff09;-CSDN博客命名管道&#xff1a;命名管道——进程间通信-CSDN博客 目录 一、共享内存的原理 二、信道的建立 …

【net1】tcp,route,iptables,macvlan

文章目录 1.局域网:CSMA/CD2.互联网:ARP,NAT,路由表比映射表复杂3.tcp协议:telnet,tcpdump,syn/accept队列4.linux的route指令:route add4.1 案例:从ubuntu机器ping 199.199.199.199,配置路由使能通5.防火墙iptables:(ip+tables)对网络上数据包通过表的形式进行规…

如何用Deepseek制作流程图?

使用Deepseek制作流程图&#xff0c;本质上是让AI根据你的需求&#xff0c;生成相关流程图的代码&#xff0c;然后在流程图编辑器中渲染&#xff0c;类似于Python一样&#xff0c;ChatGPT可以生成代码&#xff0c;但仍需在IDE中执行。 你知道绘制流程图最高效的工具是什么吗&a…

基于PySide6与CATIA Automation的批量截图处理系统开发实践

引言 本文完整实现了基于PySide6 GUI框架与CATIA Automation技术的批量截图处理系统。系统支持对CATIA文件&#xff08;.CATPart/.CATProduct&#xff09;的自动化截图、图像优化及批量导出&#xff0c;通过模块化架构设计实现了超过200%的效率提升。本文将从技术架构、核心算…

【PyQt5】【Visual Studio】环境配置

前言 最近爱上搞软件编程&#xff0c;今天我就来教学如何进行Python软件编程PyQt5 下载工具 编程环境配置 Visual Studio Python下载最新版本就行 下载完之后呢&#xff0c;简单配置一下环境&#xff0c;Visual Studio的Python环境配置教程有很多可以自己在网上找 我这有Py…

uniapp+vue实现购物车的左滑删除功能

左滑删除 删除功能利用透明的改变在显示删除按钮实现思路代码效果展示 利用scroll滑动容器来实现代码实现效果展示 我们在移动端的电商平台中&#xff0c;一般都是左滑后然后删除按钮出现&#xff0c;用户可以点击删除按钮来进行该商品的删除&#xff0c;这里我分享两种方法来达…

CSSHTML新特性

HTML5 新特性探秘 在 Web 开发的不断演进中&#xff0c;HTML5 带来了一系列令人振奋的新特性&#xff0c;极大地提升了网页的功能和用户体验。今天&#xff0c;我们就来深入探究一下这些新特性。 语义化标签&#xff1a;让网页结构更清晰 语义化标签是 HTML5 的一大亮点。在…

网络爬虫【简介】

我叫补三补四&#xff0c;很高兴见到大家&#xff0c;欢迎一起学习交流和进步 今天来讲一讲爬虫 一、网络爬虫的定义 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;又称为网络蜘蛛、网络机器人等&#xff0c;是一种按照一定规则自动抓取互联网信息的程序或脚本。它…

数字隔离器,如何提升储能系统的安全与效能?

随着全球对光伏、风电等可再生能源需求的持续增长&#xff0c;在全球能源转型的浪潮中&#xff0c;储能技术凭借着可平衡能源供需、提高能源利用效率等优势&#xff0c;已成为实现 “双碳” 目标的核心支撑。据国家能源局公布数据显示&#xff0c;截至2024年底&#xff0c;我国…

AI玩Flappy Bird || 基于Q-Learning和DQN的机器学习

一、游戏介绍 Flappy Bird 游戏需要玩家控制一只小鸟越过管道障碍物。玩家只可以进行“跳跃”或者“不操作”两种操作&#xff0c;即点或不点。点则让小鸟上升一段距离&#xff0c;不点小鸟继续下降。若小鸟碰到障碍物或地面&#xff0c;则游戏失败。 本项目目的是开发一个深层…

【Linux内核系列】:文件系统收尾以及软硬链接详解

&#x1f525; 本文专栏&#xff1a;Linux &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 世界上只有一种个人英雄主义&#xff0c;那么就是面对生活的种种失败却依然热爱着生活 内容回顾 那么在之前的学习中&#xff0c;我们…

【eNSP实战】三层交换机使用ACL实现网络安全

拓图 要求&#xff1a; vlan1可以访问Internetvlan2和vlan3不能访问Internet和vlan1vlan2和vlan3之间可以互相访问PC配置如图所示&#xff0c;这里不展示 LSW1接口vlan配置 vlan batch 10 20 30 # interface Vlanif1ip address 192.168.40.2 255.255.255.0 # interface Vla…

Trae与Builder模式初体验

说明 下载的国际版&#xff1a;https://www.trae.ai/ 建议 要选新模型 效果 还是挺不错的&#xff0c;遇到问题反馈一下&#xff0c;AI就帮忙解决了&#xff0c;真是动动嘴&#xff08;打打字就行了&#xff09;&#xff0c;做些小的原型效果或演示Demo很方便呀&#xff…

Canoe Panel常用控件

文章目录 一、Panel 中控件分类1. 指示类控件2. 功能类控件3. 信号值交互类控件4. 其他类控件 二、控件使用方法1. Group Box 控件2. Input/Output Box控件3. Static Text控件4. Button控件5. Switch/Indicator 控件 提示&#xff1a;Button 和 Switch 的区别参考 一、Panel 中…