Spring Boot 集群

可以。你可以先把 Spring Boot 集群 理解成一句话:

多个 Spring Boot 应用实例同时运行,对外表现得像一个服务,通过负载均衡分摊请求,并配合配置中心、注册中心、缓存、日志、监控、CI/CD 等完成企业级交付。

下面按企业真实开发、调试、部署上线流程给你梳理。


一、先理解集群架构

一个典型 Spring Boot 集群大概是这样:

flowchart LR User["用户/前端"] --> LB["负载均衡<br/>Nginx / SLB / Ingress"] LB --> App1["Spring Boot 实例 1"] LB --> App2["Spring Boot 实例 2"] LB --> App3["Spring Boot 实例 3"] App1 --> DB["MySQL / PostgreSQL"] App2 --> DB App3 --> DB App1 --> Redis["Redis 缓存/分布式锁/Session"] App2 --> Redis App3 --> Redis App1 --> MQ["MQ<br/>RabbitMQ/Kafka/RocketMQ"] App2 --> MQ App3 --> MQ App1 --> Config["配置中心"] App2 --> Config App3 --> Config App1 --> Obs["日志/监控/链路追踪"] App2 --> Obs App3 --> Obs

核心点:

  • 不是一个应用变成集群,而是同一个应用启动多个实例。
  • 用户不直接访问实例,而是访问负载均衡。
  • 应用实例尽量无状态,状态放 Redis、数据库、对象存储、MQ。
  • 每个实例代码相同,配置可不同,比如端口、环境、机器名。
  • 上线不是手动拷 jar,企业里通常走 CI/CD、镜像、K8s 或发布平台。

二、单机 Spring Boot 和集群区别

单机模式:

用户 -> Spring Boot -> 数据库

集群模式:

用户 -> Nginx/网关 -> 多个 Spring Boot 实例 -> 数据库/Redis/MQ

主要变化在这些地方:

  • Session 不能只放本地内存
    因为用户第一次请求到 App1,第二次可能到 App2
    解决:JWT、Redis Session、Spring Session。

  • 本地缓存要谨慎
    一个实例缓存更新了,其他实例不知道。
    解决:Redis、Caffeine + MQ 通知、分布式缓存。

  • 定时任务不能每台都执行一遍
    集群有 3 个实例,@Scheduled 会执行 3 次。
    解决:XXL-JOB、Quartz 集群、ShedLock、分布式锁。

  • 文件不能只上传到本机磁盘
    因为访问另一台机器时文件不存在。
    解决:OSS、MinIO、S3、共享文件存储。

  • 日志不能只看本机文件
    多实例排查问题很痛苦。
    解决:ELK、Loki、SkyWalking、Zipkin、Prometheus、Grafana。


三、企业级开发流程

正常公司里,一般是这个流程:

需求评审
  ↓
技术方案设计
  ↓
本地开发
  ↓
单元测试/接口测试
  ↓
提交代码
  ↓
CI 构建
  ↓
部署测试环境
  ↓
联调/测试
  ↓
部署预发环境
  ↓
灰度/滚动发布
  ↓
生产上线
  ↓
监控告警/日志排查

对应到 Spring Boot 项目,一般会有这些配置:

application.yml
application-dev.yml
application-test.yml
application-staging.yml
application-prod.yml

常见环境:

  • dev:开发环境,给程序员本地或开发服务器用。
  • test:测试环境,给测试人员验证功能。
  • staging / pre:预发环境,尽量接近生产。
  • prod:生产环境,真实用户访问。

启动时指定环境:

java -jar app.jar --spring.profiles.active=prod

或者:

java -jar app.jar --server.port=8081 --spring.profiles.active=test

四、本地如何模拟 Spring Boot 集群

你可以在本地启动多个实例:

java -jar app.jar --server.port=8081
java -jar app.jar --server.port=8082
java -jar app.jar --server.port=8083

然后用 Nginx 做负载均衡:

upstream my_springboot_cluster {
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
    server 127.0.0.1:8083;
}

