系列导读:本篇将深入讲解 Nginx 负载均衡配置与 Tomcat 集群部署,实现高可用、高性能的 Java Web 应用架构。
文章目录
-
- 前言:为什么需要负载均衡?
- 一、负载均衡核心概念
-
- [1.1 负载均衡架构](#1.1 负载均衡架构)
- [1.2 负载均衡算法](#1.2 负载均衡算法)
- [1.3 集群架构模式](#1.3 集群架构模式)
- [二、Nginx 负载均衡配置](#二、Nginx 负载均衡配置)
-
- [2.1 基础轮询配置](#2.1 基础轮询配置)
- [2.2 加权轮询配置](#2.2 加权轮询配置)
- [2.3 IP 哈希配置(会话保持)](#2.3 IP 哈希配置(会话保持))
- [2.4 最少连接配置](#2.4 最少连接配置)
- [2.5 一致性哈希配置](#2.5 一致性哈希配置)
- [2.6 服务器状态配置](#2.6 服务器状态配置)
- [三、Tomcat 集群部署](#三、Tomcat 集群部署)
-
- [3.1 单机多实例部署](#3.1 单机多实例部署)
- [3.2 多机集群部署](#3.2 多机集群部署)
- [3.3 Tomcat 集群配置](#3.3 Tomcat 集群配置)
- 四、会话管理策略
-
- [4.1 会话问题分析](#4.1 会话问题分析)
- [4.2 解决方案对比](#4.2 解决方案对比)
- [4.3 方案一:IP Hash(Nginx 配置)](#4.3 方案一:IP Hash(Nginx 配置))
- [4.4 方案二:Session 复制(Tomcat 配置)](#4.4 方案二:Session 复制(Tomcat 配置))
- [4.5 方案三:Redis Session 共享(推荐)](#4.5 方案三:Redis Session 共享(推荐))
- 五、健康检查与故障转移
-
- [5.1 被动健康检查](#5.1 被动健康检查)
- [5.2 主动健康检查(需要第三方模块)](#5.2 主动健康检查(需要第三方模块))
- [5.3 故障转移配置](#5.3 故障转移配置)
- [5.4 优雅下线](#5.4 优雅下线)
- 六、实战案例:生产级集群架构
-
- [6.1 架构设计](#6.1 架构设计)
- [6.2 完整 Nginx 配置](#6.2 完整 Nginx 配置)
- 总结
前言:为什么需要负载均衡?
单台 Tomcat 服务器的局限性:
🚫 并发瓶颈:单机最大并发约 500-1000
🚫 单点故障:服务器宕机,服务不可用
🚫 资源限制:CPU、内存、网络带宽受限
🚫 扩展困难:无法应对流量突增
负载均衡解决方案:
┌─────────────────────────────────────────────────────────────┐
│ 负载均衡架构优势 │
├─────────────────────────────────────────────────────────────┤
│ ⚖️ 流量分发 → 请求均匀分配到多台服务器 │
│ 🔄 高可用 → 单机故障不影响整体服务 │
│ 📈 弹性扩展 → 动态增减服务器应对流量变化 │
│ 🛡️ 故障隔离 → 问题服务器自动隔离 │
└─────────────────────────────────────────────────────────────┘
一、负载均衡核心概念
1.1 负载均衡架构
┌→ Tomcat1 (8080)
客户端 → Nginx ─────┼→ Tomcat2 (8081)
LB └→ Tomcat3 (8082)
Nginx 作为负载均衡器(LB)
1.2 负载均衡算法
| 算法 | 说明 | 适用场景 |
|---|---|---|
| 轮询 | 依次分配(默认) | 服务器配置相同 |
| 加权轮询 | 按权重分配 | 服务器配置不同 |
| IP 哈希 | 同一 IP 分配到同一服务器 | 需要会话保持 |
| 最少连接 | 分配给连接最少的服务器 | 请求处理时间差异大 |
| 一致性哈希 | 基于请求特征哈希 | 缓存场景 |
1.3 集群架构模式
模式一:单机多实例
一台服务器运行多个 Tomcat 实例
┌─────────────────────────────────────┐
│ 物理服务器 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐│
│ │Tomcat1 │ │Tomcat2 │ │Tomcat3 ││
│ │ :8080 │ │ :8081 │ │ :8082 ││
│ └─────────┘ └─────────┘ └─────────┘│
└─────────────────────────────────────┘
模式二:多机集群
多台服务器,每台运行一个 Tomcat
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Server1 │ │ Server2 │ │ Server3 │
│ Tomcat │ │ Tomcat │ │ Tomcat │
│ :8080 │ │ :8080 │ │ :8080 │
└───────────┘ └───────────┘ └───────────┘
二、Nginx 负载均衡配置
2.1 基础轮询配置
nginx
upstream tomcat_cluster {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
2.2 加权轮询配置
nginx
upstream tomcat_cluster {
# 高配置服务器分配更多流量
server 192.168.1.10:8080 weight=5; # 50% 流量
server 192.168.1.11:8080 weight=3; # 30% 流量
server 192.168.1.12:8080 weight=2; # 20% 流量
}
# 流量分配:10 → 11 → 12 → 10 → 11 → 10 → 10 → 11 → 12 → 10
2.3 IP 哈希配置(会话保持)
nginx
upstream tomcat_cluster {
ip_hash; # 同一 IP 始终访问同一服务器
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
# 注意:ip_hash 不支持 weight 和 backup
2.4 最少连接配置
nginx
upstream tomcat_cluster {
least_conn; # 分配给连接数最少的服务器
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
2.5 一致性哈希配置
nginx
upstream tomcat_cluster {
hash $request_uri consistent; # 基于 URI 的一致性哈希
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
# 适用场景:缓存服务器集群
2.6 服务器状态配置
nginx
upstream tomcat_cluster {
# 正常服务器
server 192.168.1.10:8080 weight=5;
# 带故障检测的服务器
server 192.168.1.11:8080
max_fails=3 # 最大失败次数
fail_timeout=30s; # 失败后暂停时间
# 备份服务器(仅在其他服务器不可用时启用)
server 192.168.1.12:8080 backup;
# 下线服务器
server 192.168.1.13:8080 down;
}
三、Tomcat 集群部署
3.1 单机多实例部署
步骤一:创建实例目录
bash
# 创建多实例目录
mkdir -p /opt/tomcat/{instance1,instance2,instance3}
# 复制 Tomcat 文件
TOMCAT_HOME=/opt/tomcat/current
for i in 1 2 3; do
cp -r $TOMCAT_HOME/* /opt/tomcat/instance$i/
done
步骤二:修改端口配置
bash
# 实例1: server.xml
sed -i 's/port="8005"/port="8105"/' /opt/tomcat/instance1/conf/server.xml
sed -i 's/port="8080"/port="8180"/' /opt/tomcat/instance1/conf/server.xml
sed -i 's/port="8009"/port="8109"/' /opt/tomcat/instance1/conf/server.xml
# 实例2: server.xml
sed -i 's/port="8005"/port="8205"/' /opt/tomcat/instance2/conf/server.xml
sed -i 's/port="8080"/port="8280"/' /opt/tomcat/instance2/conf/server.xml
sed -i 's/port="8009"/port="8209"/' /opt/tomcat/instance2/conf/server.xml
# 实例3: server.xml
sed -i 's/port="8005"/port="8305"/' /opt/tomcat/instance3/conf/server.xml
sed -i 's/port="8080"/port="8380"/' /opt/tomcat/instance3/conf/server.xml
sed -i 's/port="8009"/port="8309"/' /opt/tomcat/instance3/conf/server.xml
步骤三:创建启动脚本
bash
#!/bin/bash
# /opt/tomcat/instances.sh
INSTANCE_DIR="/opt/tomcat"
INSTANCES=("instance1" "instance2" "instance3")
start_all() {
for instance in "${INSTANCES[@]}"; do
echo "Starting $instance..."
$INSTANCE_DIR/$instance/bin/startup.sh
done
}
stop_all() {
for instance in "${INSTANCES[@]}"; do
echo "Stopping $instance..."
$INSTANCE_DIR/$instance/bin/shutdown.sh
done
}
status() {
for instance in "${INSTANCES[@]}"; do
pid_file="$INSTANCE_DIR/$instance/temp/tomcat.pid"
if [ -f "$pid_file" ]; then
echo "$instance: Running (PID: $(cat $pid_file))"
else
echo "$instance: Stopped"
fi
done
}
case "$1" in
start) start_all ;;
stop) stop_all ;;
restart) stop_all; sleep 3; start_all ;;
status) status ;;
*) echo "Usage: $0 {start|stop|restart|status}" ;;
esac
3.2 多机集群部署
服务器规划:
| 服务器 | IP | 角色 | 端口 |
|---|---|---|---|
| Server1 | 192.168.1.10 | Nginx + Tomcat | 80/8080 |
| Server2 | 192.168.1.11 | Tomcat | 8080 |
| Server3 | 192.168.1.12 | Tomcat | 8080 |
Nginx 配置(Server1):
nginx
upstream tomcat_cluster {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=3;
server 192.168.1.12:8080 weight=3;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
3.3 Tomcat 集群配置
每个 Tomcat 实例需要配置 jvmRoute:
xml
<!-- server.xml -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
<!-- jvmRoute 用于会话粘性,值必须唯一 -->
</Engine>
不同实例的 jvmRoute:
bash
# 实例1
<Engine ... jvmRoute="tomcat1">
# 实例2
<Engine ... jvmRoute="tomcat2">
# 实例3
<Engine ... jvmRoute="tomcat3">
四、会话管理策略
4.1 会话问题分析
用户请求 → Nginx → Tomcat1(创建 Session A)
用户请求 → Nginx → Tomcat2(找不到 Session A,创建 Session B)
问题:用户登录后,下次请求可能被分发到其他 Tomcat,导致登录状态丢失。
4.2 解决方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| IP Hash | 配置简单 | 负载不均衡 | 小规模集群 |
| Session 复制 | 配置简单 | 性能开销大 | 小规模集群 |
| Session 共享(Redis) | 性能好、可扩展 | 需要 Redis | 大规模集群 |
| JWT 无状态 | 无会话问题 | 无法主动失效 | RESTful API |
4.3 方案一:IP Hash(Nginx 配置)
nginx
upstream tomcat_cluster {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
# 缺点:某 IP 流量大时,对应服务器压力大
4.4 方案二:Session 复制(Tomcat 配置)
xml
<!-- server.xml -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
web.xml 添加配置:
xml
<!-- web.xml -->
<distributable/>
4.5 方案三:Redis Session 共享(推荐)
添加依赖:
xml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Spring Boot 配置:
yaml
# application.yml
spring:
session:
store-type: redis
redis:
host: 192.168.1.100
port: 6379
password: yourpassword
database: 0
启用 Session:
java
@EnableRedisHttpSession
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Nginx 配置:
nginx
upstream tomcat_cluster {
least_conn; # 使用最少连接算法
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
五、健康检查与故障转移
5.1 被动健康检查
nginx
upstream tomcat_cluster {
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 max_fails=3 fail_timeout=30s;
}
# max_fails: 最大失败次数
# fail_timeout: 失败后暂停时间
5.2 主动健康检查(需要第三方模块)
nginx
# 使用 nginx_upstream_check_module
upstream tomcat_cluster {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
check interval=3000 rise=2 fall=3 timeout=1000 type=http;
check_http_send "GET /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
server {
location /nginx_status {
check_status;
access_log off;
}
}
5.3 故障转移配置
nginx
upstream tomcat_cluster {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup; # 备份服务器
}
server {
location / {
proxy_pass http://tomcat_cluster;
# 故障重试
proxy_next_upstream
error
timeout
http_500
http_502
http_503
http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 30s;
}
}
5.4 优雅下线
nginx
# 服务器下线流程
# 1. 标记为 down
# 2. 等待现有请求处理完成
# 3. 停止 Tomcat
upstream tomcat_cluster {
server 192.168.1.10:8080;
server 192.168.1.11:8080 down; # 下线
server 192.168.1.12:8080;
}
六、实战案例:生产级集群架构
6.1 架构设计
┌─────────────────────────────────────────────────────────────┐
│ 用户请求 │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Nginx 负载均衡 │
│ (主备 Keepalived) │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Tomcat 集群 │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Tomcat 1 │ │ Tomcat 2 │ │ Tomcat 3 │ │
│ └───────────┘ └───────────┘ └───────────┘ │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Redis Session │
│ (主从 Sentinel) │
└─────────────────────────────────────────────────────────────┘
6.2 完整 Nginx 配置
nginx
# /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'upstream=$upstream_addr response_time=$upstream_response_time';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/json application/xml;
# 限流配置
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
# Upstream 定义
upstream tomcat_cluster {
least_conn;
server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=3 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# WebSocket 映射
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# 引入站点配置
include /etc/nginx/conf.d/*.conf;
}
nginx
# /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name app.example.com;
# 强制 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name app.example.com;
# SSL 配置
ssl_certificate /etc/nginx/ssl/app.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/app.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 日志
access_log /var/log/nginx/app.access.log main;
error_log /var/log/nginx/app.error.log;
# 静态资源
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
root /opt/tomcat/current/webapps/ROOT;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# WebSocket
location /ws/ {
proxy_pass http://tomcat_cluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_buffering off;
}
# API 限流
location /api/ {
limit_req zone=api_limit burst=200 nodelay;
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
# 默认转发
location / {
proxy_pass http://tomcat_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 健康检查
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
# Nginx 状态
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}
}
总结
本文深入讲解了 Nginx 负载均衡与 Tomcat 集群部署:
✅ 负载均衡算法 :轮询、加权、IP Hash、最少连接、一致性哈希
✅ 集群部署 :单机多实例、多机集群、端口配置
✅ 会话管理 :IP Hash、Session 复制、Redis 共享
✅ 健康检查 :被动检查、主动检查、故障转移
✅ 生产架构:完整配置模板、高可用设计
集群部署检查清单:
□ Tomcat 实例端口配置正确
□ jvmRoute 值唯一
□ Session 共享方案已实施
□ Nginx upstream 配置正确
□ 健康检查已配置
□ 故障转移已测试
□ 静态资源路径一致
□ 日志收集已配置
下一篇预告 :(Nginx + Tomcat 整合实战(四):会话管理与共享详解),将深入讲解 Session 管理的各种方案、Redis 集成、分布式会话最佳实践。
作者 :刘~浪地球
系列 :Nginx + Tomcat 整合实战(三)
更新时间:2026-03-31