⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/weixin_45226479 「小陈爱coding」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

一、 引言:从单点故障的痛说起

想象一下这样的场景:你的应用只有一个服务实例在运行,突然之间,流量暴涨,这个唯一的实例不堪重负,CPU飙升到100%,最终崩溃。用户浏览器上显示一个冰冷的 “502 Bad Gateway” 错误。整个服务彻底不可用。

这就是典型的单点故障问题。解决它的金钥匙就是 “多实例部署 + 负载均衡”。今天,我们就使用 Spring Cloud Alibaba 生态中的两大王牌——Nacos(服务发现与注册中心)和 Spring Cloud Gateway(API网关),来打造一个高可用的微服务系统,让请求在多个实例间智能分配,轻松应对高并发与单点故障。

本文将全程实战,带你从零开始,一步步搭建环境、配置负载均衡,并深入原理,最后分享生产级的进阶技巧。

二、 环境准备:打造我们的微服务实验场

1.技术栈与版本

  • JDK: 8 或 11 (推荐11)
  • Spring Boot: 2.7.x
  • Spring Cloud: 2021.0.8
  • Spring Cloud Alibaba: 2021.0.5.0
  • Nacos Server: 2.2.0+

版本兼容性非常重要! 请务必查看 官方版本说明 来选择匹配的版本,否则会出现各种意想不到的错误。

2.项目结构

我们创建一个标准的Maven父子工程来解决依赖管理问题。

spring-cloud-nacos-lb-demo (父工程,pom打包)
├── api-gateway (网关模块,jar打包)
├── service-provider (服务提供者模块,jar打包)
└── pom.xml (父pom,统一管理依赖和版本)

3.父pom.xml 核心依赖管理

在父工程的 pom.xml 中,我们引入依赖管理(dependencyManagement)来锁定整个项目的版本。

<?xml version="1.0" encoding="UTF-8"?>
<project>
...
<packaging>pom</packaging>

<properties>
<java.version>11</java.version>
<spring-boot.version>2.7.15</spring-boot.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- Spring Boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- Spring Cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- Spring Cloud Alibaba 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<modules>
<module>api-gateway</module>
<module>service-provider</module>
</modules>
...
</project>

三、 核心概念:什么是“双实例”?

在微服务架构中,“双实例”部署通常有两种模式,理解这两种模式对设计高可用系统至关重要。无论哪种模式,对于Nacos和Gateway来说,只要【服务名(spring.application.name)】 + 【主机地址(IP)】 +【 端口(Port)】这个三元组不同,它们就被视为不同的实例,都会参与负载均衡。

模式一:单机多实例(开发测试常用)

描述: 在一台物理机或虚拟机(同一个IP地址)上,启动同一个服务的多个进程,每个进程监听不同的端口。

目的: 资源最大化利用、节省成本、方便本地开发和测试,快速模拟集群环境。

示例: 在你的本地开发环境,启动两个 service-provider
如:Instance 1: 192.168.1.100:8081
Instance 2: 192.168.1.100:8082

可靠性: 较低。服务器宕机导致所有实例不可用,单点故障(SPOF)风险高。

模式二:多机多实例(生产环境必备)

描述: 在多台物理机或虚拟机(不同IP地址)上,分别启动服务实例。端口可以相同也可以不同。

目的: 真正的高可用、容灾、水平扩展。是生产环境的黄金标准。

示例: 在两台云服务器上部署:
Instance 1 on Server A: 192.168.1.101:8080
Instance 2 on Server B: 192.168.1.102:8080

可靠性: 高。一台服务器宕机,另一台仍可服务,实现了高可用。

下面的流程图清晰地展示了Gateway如何对这两种模式下的实例进行统一的负载均衡:

本文将以模式一(单机多实例) 进行演示,因为它是最方便开发者本地实验的方式。但请记住,模式二的架构思想才是你未来部署生产应用的标准。

四、 实战:五步实现Gateway+Nacos负载均衡

第1步:搭建双实例服务提供者(Service Provider)

1.创建 service-provider 模块,并添加依赖:

<!-- service-provider/pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos 服务发现核心依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

2.编写一个简单的Controller:

// ServiceProviderApplication.java
@SpringBootApplication
@RestController
public class ServiceProviderApplication {

@Value("${server.port}")
private String port;

@GetMapping("/hello")
public String sayHello(@RequestParam String name) {
return "Hello, " + name + ". I'm from port: " + port;
}

public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}

3.准备两个配置文件,用于启动两个实例:

application-instance1.yml (端口8081)

server:
port: 8081
spring:
application:
name: service-provider # 服务名必须相同!
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos服务器地址

application-instance2.yml (端口8082)

server:
port: 8082
spring:
application:
name: service-provider # 服务名必须相同!
cloud:
nacos:
discovery:
server-addr: localhost:8848

4.启动两次应用,使用不同的配置 profile:在IDEA中,复制一个启动配置,在 Active profiles 中分别填写 instance1 和 instance2

第2步:配置网关(Spring Cloud Gateway)

1.创建 api-gateway 模块,并添加依赖:

<!-- api-gateway/pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Gateway也要从Nacos发现服务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 显式引入 LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>

2.配置网关路由(最关键的一步):

# api-gateway/application.yml
server:
port: 9999

spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848 # 网关自己也注册到Nacos,方便管理
gateway:
discovery:
locator:
enabled: false # 关闭默认的全局路由发现,我们使用手动配置,更清晰
routes:
- id: service-provider-route # 路由ID,唯一即可
uri: lb://service-provider # 【核心】使用 lb:// 协议 + 服务名
predicates:
- Path=/api/** # 匹配所有以/api开头的请求
filters:
- StripPrefix=1 # 去掉第一个路径前缀(/api),再转发给服务

第3步:启动与验证

确保Nacos Server已启动(默认地址 localhost:8848,账号/密码 nacos/nacos)。

依次启动:两个 service-provider 实例和 api-gateway。

查看Nacos控制台:在 服务管理 -> 服务列表 中,你应该能看到一个名为 service-provider 的服务,并且有
2个健康实例(IP相同但端口分别为8081和8082)。同时也会有一个 api-gateway 服务。

第4步:发起测试,见证负载均衡

使用浏览器、Postman或命令行连续访问网关地址:

curl "http://localhost:9999/api/hello?name=xiaochenCoding

多次执行此命令,观察返回结果。你会看到输出在两种之间交替变化:

Hello, xiaochenCoding. I'm from port: 8081
Hello, xiaochenCoding. I'm from port: 8082
Hello, xiaochenCoding. I'm from port: 8081
Hello, xiaochenCoding. I'm from port: 8082

恭喜!负载均衡已经成功实现了! Gateway自动将请求均匀地分发到了两个后端实例上。

五、 原理解析:揭开lb://的神秘面纱

lb:// 协议背后是 Spring Cloud LoadBalancer 在默默工作。整个流程如下:

  • 服务注册: Provider启动时,向Nacos注册自己的元数据(服务名、IP、端口)。
  • 服务发现: Gateway作为Consumer,通过Nacos Discovery Client订阅和拉取service-provider的服务列表并缓存。
  • 负载均衡: 当请求到达Gateway,LoadBalancer根据lb://协议从缓存中获取可用实例列表,并通过轮询算法选择一个实例。
  • 请求转发: Gateway将请求转发到第3步选出的真实实例地址上。
  • 响应返回: 实例处理完请求,将结果返回给Gateway,再由Gateway返回给客户端。

六、 进阶玩法:让负载均衡更智能

玩法一:基于Nacos权重的负载均衡(生产常用)

这是Nacos提供的一个开箱即用的强大功能。你可以在Nacos控制台上动态调整实例的权重,从而控制流量分配。

  • 进入Nacos控制台 -> 服务列表 -> 点击 service-provider 服务的“详情”。
  • 在实例列表中,你可以直接修改权重值(0-1之间)。 (将实例1的权重改为 0.2。 将实例2的权重改为 0.8。 )
  • 点击“更新”保存。

效果:在多次请求中,大约会有 20% 的请求被分发到实例1的8081端口,80% 的请求被分发到实例28082端口。这在灰度发布、金丝雀发布、压测引流等场景中非常有用。

玩法二:自定义负载均衡策略(如随机、粘性会话)

如果你想改用随机策略,可以编写一个配置类。

1.在Gateway模块中创建配置类:

package com.yourcompany.gateway.config;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class LoadBalancerConfig {

/**
* 为所有服务配置随机负载均衡策略
* 如果只想为特定服务配置,请使用 @LoadBalancerClient 注解
*/
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 返回随机负载均衡器,替换默认的轮询
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class),
serviceId
);
}
}

2.如果想针对特定服务(如service-provider),可以使用 @LoadBalancerClient:

@Configuration
@LoadBalancerClient(value = "service-provider", configuration = LoadBalancerConfig.class)
public class ServiceProviderLoadBalancerConfig {
// 这个配置只对 service-provider 服务生效
}

八、 总结

通过本文的实战,我们成功地利用 Spring Cloud Gateway 和 Nacos 实现了高效、自动化的客户端负载均衡。关键在于理解 lb:// 协议的工作原理以及Nacos作为服务注册中心的核心作用。

我们深入探讨了“双实例”的两种模式:

  • 单机多实例: 适用于开发测试,快速简便。
  • 多机多实例: 生产环境必备,真正实现高可用和容灾。

这种架构的优势显而易见:

  • 高可用: 单个实例宕机不影响整体服务。
  • 高扩展: 轻松扩容,只需启动新实例并注册到Nacos,Gateway会自动感知。
  • 灵活性: 支持权重调节、自定义策略,满足各种业务场景。

接下来,你可以继续探索如何集成 Sentinel 实现网关限流降级,或使用 Nacos Config 实现动态路由配置,构建更加健壮和强大的微服务体系。

文章目录
  1. 1. 一、 引言:从单点故障的痛说起
  2. 2. 二、 环境准备:打造我们的微服务实验场
    1. 2.0.1. 1.技术栈与版本
    2. 2.0.2. 2.项目结构
    3. 2.0.3. 3.父pom.xml 核心依赖管理
  • 3. 三、 核心概念:什么是“双实例”?
    1. 3.0.0.1. 模式一:单机多实例(开发测试常用)
    2. 3.0.0.2. 模式二:多机多实例(生产环境必备)
  • 4. 四、 实战:五步实现Gateway+Nacos负载均衡
    1. 4.0.1. 第1步:搭建双实例服务提供者(Service Provider)
    2. 4.0.2. 第2步:配置网关(Spring Cloud Gateway)
    3. 4.0.3. 第3步:启动与验证
    4. 4.0.4. 第4步:发起测试,见证负载均衡
  • 5. 五、 原理解析:揭开lb://的神秘面纱
  • 6. 六、 进阶玩法:让负载均衡更智能
    1. 6.0.1. 玩法一:基于Nacos权重的负载均衡(生产常用)
    2. 6.0.2. 玩法二:自定义负载均衡策略(如随机、粘性会话)
  • 7. 八、 总结