SpringCloud 学习笔记1(Spring概述、工程搭建、注册中心、负载均衡、 SpringCloud LoadBalancer)

文章目录

  • SpringCloud
    • SpringCloud 概述
      • 集群和分布式
        • 集群和分布式的区别和联系
      • 微服务
        • 什么是微服务?
        • 分布式架构和微服务架构的区别
        • 微服务的优缺点?
        • 拆分微服务原则
      • 什么是 SpringCloud ?
        • 核心功能与组件
    • 工程搭建
      • 父项目的 pom 文件
    • 注册中心
      • RestTemplate
      • 注册中心介绍
      • CAP 理论
      • Eureka
        • 添加依赖
        • 配置文件
        • 启动类
        • 查看
        • 服务注册
          • 添加依赖
          • 配置文件
          • 查看
        • 服务发现
          • 依赖、配置
          • 修改代码
        • Eureka 和 Zookeeper 区别
    • 负载均衡
      • 多次在不同的端口号上开启同一个服务
      • 出现的问题
      • 解决问题
      • 负载均衡正式介绍
        • 什么是负载均衡
        • 负载均衡的一些实现
          • 服务端负载均衡
          • 客户端负载均衡
        • SpringCloud LoadBalancer
          • 负载均衡策略
          • LoadBalancer 原理
            • 随机选择策略
            • 轮询策略
          • 服务部署

SpringCloud

SpringCloud 概述

集群和分布式

集群:是将一个系统完整的部署到多个服务器上,每个服务器都能提供系统的所有服务,多个服务器通过负载均衡调度完成任务,每个服务器称为集群的节点。

分布式:是将一个系统拆分成多个子系统,多个子系统部署在多个服务器上,多个服务器上的子系统协同合作完成一个特定任务。

集群和分布式的区别和联系
  1. 集群是多个计算机做同样的事情,分布式是多个计算机做不同的事。
  2. 集群的每一个节点的功能是相同的,并且是可以替代的。分布式也是多个节点组成的系统,但是每个节点完成的任务是不同的,一个节点出现问题,这个业务就无法访问了。
  3. 分布式和集群在实践中,很多时候都是相互配合使用的。比如分布式的某一个节点,可能由一个集群来代替。分布式架构大多数是建立在集群上的。所以实际的分布式架构中并不会把分布式和集群单独区分,而是统称:分布式架构。

微服务

什么是微服务?

微服务是一种经过良好架构设计的分布式架构方案。

一个服务只对应一个单一的功能,只做一件事,这个服务可以单独部署运行。

分布式架构和微服务架构的区别

分布式:服务拆分,拆了就行。

微服务:指非常微小的服务,更细粒度的垂直拆分,通常指不能再拆的服务。

分布式架构侧重于压力的分散,强调的是服务的分散化,微服务侧重于能力的分散,更强调服务的专业化和精细分工。

微服务的优缺点?

优点:

image-20250311173842098

缺点:

image-20250311173920969

拆分微服务原则
  1. 单一职责原则

    单一职责原则原本是面向对象程序设计中的一个基本原则,它指的是一个类应该专注于单一功能。

    在微服务架构中,一个微服务也应该只负责一个功能或业务领域,每个服务应该有清晰的定义和边界,只关注自己的特定业务领域。

  2. 服务自治

    服务自治是指每个微服务都应该具备高度自治的能力,即每个服务要能做到独立开发,独立测试, 独立构建, 独立部署,独立运行。

  3. 单向依赖

    微服务之间需要做到单向依赖,严禁循环依赖,双向依赖。

    image-20250311174451006

    如果一些场景确实无法避免循环依赖或者双向依赖,,可以考虑使用消息队列等其他方式来实现。

什么是 SpringCloud ?

Spring Cloud 是一套基于 Spring Boot 的微服务开发工具集,用于简化分布式系统(如微服务架构)的构建、部署和管理。它整合了多种开源组件,提供了一站式解决方案,帮助开发者快速实现服务治理、配置管理、负载均衡、熔断降级等分布式系统中的常见问题。