server {
    listen 80;

    location / {
        proxy_pass http://my_springboot_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

访问:

http://localhost

请求会被分发到 808180828083

为了观察效果,可以加一个接口:

@RestController
public class DemoController {

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

    @GetMapping("/hello")
    public String hello() {
        return "hello from port: " + port;
    }
}

多刷新几次,就能看到请求落到不同实例。


五、企业级调试方式

开发阶段:

  • 本地直接 Debug 单个实例。
  • dev 配置连接开发数据库、开发 Redis。
  • 通过 Postman、Apifox、Swagger 调接口。
  • 使用日志定位问题。

集群调试阶段:

  • 本地启动多个端口模拟集群。
  • 观察负载均衡是否正常。
  • 检查 Session、缓存、定时任务、文件上传是否有集群问题。
  • 使用请求 ID / Trace ID 串起完整链路。

常用日志规范:

traceId=xxx userId=1001 orderId=20260101 method=createOrder message=创建订单开始

企业里很少直接在生产远程 Debug,因为风险大。生产排查通常靠:

  • 日志
  • 监控
  • 链路追踪
  • JVM 指标
  • 慢 SQL
  • 错误告警
  • 临时提高日志级别

可以开启 Spring Boot Actuator:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      probes:
        enabled: true

常用端点:

/actuator/health
/actuator/metrics
/actuator/prometheus

六、部署方式一:传统服务器部署

适合中小项目或老项目。

流程:

开发提交代码
  ↓
Jenkins/GitLab CI 打包
  ↓
生成 app.jar
  ↓
上传到服务器
  ↓
systemd/shell 启动多个实例
  ↓
Nginx 转发流量

启动示例:

java -Xms512m -Xmx512m -jar app.jar \
  --server.port=8081 \
  --spring.profiles.active=prod

多个实例:

java -jar app.jar --server.port=8081 --spring.profiles.active=prod
java -jar app.jar --server.port=8082 --spring.profiles.active=prod
java -jar app.jar --server.port=8083 --spring.profiles.active=prod

优点:

  • 简单直观。
  • 学习成本低。
  • 小团队容易落地。

缺点:

  • 扩容麻烦。
  • 发布容易依赖人工操作。
  • 回滚和灰度不够优雅。

七、部署方式二:Docker 部署

企业里更常见的是容器化。

Dockerfile 示例:

FROM eclipse-temurin:17-jre
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

构建镜像:

docker build -t my-app:1.0.0 .

启动容器:

docker run -d \
  --name my-app-1 \
  -p 8081:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  my-app:1.0.0

启动多个容器:

docker run -d -p 8081:8080 my-app:1.0.0
docker run -d -p 8082:8080 my-app:1.0.0
docker run -d -p 8083:8080 my-app:1.0.0

然后前面挂 Nginx 或云负载均衡。


八、部署方式三:Kubernetes 部署

大型企业更常见。

你不再手动启动 3 个应用,而是告诉 K8s:

我要运行 3 个副本,挂掉了自动拉起来,发布时滚动更新。

简化版 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-springboot-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-springboot-app
  template:
    metadata:
      labels:
        app: my-springboot-app
    spec:
      containers:
        - name: app
          image: my-app:1.0.0
          ports:
            - containerPort: 8080
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080

核心概念:

  • Pod:一个应用实例。
  • Deployment:管理多个实例。
  • replicas: 3:运行 3 个副本。
  • Service:内部负载均衡。
  • Ingress:外部入口。
  • ConfigMap:配置。
  • Secret:密码、Token、证书。
  • Probe:健康检查。
  • HPA:自动扩缩容。

九、上线发布流程

企业发布不能直接全量替换,常用这些方式:

1. 滚动发布

逐台替换实例:

旧版本实例 3 个
  ↓
停 1 个旧版本,启动 1 个新版本
  ↓
再替换第 2 个
  ↓
再替换第 3 个

优点:

  • 用户基本无感。
  • K8s 默认支持。
  • 适合大多数业务。

2. 蓝绿发布

同时准备两套环境:

蓝色环境:旧版本
绿色环境:新版本

验证绿色环境没问题后,把流量切过去。

优点:

  • 回滚快。
  • 风险低。

缺点:

  • 资源成本高。

3. 灰度发布

先让少部分用户访问新版本:

1% 用户 -> 新版本
99% 用户 -> 旧版本

没问题后逐步扩大:

1% -> 5% -> 20% -> 50% -> 100%

适合高风险业务。


十、上线前检查清单

上线前至少检查这些:

  • 数据库脚本是否兼容旧版本。
  • 配置是否已经上配置中心。
  • Redis Key 是否有过期时间。
  • MQ 消费是否幂等。
  • 定时任务是否会重复执行。
  • 接口是否有超时控制。
  • 外部服务调用是否有重试、熔断、降级。
  • 日志里不能打印密码、Token、身份证等敏感信息。
  • /actuator/health 是否正常。
  • JVM 内存参数是否合理。
  • 是否支持优雅停机。
  • 是否有回滚方案。
  • 是否配置监控和告警。

十一、Spring Boot 集群常见坑

1. 本地变量保存业务状态

错误示例:

private Map<Long, Order> orderMap = new HashMap<>();

集群下每个实例都有自己的 Map,数据不一致。

解决:

  • 放数据库。
  • 放 Redis。
  • 放分布式缓存。

2. 定时任务重复执行

错误示例:

@Scheduled(cron = "0 0 1 * * ?")
public void cleanData() {
    // 清理数据
}

如果有 3 个实例,就执行 3 次。

解决:

  • XXL-JOB。
  • Quartz 集群。
  • ShedLock。
  • Redis 分布式锁。

3. 文件上传到本机

错误:

上传到了 App1 的 /data/upload
访问请求去了 App2,文件不存在

解决:

  • MinIO
  • 阿里云 OSS
  • AWS S3
  • 共享存储

4. Session 丢失

用户登录请求在 App1,下一次请求去了 App2

解决:

  • JWT。
  • Redis Session。
  • 网关统一鉴权。

5. 发布时请求被强杀

应用正在处理订单,容器被直接杀掉。

解决:

server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

十二、推荐你按这个路线学习

第一阶段:基础集群

  • Spring Boot 多实例启动。
  • Nginx 负载均衡。
  • Redis 共享 Session。
  • 日志中打印端口号观察请求分发。

第二阶段:企业开发

  • 多环境配置。
  • Maven/Gradle 打包。
  • 接口文档。
  • 单元测试。
  • Actuator 健康检查。

第三阶段:微服务/分布式

  • Spring Cloud Gateway。
  • Nacos / Eureka 注册中心。
  • OpenFeign 服务调用。
  • Sentinel / Resilience4j 熔断限流。
  • Seata 分布式事务,了解即可。

第四阶段:部署上线

  • Docker 镜像。
  • Jenkins / GitLab CI。
  • K8s Deployment。
  • 滚动发布。
  • 灰度发布。
  • Prometheus + Grafana 监控。

十三、你可以记住这个企业级标准答案

如果面试或工作中有人问:
Spring Boot 集群怎么做?

可以这样回答:

Spring Boot 集群本质是同一个应用部署多个实例,前面通过 Nginx、SLB、Ingress 或网关做负载均衡。为了保证集群可用,应用要尽量无状态,Session 放 Redis 或使用 JWT,文件放对象存储,缓存用 Redis,定时任务要避免多实例重复执行。配置通过多环境配置或配置中心管理,日志、指标和链路追踪接入统一监控平台。部署上一般通过 CI/CD 构建 jar 或 Docker 镜像,再发布到服务器或 Kubernetes,生产环境采用滚动发布、灰度发布或蓝绿发布,并配合健康检查、优雅停机和回滚方案。

这是比较符合企业级的表述。


如果你想实操,我建议下一步直接做一个小实验:

Spring Boot 项目启动 3 个端口
Nginx 做负载均衡
Redis 保存登录状态
Actuator 做健康检查
Docker 打镜像
模拟滚动发布

这个实验做完,你对 Spring Boot 集群就会很清楚。

posted @ 2026-06-23 22:23  微风抚秀发  阅读(3)  评论(0)    收藏  举报