Spring Cloud LoadBalancer 原理与实践

news2025/3/15 17:31:57

背景

当前我们的微服务架构基于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 ServiceInstanceListSupplier
		extends Supplier<Flux<List<ServiceInstance>>> {

	// 服务id
	String 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;

		@Deprecated
		public 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。
		@Override
		public String getServiceId() {
			return serviceId;
		}
		//返回固定的实例列表,不会随 Nacos 或 Eureka 变化。
		@Override
		public 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 DelegatingServiceInstanceListSupplier
		implements 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
	@ConditionalOnMissingBean
	public 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
	@ConditionalOnMissingBean
	public LoadBalancerZoneConfig zoneConfig(Environment environment) {
		return new LoadBalancerZoneConfig(
				environment.getProperty("spring.cloud.loadbalancer.zone"));
	}

	@ConditionalOnMissingBean
	@Bean
	public LoadBalancerClientFactory loadBalancerClientFactory() {
		LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
		clientFactory.setConfigurations(
				this.configurations.getIfAvailable(Collections::emptyList));
		return clientFactory;
	}

}

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

执行最终实例

BlockingLoadBalancerClient

public class BlockingLoadBalancerClient implements LoadBalancerClient {
	...
	@Override
	public <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请求
	@Override
	public <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;
	}

	@Override
	public URI reconstructURI(ServiceInstance serviceInstance, URI original) {
		return LoadBalancerUriTools.reconstructURI(serviceInstance, original);
	}
	
	@Override
	public 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 {
    
    /**
     * 自定义负载均衡器
     */
    @Bean
    public 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;
    }
    
    @Override
    public 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;
          }
      
          @Override
          public 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;
          }
      
          @Override
          public 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_load
              return 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.coloradmin.cn/o/2315574.html

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

相关文章

Vmware下的openEuler

1.下载openEuler操作系统镜像 https://repo.openeuler.org/openEuler-20.03-LTS/ISO/ 2.在VM新建虚拟机 3.虚拟机联网 我是出现了没有网络&#xff0c;ping不通的问题 参考&#xff1a;https://blog.csdn.net/FHY26828/article/details/140941234 修改文件&#xff1a; 在…

【瞎折腾/Dify】使用docker离线部署Dify

文章目录 说在前面安装Docker(外网)获取Dify源码(外网)拉取docker镜像(外网)导出镜像(内网)导入镜像(内网)运行问题 说在前面 外网操作系统&#xff1a;windows内网操作系统&#xff1a;ubuntu外网docker desktop版本&#xff1a;4.29.0外网docker版本&#xff1a;version 26.0…

Java EE Web环境安装

Java EE Web环境安装 一、JDK安装与测试&#xff08;Windows环境&#xff09; 1. 安装JDK 官网下载&#xff1a;Oracle JDK&#xff08;选择Windows x64 Installer&#xff09;双击安装包&#xff0c;按向导完成安装 ​ 2. 环境变量配置 右键【此电脑】→【属性】→【高级…

大语言模型基础之‘显存优化‘

上一篇可扩展的训练技术(二)中&#xff0c;我们介绍了零冗余优化器&#xff08;Zero Redundancy Optimizer, Zero&#xff09;&#xff0c;该技术由DeepSpeed代码库提出&#xff0c;主要用于解决数据并行中的模型冗余技术&#xff0c;即在数据并行训练中&#xff0c;每个GPU上都…

【Nexus】Maven 私服搭建以及上传自己的Jar包

Nexus 安装 docker run -d -uroot --name nexus3 --restartalways -p 8081:8081 -v /data/nexus-data/blobs:/nexus-data/blobs -v /etc/localtime:/etc/localtime sonatype/nexus3这里也提供一下docker-composer的方法 .env 文件 VERSIONlatest CONTAINER_NAMECONTAINER_N…

冠珠瓷砖×郭培“惟质致美”品质主题片上映,讲述高定艺术背后的致美品质故事

168年前&#xff0c;一位英国服装设计师&#xff0c;开创了「高级定制」的先河。时至今日&#xff0c;从服装到各行各业「高级定制」始终代表着对完美的极致追求&#xff0c;成为了行业至高境界的象征。 被誉为“中国高定第一人”&#xff0c;高级定制服装设计师郭培&#xff0…

3DS模拟器使用(pc+安卓)+金手指+存档互传

1、引言 3ds模拟器已经能够在手机端近乎完美模拟了&#xff0c;那么多的3ds游戏&#xff0c;比玩手机游戏舒服多了。 本人是精灵宝可梦的老玩家&#xff0c;从第一世代就一直在玩&#xff0c;刚耍完NDS的第五世代&#xff0c;黑白系列。现在到宝可梦XY了&#xff0c;需要在3d…

从以太网 II 到 VLAN 和 Jumbo Frame:数据帧格式解读

以太网数据帧是计算机网络通信的基本单位&#xff0c;在不同的应用场景中&#xff0c;它的格式有所不同。根据协议标准和用途&#xff0c;以太网数据帧主要包括以太网 II 帧、IEEE 802.3 帧、IEEE 802.1Q VLAN 帧等七种主要类型。为了更好地理解以太网的通信机制&#xff0c;我…