简单的说,Spring Cloud 就是分布式微服务架构的一站式解决方案。

核心功能与组件
  1. 服务注册与发现
    • 组件:EurekaNacosConsul
    • 功能:服务自动注册到注册中心,并通过服务名实现动态发现,避免硬编码服务地址。
  2. 负载均衡
    • 组件:RibbonLoadBalancer
    • 功能:在多个服务实例间分配请求,支持轮询、随机等策略。
  3. 服务调用
    • 组件:OpenFeign
    • 功能:声明式的 HTTP 客户端,简化服务间的 RESTful 调用。
  4. 熔断与容错
    • 组件:HystrixResilience4jSentinel
    • 功能:防止服务雪崩,提供降级逻辑和故障隔离。
  5. 配置中心
    • 组件:Spring Cloud ConfigNacos
    • 功能:集中管理配置文件,支持动态更新。
  6. API 网关
    • 组件:Spring Cloud GatewayZuul
    • 功能:统一入口,处理路由、鉴权、限流等跨服务功能。
  7. 分布式链路追踪
    • 组件:Sleuth + Zipkin
    • 功能:追踪请求在微服务间的调用路径,便于排查问题。

工程搭建

父项目的 pom 文件

指定父项目的打包方式:

image-20250311175319332

添加依赖:

image-20250311175027099

子项目被创建时,父项目的 pom 文件中会自动添加

image-20250311175514057

上面的代码表示这里有两个子项目(模块),分别是 order-service 和 product-service 。

注册中心

RestTemplate

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它是一个同步的 REST API 客户端,提供了常见的 REST 请求方案的模版。

在项目中,当我们需要远程调用一个 HTTP 接口时,我们经常会用到 RestTemplate 这个类。这个类是 Spring 框架提供的一个工具类。

定义 RestTemplate

@Configuration
public class BeanConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}

使用 RestTemplate

@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);// 通过 RestTemplate 从指定 URL 获取 ProductInfo 对象String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}

在前面的示例中 URL 是写死的。写死这个事情做的不好,如果服务器的 IP 发生变化,我们还得把所有 IP 都修改,太麻烦了,而且没有技术含量。

String url = "http://127.0.0.1:9090/product/";

注册中心介绍

有没有什么办法来解决这个问题呢?

我们可以这样做:

当服务 启动/变更 时, 向注册中⼼报道。注册中⼼记录应⽤和 IP 的关系。

调⽤⽅调⽤时,先去注册中⼼获取服务⽅的 IP,再去服务⽅进⾏调⽤。

image-20250311212947467

image-20250311213409451

CAP 理论

image-20250311213758583

  • 一致性(C):CAP 理论中的一致性,指的是强一致性。所有节点在同一时间具有相同的数据。

    image-20250311214959974

    强一致性:主库和从库不论何时,服务器对外提供的服务都是一致的。

    弱一致性:随着时间的推移,主库和从库最终达到了一致性。

  • 可用性(A):保证每个请求都有响应。

  • 分区容错性(P):当出现网络分区后,系统仍然能够对外提供服务。

    网络分区:指分布式系统中,由于网络故障导致集群中的节点被分割成多个孤立的子集,子集之间的节点无法正常通信,但子集内部的节点仍然可以正常通信。这种现象也被称为“脑裂”(Split-Brain)。

    举个例子

    假设有一个分布式系统,由 5 个节点(A、B、C、D、E)组成,它们之间通过网络通信。如果由于网络故障,节点 A 和 B 之间的网络断开,那么可能会形成两个分区:

    • 分区 1:节点 A、B
    • 分区 2:节点 C、D、E

    此时,分区 1 和分区 2 之间的节点无法通信,但分区内部的节点仍然可以正常通信。

