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

发布于:2025-03-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

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 {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

使用 RestTemplate

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private 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: localhost
  client:
    # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为false
    fetch-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;   
// 注意不要引错包
	@Autowired
    private 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();
        // 替换之前写死的 url
        String 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 {
    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    // 计数器
    private AtomicInteger count = new AtomicInteger(1);

    // 将实例提取出来
    private List<ServiceInstance> instances;

    @PostConstruct
    public 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
    @LoadBalanced
    public 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 {
    @Bean
    ReactorLoadBalancer<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
        @LoadBalanced
        public 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 方法:

	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
        // 拿到 uri 
		final URI originalUri = request.getURI();
        // 拿到 host
		String 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


本文到这里就结束啦~

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到