X86 RouterOS 7.18 设置笔记六:端口映射(IPv4、IPv6)及回流问题

X86 j4125 4网口小主机折腾笔记五&#xff1a;PVE安装ROS RouterOS X86 RouterOS 7.18 设置笔记一&#xff1a;基础设置 X86 RouterOS 7.18 设置笔记二&#xff1a;网络基础设置(IPV4) X86 RouterOS 7.18 设置笔记三&#xff1a;防火墙设置(IPV4) X86 RouterOS 7.18 设置笔记四…

69.Harmonyos NEXT图片预览组件应用实践(二):电商、内容与办公场景

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; Harmonyos NEXT图片预览组件应用实践&#xff08;二&#xff09;&#xff1a;电商、内容与办公场景 文章目录 Harmonyos NEXT图片预览组件应用实践…

31.Harmonyos Next仿uv-ui 组件NumberBox 步进器组件异步操作处理

Harmonyos Next仿uv-ui 组件NumberBox 步进器组件异步操作处理 文章目录 Harmonyos Next仿uv-ui 组件NumberBox 步进器组件异步操作处理1. 组件介绍2. 效果展示3. 异步操作处理3.1 异步初始化3.2 异步值更新 4. 完整示例代码5. 知识点讲解5.1 异步操作基础5.2 异步操作中的状态…

mac安装python没有环境变量怎么办?zsh: command not found: python

在mac电脑上,下载Python安装包进行安装之后,在终端中,输入python提示: zsh: command not found: python 一、原因分析 首先,这个问题不是因为python没有安装成功的原因,是因为python安装的时候,没有为我们添加环境变量导致的,所以我们只需要,在.zshrc配置文件中加上环…

使用DeepSeek制作可视化图表和流程图

用DeepSeek来制作可视化图表&#xff0c;搭配python、mermaid、html来实现可视化&#xff0c;我已经测试过好几种场景&#xff0c;都能实现自动化的代码生成&#xff0c;效果还是不错的&#xff0c;流程如下。 统计图表 &#xff08;搭配Matplotlib来做&#xff09; Python中的…

jmeter-sample

jmeter-sample http request:接口测试常用请求参数ParametersBody DataFiles Upload jdbc request配置JDBC Connection Configuration创建JDBC Requst请求 http request:接口测试常用 请求参数 Parameters 常见于get请求&#xff0c;与拼在接口后面是一样的效果&#xff1a;如…

C++之文字修仙小游戏

1 效果 1.1 截图 游戏运行&#xff1a; 存档&#xff1a; 1.2 游玩警告 注意&#xff01;不要修改装备概率&#xff0c;装备的概率都是凑好的数字。如果想要速升&#xff0c;修改灵石数量 2 代码 2.1 代码大纲 1. 游戏框架与初始化 控制台操作&#xff1a;通过 gotoxy() …

MacOS 15.3.1 安装 GPG 提示Error: unknown or unsupported macOS version: :dunno

目录 1. 问题锁定 2. 更新 Homebrew 3. 切换到新的 Homebrew 源 4. 安装 GPG 5. 检查 macOS 版本兼容性 6. 使用 MacPorts 或其他包管理器 7. 创建密钥&#xff08;生成 GPG 签名&#xff09; 往期推荐 1. 问题锁定 通常是因为你的 Homebrew 版本较旧&#xff0c;或者你…

硬件驱动——51单片机:独立按键、中断、定时器/计数器

目录 一、独立按键 1.原理 2.封装函数 3.按键控制点灯 数码管 二、中断 1.原理 2.步骤 3.中断寄存器IE 4.控制寄存器TCON 5.打开外部中断0和1 三、定时器/计数器 1.原理 2.控制寄存器TCON 3.工作模式寄存器TMOD 4.按键控制频率的动态闪烁 一、独立按键 1…

P1259 黑白棋子的移动【java】【AC代码】

有 2n 个棋子排成一行&#xff0c;开始为位置白子全部在左边&#xff0c;黑子全部在右边&#xff0c;如下图为 n5 的情况&#xff1a; 移动棋子的规则是&#xff1a;每次必须同时移动相邻的两个棋子&#xff0c;颜色不限&#xff0c;可以左移也可以右移到空位上去&#xff0c;但…

67.Harmonyos NEXT 图片预览组件之性能优化策略

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; Harmonyos NEXT 图片预览组件之性能优化策略 文章目录 Harmonyos NEXT 图片预览组件之性能优化策略效果预览一、性能优化概述1. 性能优化的关键指标…

Windows下安装Git客户端

① 官网地址&#xff1a;https://git-scm.com/。 ② Git的优势 大部分操作在本地完成&#xff0c;不需要联网&#xff1b;完整性保证&#xff1b;尽可能添加数据而不是删除或修改数据&#xff1b;分支操作非常快捷流畅&#xff1b;与Linux 命令全面兼容。 ③ Git的安装 从官网…