在分布式系统中,系统间的⽹络不能100%保证健康, 服务⼜必须对外保证服务. 因此 分区容错性(P) 不可避免. 那就只能在 C 和 A 中选择⼀个. 也就是 CP 或者 AP 架构。

正常情况:

image-20250311215348627

网络异常:

image-20250311215423635

CP架构:为了保证分布式系统对外的数据⼀致性,于是选择不返回任何数据。

AP架构:为了保证分布式系统的可⽤性,节点2返回V0版本的数据(即使这个数据不正确)。

关于CAP的更多信息,可以看看这篇文章:一文看懂|分布式系统之CAP理论-腾讯云开发者社区-腾讯云

Eureka

添加依赖
 <!--  给客户端用  --><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies><!--  给服务器用  -->
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies><!--  借助 Maven 打包  --><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
配置文件
# Eureka相关配置
# Eureka 服务
server:port: 10010
spring:application:# 这个应用的名称name: eureka-server
eureka:instance:# 主机的名称hostname: localhostclient:# 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为falsefetch-registry: false# 表示是否将自己注册到Eureka Server,默认为true.register-with-eureka: false service-url:# 设置Eureka Server的地址,查询服务和注册服务都需要依赖这个地址defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类
@EnableEurekaServer  // 开启 Eureka 的功能
@SpringBootApplication
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
}
查看
http://127.0.0.1:10010/

image-20250314164624808

服务注册
添加依赖
        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
配置文件
spring:application:# 配置应用名称name: product-service#Eureka Client
eureka:client:service-url:# 注册到哪里 / 从哪里拿相关信息defaultZone: http://127.0.0.1:10010/eureka/
查看
http://127.0.0.1:10010/

image-20250314164722794

服务发现
依赖、配置

同服务注册。

修改代码
import org.springframework.cloud.client.discovery.DiscoveryClient;   
// 注意不要引错包@Autowiredprivate DiscoveryClient discoveryClient;// 从 Eureka 中获取服务列表,括号内写应用名称List<ServiceInstance> instances = discoveryClient.getInstances("product-service");String uri = instances.get(0).getUri().toString();// 替换之前写死的 url String url = uri + "/product/" + orderInfo.getProductId();
Eureka 和 Zookeeper 区别

Eureka 和 Zookeeper 都是用于服务注册和服务发现的工具,区别如下:

  1. Eureka 基于 AP 原则,保证高可用。Zookeeper 基于 CP 原则,保证数据一致性。
  2. Eureka 每个节点都是均等的,Zookeeper 的节点区分 Leader 和 Follower 或 Observer,如果 Zookeeper 的 Leader 发生故障时,需要重新选举,选举过程集群会有短暂时间的不可用。

负载均衡

多次在不同的端口号上开启同一个服务

image-20250314172405488

点击 Services 并添加应用

image-20250314172548516

选择 Application

image-20250314172632852

复制你要多开的服务

image-20250314172710731

设置端口号,设置完毕后点击 Apply。

image-20250314172839615

可以看到,已经配置好了。

image-20250314173002148

出现的问题

当我们进行多次访问时,每次的 discoveryClient.getInstances(“product-service”); 拿到的列表是不固定的。

    public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);// 从 Eureka 中获取服务列表List<ServiceInstance> instances = discoveryClient.getInstances("product-service");String uri = instances.get(0).getUri().toString();// 替换之前写死的 urlString url = uri + "/product/" + orderInfo.getProductId();log.info("远程调用 url:{}", url);ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}

image-20250314173928563

显然这并不是很合理。

解决问题

假设我们想让请求平均分配到每个端口上。可以使用以下方法:

image-20250314174244230

修改代码:

package com.demo.order.service;import com.demo.order.mapper.OrderMapper;
import com.demo.order.model.OrderInfo;
import com.demo.order.model.ProductInfo;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;/*** @author hanzishuai* @date 2025/03/07 19:19* @Description*/
@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;// 计数器private AtomicInteger count = new AtomicInteger(1);// 将实例提取出来private List<ServiceInstance> instances;@PostConstructpublic void init() {// 从 Eureka 中获取服务列表instances = discoveryClient.getInstances("product-service");}// 这是之前的写法:    
//        public OrderInfo selectOrderById(Integer orderId) {
//        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//        String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
//    
//        // 从 Eureka 中获取服务列表,这里每次请求拿到的 instances 是不固定的 
//        List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//        String uri = instances.get(0).getUri().toString();
//    
//        // 替换之前写死的 url
//        String url = uri + "/product/" + orderInfo.getProductId();
//        log.info("远程调用 url:{}", url);
//        ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
//        orderInfo.setProductInfo(productInfo);
//        return orderInfo;
//    }public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);// 实现平均分配int index = count.getAndIncrement() % instances.size();String uri = instances.get(index).getUri().toString();String url = uri + "/product/" + orderInfo.getProductId();log.info("远程调用 url:{}", url);ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}
}

但是上述写法会带来一个新的问题:当实例发生变化,这里的服务并不能实时的感知到。

不要较真,在这里只是为了演示一下。

更改后的效果:

image-20250314180245882

负载均衡正式介绍

什么是负载均衡

负载均衡用来在多个机器或者其他资源中,按照一定的规则合理分配负载。

比如说,有很多请求和很多服务器,负载均衡就是把这些请求合理的分配到各个服务器上。

负载均衡的一些实现
服务端负载均衡

服务端负载均衡就是在服务端进行负载均衡算法的分配。

以 Nginx 为例,请求先到达 Nginx 负载均衡器,然后通过负载均衡算法,在多个服务器之间选一个进行访问。

image-20250314182324386
客户端负载均衡

服务端负载均衡就是在客户端进行负载均衡算法的分配。

以 SpringCloud 的 Ribbon 为例,请求发送到客户端,客户端从注册中心获取服务器列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问。

image-20250314183108271
SpringCloud LoadBalancer

加上 @LoadBalanced 注解

@Configuration
public class BeanConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
}

修改代码

    public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);// 修改前// String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();// 修改后String url = "http://product-service/product/" + orderInfo.getProductId();log.info("远程调用 url:{}", url);ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}

效果:

image-20250314203553542

image-20250314203658809

image-20250314203605370

负载均衡策略

SpringCloud LoadBalancer 仅支持两种负载均衡策略:

  1. 轮询策略: 指服务器轮流处理用户的请求.
  2. 随机选择: 随机选择一个后端服务器来处理请求.

自定义负载均衡策略

public class CustomLoadBalancerConfiguration {@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);}
}

使用方法:

  1. 需要在关联负载均衡策略的配置类上添加 @LoadBalancerClient 或者 @LoadBalancerClients 注解.

    // name 表示要对那个服务生效, configuration 表示你采取的负载均衡策略是什么.
    @LoadBalancerClient(name = "product-service",configuration = CustomLoadBalancerConfiguration.class)
    @Configuration
    public class BeanConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}
    }
    
  2. 自定义负载均衡策略的配置类(如 CustomLoadBalancerConfiguration)上不能使用 @Configuration 注解.

    原因: 如果 CustomLoadBalancerConfiguration 类被标记为 @Configuration,并且位于主应用程序组件扫描的路径下,它会被 Spring 自动加载为一个配置类。而当通过 @LoadBalancerClient 的 configuration 属性引用它时,可能会导致 Spring 尝试再次加载它,从而产生冲突或重复的 bean 定义。

  3. 自定义负载均衡策略的配置类(如 CustomLoadBalancerConfiguration)必须能被 Spring 容器发现。

