Nginx配置Spring Boot集群:负载均衡+静态资源分离实战
- 一、架构设计与原理
- 二、Nginx核心配置详解
- 2.1 主配置文件(nginx.conf)
- 2.2 负载均衡配置(upstream.conf)
- 2.3 静态资源分离配置(static.conf)
- 2.4 动态请求代理配置(api.conf)
- 三、Spring Boot应用适配
- 3.1 应用配置优化
- 3.2 获取真实客户端IP
- 3.3 健康检查增强
- 四、性能优化与监控
- 4.1 Linux内核优化
- 4.2 Nginx性能调优
- 4.3 监控系统集成
- 五、安全加固措施
- 六、高级功能实现
- 6.1 灰度发布配置
- 6.2 多机房容灾
- 6.3 AB测试实现
- 七、常见问题解决方案
- 7.1 502 Bad Gateway排查
- 7.2 静态资源加载失败
- 八、生产环境检查清单
- 九、性能测试报告示例
一、架构设计与原理
1.1 整体架构图
1.2 技术选型对比
技术方案 | 优点 | 缺点 | 适用场景 |
---|
Nginx | 高性能、低资源消耗、配置灵活 | 功能相对单一 | 高并发Web服务 |
HAProxy | 专业的负载均衡、丰富的健康检查 | 配置复杂 | TCP层负载均衡 |
Spring Cloud Gateway | 深度集成Spring生态、服务发现 | Java应用较重 | 微服务架构 |
二、Nginx核心配置详解
2.1 主配置文件(nginx.conf)
# 运行用户和组
user nginx nginx;# 工作进程数(建议设置为CPU核心数)
worker_processes auto;# 错误日志级别和路径
error_log /var/log/nginx/error.log warn;# PID文件位置
pid /var/run/nginx.pid;# 事件模块配置
events {# 每个worker的最大连接数worker_connections 10240;# 使用epoll模型(Linux)use epoll;# 开启多连接接受multi_accept on;# 连接处理优化accept_mutex on;
}# HTTP模块配置
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"';access_log /var/log/nginx/access.log main;# 性能优化参数sendfile on;tcp_nopush on;tcp_nodelay on;keepalive_timeout 65;types_hash_max_size 2048;server_tokens off;# 客户端请求体大小限制client_max_body_size 20m;client_body_buffer_size 128k;# 代理相关超时设置proxy_connect_timeout 5s;proxy_read_timeout 30s;proxy_send_timeout 30s;proxy_buffer_size 4k;proxy_buffers 8 32k;proxy_busy_buffers_size 64k;# Gzip压缩配置gzip on;gzip_min_length 1k;gzip_comp_level 6;gzip_types text/plain text/css application/json application/javascript;gzip_vary on;gzip_proxied any;# 包含其他配置文件include /etc/nginx/conf.d/*.conf;include /etc/nginx/sites-enabled/*;
}
2.2 负载均衡配置(upstream.conf)
upstream springboot_cluster {# 负载均衡算法(可选:ip_hash, least_conn, hash等)least_conn;# 后端服务器配置server 192.168.1.101:8080 weight=5 max_fails=3 fail_timeout=30s;server 192.168.1.102:8081 weight=3 max_fails=3 fail_timeout=30s;server 192.168.1.103:8082 weight=2 max_fails=3 fail_timeout=30s;# 健康检查配置(需要nginx_upstream_check_module模块)check interval=3000 rise=2 fall=3 timeout=2000 type=http;check_http_send "HEAD /actuator/health HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";check_http_expect_alive http_2xx http_3xx;# 长连接优化keepalive 32;
}
2.3 静态资源分离配置(static.conf)
server {listen 80;server_name static.example.com;# 静态资源根目录root /data/static-resources;# 访问控制location / {# 缓存控制expires 1y;add_header Cache-Control "public, immutable";# 防盗链配置valid_referers none blocked *.example.com;if ($invalid_referer) {return 403;}# 开启目录浏览(可选)autoindex off;# 尝试文件存在性检查try_files $uri $uri/ =404;}# 图片资源特殊处理location ~* \.(jpg|jpeg|png|gif|webp)$ {# 图片优化处理image_filter resize 800 -;image_filter_buffer 10M;# WebP自动转换if ($http_accept ~* "webp") {add_header Vary Accept;rewrite ^(.*)\.(jpg|jpeg|png|gif)$ $1.webp last;}}# 禁止访问的文件类型location ~* \.(env|git|svn|htaccess|bak|swp)$ {deny all;}# 错误页面配置error_page 404 /404.html;error_page 500 502 503 504 /50x.html;# 访问日志单独记录access_log /var/log/nginx/static-access.log main buffer=32k flush=5m;
}
2.4 动态请求代理配置(api.conf)
server {listen 443 ssl http2;server_name api.example.com;# SSL证书配置ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;ssl_session_timeout 1d;ssl_session_cache shared:SSL:50m;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';ssl_prefer_server_ciphers on;# HSTS安全头add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;# 动态请求处理location / {# 负载均衡转发proxy_pass http://springboot_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_set_header X-Forwarded-Port $server_port;proxy_set_header X-Request-ID $request_id;# WebSocket支持proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";# 缓冲区优化proxy_buffering on;proxy_buffer_size 4k;proxy_buffers 8 32k;proxy_busy_buffers_size 64k;proxy_temp_file_write_size 64k;# 超时控制proxy_connect_timeout 5s;proxy_read_timeout 30s;proxy_send_timeout 30s;# 错误处理proxy_intercept_errors on;error_page 502 503 504 /50x.json;}# 健康检查端点location /actuator/health {access_log off;proxy_pass http://springboot_cluster;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}# 管理接口保护location /actuator {allow 192.168.1.0/24;deny all;proxy_pass http://springboot_cluster;}# 静态资源重定向到CDNlocation ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {return 301 https://static.example.com$request_uri;}# 错误页面配置location = /50x.json {default_type application/json;return 503 '{"status":503,"message":"Service Temporarily Unavailable"}';}
}
三、Spring Boot应用适配
3.1 应用配置优化
server:port: 8080forward-headers-strategy: frameworktomcat:remoteip:protocol-header: X-Forwarded-Protoremote-ip-header: X-Forwarded-Formax-threads: 200accept-count: 100connection-timeout: 5smax-connections: 10000use-forward-headers: truemanagement:endpoints:web:exposure:include: health,info,metrics,prometheusendpoint:health:show-details: alwaysprobes:enabled: truemetrics:distribution:percentiles-histogram: truepercentiles: 0.5,0.75,0.95,0.99spring:resources:static-locations: file:/data/static-resources/mvc:static-path-pattern: /static/**
3.2 获取真实客户端IP
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;public class IpUtils {private static final String[] IP_HEADERS = {"X-Forwarded-For","Proxy-Client-IP","WL-Proxy-Client-IP","HTTP_X_FORWARDED_FOR","HTTP_X_FORWARDED","HTTP_X_CLUSTER_CLIENT_IP","HTTP_CLIENT_IP","HTTP_FORWARDED_FOR","HTTP_FORWARDED","HTTP_VIA","REMOTE_ADDR"};public static String getClientIp(HttpServletRequest request) {for (String header : IP_HEADERS) {String ip = request.getHeader(header);if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {if (header.equals("X-Forwarded-For")) {return ip.split(",")[0].trim();}return ip;}}return request.getRemoteAddr();}public static boolean isInternalIp(String ip) {try {InetAddress inetAddress = InetAddress.getByName(ip);return inetAddress.isSiteLocalAddress() || inetAddress.isLoopbackAddress()|| ip.startsWith("192.168.")|| ip.startsWith("10.")|| ip.startsWith("172.") && ip.substring(6, 8).compareTo("16") >= 0 && ip.substring(6, 8).compareTo("31") <= 0;} catch (UnknownHostException e) {return false;}}
}
3.3 健康检查增强
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;@Component
public class CustomHealthIndicator implements HealthIndicator {private final DatabaseService databaseService;private final CacheService cacheService;public CustomHealthIndicator(DatabaseService databaseService, CacheService cacheService) {this.databaseService = databaseService;this.cacheService = cacheService;}@Overridepublic Health health() {Health.Builder builder = Health.up();if (!databaseService.isHealthy()) {builder.down().withDetail("database", "Connection failed");}if (!cacheService.isHealthy()) {builder.down().withDetail("cache", "Connection failed");}File root = new File("/");long freeSpace = root.getFreeSpace();if (freeSpace < 1024 * 1024 * 1024) { builder.withDetail("disk", "Free space low: " + freeSpace / (1024 * 1024) + "MB");}return builder.build();}
}
四、性能优化与监控
4.1 Linux内核优化
fs.file-max = 65535
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_keepalive_intvl = 15
net.core.somaxconn = 32768
net.core.netdev_max_backlog = 8192
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
sysctl -p
4.2 Nginx性能调优
# 在http模块中添加
http {# 文件缓存open_file_cache max=10000 inactive=20s;open_file_cache_valid 30s;open_file_cache_min_uses 2;open_file_cache_errors on;# 连接优化reset_timedout_connection on;client_header_timeout 15s;client_body_timeout 15s;send_timeout 10s;# TCP优化tcp_nodelay on;tcp_nopush on;# 输出缓冲区output_buffers 4 32k;postpone_output 1460;# 客户端限制limit_conn_zone $binary_remote_addr zone=addr:10m;limit_conn addr 100;
}
4.3 监控系统集成
Prometheus配置
scrape_configs:- job_name: 'nginx'metrics_path: '/nginx_status'static_configs:- targets: ['nginx-server:9113']- job_name: 'springboot'metrics_path: '/actuator/prometheus'static_configs:- targets: ['app1:8080', 'app2:8080', 'app3:8080']relabel_configs:- source_labels: [__address__]target_label: instanceregex: '([^:]+)(:\d+)?'replacement: '$1'
Grafana仪表盘
- Nginx关键指标
- 请求率 (requests/sec)
- 活跃连接数
- 请求处理时间 (P95, P99)
- 错误率 (4xx, 5xx)
- Spring Boot关键指标
- JVM内存使用
- GC次数和时间
- 线程池状态
- HTTP请求延迟
五、安全加固措施
5.1 Nginx安全配置
# 在http模块中添加
http {# 安全头设置add_header X-Frame-Options "SAMEORIGIN";add_header X-Content-Type-Options "nosniff";add_header X-XSS-Protection "1; mode=block";add_header Referrer-Policy "strict-origin-when-cross-origin";add_header Content-Security-Policy "default-src 'self'";# 禁用不安全的HTTP方法if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE)$ ) {return 405;}# 隐藏服务器信息more_clear_headers 'Server';more_clear_headers 'X-Powered-By';
}
5.2 防火墙规则
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -j DROP
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT
iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j REJECT
六、高级功能实现
6.1 灰度发布配置
# 根据Cookie分流
map $cookie_gray_release $backend {default "production";"true" "gray";
}upstream production {server 192.168.1.101:8080;server 192.168.1.102:8080;
}upstream gray {server 192.168.1.103:8080;
}server {location / {proxy_pass http://$backend;}
}
6.2 多机房容灾
upstream backend {# 主机房server 192.168.1.101:8080 weight=5;server 192.168.1.102:8080 weight=5;# 备机房server 192.168.2.101:8080 backup;server 192.168.2.102:8080 backup;# 健康检查check interval=3000 rise=2 fall=3 timeout=2000 type=http;
}
6.3 AB测试实现
split_clients "${remote_addr}${http_user_agent}" $variant {50% "A";50% "B";
}server {location / {if ($variant = "A") {proxy_pass http://backend_a;}if ($variant = "B") {proxy_pass http://backend_b;}}
}
七、常见问题解决方案
7.1 502 Bad Gateway排查
- 检查后端服务状态
curl -I http://backend:8080/actuator/health
- 检查Nginx错误日志
tail -f /var/log/nginx/error.log
- 检查连接限制
netstat -anp | grep nginx | wc -l
- 调整缓冲区大小
proxy_buffer_size 16k;
proxy_buffers 4 32k;
7.2 静态资源加载失败
- 检查文件权限
chown -R nginx:nginx /data/static-resources
- 检查MIME类型
types {application/javascript js;text/css css;
}
- 检查缓存头
expires 1y;
add_header Cache-Control "public, immutable";
八、生产环境检查清单
九、性能测试报告示例
测试场景 | 请求量 (QPS) | 平均延迟 (ms) | 错误率 (%) | CPU使用率 (%) | 内存使用 (MB) |
---|
纯静态资源 | 15,000 | 12.5 | 0 | 45 | 320 |
纯API请求 | 8,200 | 28.3 | 0 | 68 | 450 |
混合负载 | 6,500 | 42.7 | 0.2 | 75 | 520 |
峰值压力 | 12,000 | 185.4 | 3.5 | 95 | 780 |