是不是感觉与第三条第二条有矛盾?

  • 在之前的回答中提到,CustomLoadBalancerConfiguration 不能添加 @Configuration 注解,这是为了避免被 Spring 自动扫描到后重复加载。
  • 但同时又需要确保该类能被 Spring 容器发现,这里的矛盾需要通过以下方式解决:
    • 通过 @LoadBalancerClient(configuration = ...) 显式引用该类,而不是依赖组件扫描。上面的 BeanConfig 采用的就是这种方法。
    • 确保 CustomLoadBalancerConfiguration 不在主应用的扫描范围内,但能被 @LoadBalancerClient 正确引用。
LoadBalancer 原理

tip: 按 Ctrl + alt + ← 或者 → 可以快速定位上/下次查看的位置

在 LoadBalancerInterceptor 中有一个 intercept 方法:

	@Overridepublic ClientHttpResponse intercept(final HttpRequest request, final byte[] body,final ClientHttpRequestExecution execution) throws IOException {// 拿到 uri final URI originalUri = request.getURI();// 拿到 hostString serviceName = originalUri.getHost();Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));}

它会拦截所有的请求。

它做了三件事:

  1. 拿到 uri,也就是 http://product-service/product/1001
  2. 拿到 host,也就是 product-service
  3. 执行

接下来具体看看 this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution))它干了什么。

   public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {String hint = this.getHint(serviceId);LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter(request, this.buildRequestContext(request, hint));Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);supportedLifecycleProcessors.forEach((lifecycle) -> {lifecycle.onStart(lbRequest);});// 通过 choose 方法返回了一个应用ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);if (serviceInstance == null) {supportedLifecycleProcessors.forEach((lifecycle) -> {lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, new EmptyResponse()));});throw new IllegalStateException("No instances available for " + serviceId);} else {return this.execute(serviceId, serviceInstance, lbRequest);}}

接下来看一下 choose 方法

    public <T> ServiceInstance choose(String serviceId, Request<T> request) {// 根据应用名称获取负载均衡策略ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId);if (loadBalancer == null) {return null;} else {// 如果 loadBalancer 不为空,这里又进行了一次选择Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block();return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer();}}

如果 loadBalancer 不为空,这里又进行了一次选择接下来进入 Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block() 中的 choose 看看,

可以看到它有两个实现,一个是RandomLoadBalancer,一个是RoundRobinLoadBalancer

image-20250314222100838

随机选择策略

先来看一下 RandomLoadBalancer

    public Mono<Response<ServiceInstance>> choose(Request request) {// 做了一些处理ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);// 根据请求获取到服务列表return supplier.get(request).next().map((serviceInstances) -> {// 对服务列表进行处理return this.processInstanceResponse(supplier, serviceInstances);});}

进入 processInstanceResponse 看一下

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());}return serviceInstanceResponse;}

getInstanceResponse 看一下

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else {// 生成随机数int index = ThreadLocalRandom.current().nextInt(instances.size());// 根据生成的随机数,在服务列表中选择ServiceInstance instance = (ServiceInstance)instances.get(index);// 进行下一步的处理return new DefaultResponse(instance);}}
轮询策略

进入RoundRobinLoadBalancer

image-20250314223558314

    public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map((serviceInstances) -> {return this.processInstanceResponse(supplier, serviceInstances);});}

进入 processInstanceResponse

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());}return serviceInstanceResponse;}

进入 getInstanceResponse

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else if (instances.size() == 1) {return new DefaultResponse((ServiceInstance)instances.get(0));} else {// 计数器int pos = this.position.incrementAndGet() & 2147483647;// 通过计数器 % instances.size() 来拿到坐标ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());return new DefaultResponse(instance);}}
服务部署

参考 博客系统笔记总结 2( Linux 相关) 中的部署 Web 项目到 Linux


本文到这里就结束啦~

在这里插入图片描述

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

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

相关文章

1140:验证子串--next.data()、KMP和find

1140&#xff1a;验证子串--KMP 题目 解析next.data()KMP代码Find代码 题目 解析 对于字符串的匹配常见的KMP算法【面试常考】 KMP中需要注意的是&#xff1a;应该从下标1开始遍历&#xff0c;因为下标0前面无值&#xff0c;不能匹配next 固在循环外应初始next[0]0;//易忘点 …

Python 实现大文件的高并发下载

项目背景 基于一个 scrapy-redis 搭建的分布式系统&#xff0c;所有item都通过重写 pipeline 存储到 redis 的 list 中。这里我通过代码演示如何基于线程池 协程实现对 item 的中文件下载。 Item 结构 目的是为了下载 item 中 attachments 保存的附件内容。 {"crawl_tim…

ubuntu中用docker下载opengauss

1.安装docker sudo apt install docker.io2.拉取opengauss镜像 sudo docker pull enmotech/opengauss3.创建容器 sudo docker run --name opengauss --privilegedtrue -d -e GS_PASSWORDEnmo123 enmotech/opengauss:latest3.5.如果容器停止运行&#xff08;比如关机了&#…

从零基础到能独立设计单片机产品,一般需要经历哪些学习阶段?

相信很多人&#xff0c;内心都有“钢铁侠”的幻想&#xff0c;成为能写程序&#xff0c;能设计硬件&#xff0c;能设计结构&#xff0c;能焊接的全能型人才。 上次徐工问我&#xff0c;如果你财富自由了&#xff0c;想去做啥&#xff1f; 我说出来&#xff0c;可能大家都不信&a…

cursor中git提交记录出现 签出(已分离)

我当时在cursor中的git记录右键点击 签出(已分离) 就导致最左边的记录图标的颜色由蓝色变为了橙色 后面提交的记录都不在显示本地分支和远程分支 创建新分支&#xff1a;在您当前的分离HEAD状态下&#xff0c;创建一个新的分支来保存这些提交。 git checkout -b new-branch-nam…

软件测试之测试用例

1. 什么是测试用例 测试用例&#xff08;TestCase)是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 设计测试⽤例原则⼀&#xff1a; 测试⽤例中⼀个必需部分是对预期输出或结果进⾏定义 使…

Unity2D 井字棋

Unity版本2022.3 场景布置 其中可以通过给Board对象添加Grid Layout Group&#xff0c;然后设置每个子物体所占宽高快速排整齐。用完删掉。每个落子的方格ChessBox都是一个Button。 根据Board的宽高除以三即可。 然后隐藏按钮&#xff0c;通过设置alpha值实现。 将ChessBox的…

专题三搜索插入位置

1.题目 题目分析&#xff1a; 给一个目标值&#xff0c;然后要在排序的整数数组中&#xff0c;找到跟目标值一样的&#xff0c;如果没有就把这个值插入进去&#xff0c;然后返回插入后的下标。 2.算法原理 根据题目的时间复杂度可以知道要用二分&#xff0c;开始划分区域&…

正式进入linux 1.0

切记&#xff1a;在Linux中空格很重要 回车键也很重要&#xff0c;不要按两次回车键 ls是显示当前所有文件夹 具体解释&#xff1a; 前面的东西是用户名 后面的是设备名&#xff08;计算机名&#xff09; 这是因为linux允许不同用户在终端下进行操作&#xff0c;这么做可以…

分页查询的实现

目录 前言 一.问题描述 二.后端实现步骤 2.1配置PageHelper插件 ①导入依赖 ②在application.yml配置文件中添加相关配置 2.2编写一个入门的程序&#xff0c;体验分页过程 2.3定义一个vo&#xff0c;用来收集分页后的所有信息 2.4修改serviceImpl层的代码 2.5动态设…

16003. orin camera 相机驱动源码 imx477分析记录

文章目录 1 背景2 原理图2.1 CAM_MUX_SEL 4 lane 选通2.2 J21 和 J20 原理图3 驱动源码及设备树3.1 子设备树 tegra234-p3768-camera-rbpcv3-imx477.dtsi3.2 顶层设备树 tegra234-camera-rbpcv3-imx477.dtsi3.2.1 tegra-capture-vi 视频输入子系统节点配置.3.2.2 host1x 主机控…

无标签数据增强+高效注意力GAN:基于CARLA的夜间车辆检测精度跃升

目录 一、摘要 二、引言 三、框架 四、方法 生成合成夜间数据 昼夜图像风格转换 针对夜间图像的无标签数据增强技术 五、Coovally AI模型训练与应用平台 六、实验 数据 图像风格转换 夜间车辆检测和分类 结论 论文题目&#xff1a;ENHANCING NIGHTTIME VEHICLE D…

开源工具利器:Mermaid助力知识图谱可视化与分享

在现代 web 开发中&#xff0c;可视化工具对于展示流程、结构和数据关系至关重要。Mermaid 是一款强大的 JavaScript 工具&#xff0c;它使用基于 Markdown 的语法来呈现可定制的图表、图表和可视化。对于展示流程、结构和数据关系至关重要。通过简单的文本描述&#xff0c;你可…

C++算法学习2:二分算法精讲

一、实数二分法回顾 1.1问题背景 在1~2的范围内找到一个x&#xff0c;使得式子5x2 -9x 1 的绝对值<10-9&#xff08;即无限接近0&#xff09; 要求&#xff1a;x精确到小数点后9位。 换句话说也就是求&#xff1a;就是求方程 5x2- 9x 1 0 在1~2内的近似解 1.2怎么找到…

手写一个简易版的tomcat

Tomcat 是一个广泛使用的开源 Servlet 容器&#xff0c;用于运行 Java Web 应用程序。深入理解 Tomcat 的工作原理对于 Java 开发者来说是非常有价值的。本文将带领大家手动实现一个简易版的 Tomcat&#xff0c;通过这个过程&#xff0c;我们可以更清晰地了解 Tomcat 是如何处理…

object.assign和扩展运算法是深拷贝还是浅拷贝,两者区别

object.assign和扩展运算法是深拷贝还是浅拷贝&#xff0c;两者区别 1. 浅拷贝的本质2. Object.assign 和扩展运算符的区别‌3. 具体场景对比‌合并多个对象‌‌复制数组‌‌处理默认值‌ ‌4. 如何实现深拷贝&#xff1f;JSON.parse(JSON.stringify(obj))‌‌递归深拷贝函数第…

X-CLIP和X-FLORENCE论文解读

1.研究背景 尽管已有研究探索了如何将语言-图像模型迁移到其他下游任务&#xff08;如点云理解和密集预测&#xff09;&#xff0c;但视频识别领域的迁移和适应性研究还不够充分。例如&#xff0c;ActionCLIP提出了一种“预训练、提示和微调”的框架用于动作识别&#xff0c;但…

微信小程序刷题逻辑实现:技术揭秘与实践分享

页面展示&#xff1a; 概述 在当今数字化学习的浪潮中&#xff0c;微信小程序以其便捷性和实用性&#xff0c;成为了众多学习者刷题备考的得力工具。今天&#xff0c;我们就来深入剖析一个微信小程序刷题功能的实现逻辑&#xff0c;从代码层面揭开其神秘面纱。 小程序界面布局…

Android UI 组件系列(二):Button 进阶用法

引言 在上一篇博客中&#xff0c;我们介绍了 Button 的基本用法和常见属性&#xff0c;掌握了 Button 的基础知识。然而&#xff0c;在实际开发中&#xff0c;Button 远不止于简单的点击功能&#xff0c;它还可以支持不同的变体、丰富的自定义样式&#xff0c;以及更灵活的状态…

【云馨AI-大模型】RAGFlow功能预览:Dify接入外部知识库RAGFlow指南

介绍 Dify介绍 开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力&#xff0c;轻松构建和运营生成式 AI 原生应用。比 LangChain 更易用。官网&#xff1a;https://dify.ai/zh RAGFlow介绍 RAGFlow 是一款基于深度文档理解构建的…