Nginx + Tomcat 反向代理完全指南

想象一家大型商场,门口有专门的引导员(Nginx)把顾客带到不同的专柜(Tomcat)。引导员负责分发客流,专柜负责服务顾客,这就是 Nginx + Tomcat 反向代理架构的精髓!


📑 目录

  1. 什么是反向代理?
  2. 名词解释(命令与概念)
  3. [Nginx 详解](#Nginx 详解)
  4. [Tomcat 详解](#Tomcat 详解)
  5. [Tomcat 与 JSP、Servlet、Java、Vue 的配置与使用](#Tomcat 与 JSP、Servlet、Java、Vue 的配置与使用)
  6. [Nginx 反向代理 Tomcat 实战](#Nginx 反向代理 Tomcat 实战)
  7. 静态资源缓存
  8. 性能优化
  9. 监控与日志
  10. [Nginx 线上常见问题与解决方案](#Nginx 线上常见问题与解决方案)
  11. 最佳实践
  12. 与其他软件配合使用及方案对比
  13. 总结与官方参考

🎯 什么是反向代理?

正向代理 vs 反向代理

🔒 反向代理
请求目标服务器
转发
转发
👤 客户端
🖥️ 反向代理
📦 后端服务器1
📦 后端服务器2
🔓 正向代理
请求
转发
👤 客户端
🖥️ 代理服务器
🌐 目标服务器

特性 正向代理 反向代理
服务对象 客户端 服务器
透明性 服务器知道是代理 客户端不知道
典型用途 科学上网、加速 负载均衡、缓存
生活类比 帮朋友买东西 银行大堂经理

反向代理的优势

  • 负载均衡
  • 隐藏后端服务器
  • SSL 终止
  • 静态资源缓存
  • 请求分发

📖 名词解释(命令与概念)

以下对文档中出现的指令、概念、术语 做简要解释,并配上生活例子与「为什么」,便于记忆与理解。Nginx 以 ngx_http_proxy_modulengx_http_upstream_module 为准;Tomcat 以 Apache Tomcat 配置参考 为准。

反向代理相关

名词 含义 生活例子 为什么?
反向代理 代理服务器替「后端服务器」接客,客户端以为在访问目标站 银行大堂经理:客户只跟经理打交道,经理再把业务分给后台柜员 为什么用反向?可隐藏后端、做负载均衡、SSL 终结、缓存,客户端无需改配置
upstream 定义一组后端服务器,供 proxy_pass 引用 后厨名单:经理按名单把单子分给不同厨师 为什么单独定义?可集中写 server、weight、backup、负载算法,多处 location 可复用同一组
proxy_pass 把当前请求转发到指定上游或 URL 经理把单子交给某位厨师或某窗口 为什么有时带 URI 有时不带?带 URI 时会把 location 匹配部分替换成该 URI;不带则把完整请求 URI 转给后端
proxy_set_header 设置转发给后端时的请求头 经理在单子上注明「客人来自哪桌、原桌号」 为什么必须设 X-Real-IP、Host?后端要拿到真实客户端 IP 和原始域名做日志、限流、跨域;否则看到的是 Nginx 的 IP
X-Forwarded-For 记录客户端及途经代理的 IP 链 快递单上的「经手站点」列表 为什么用 $proxy_add_x_forwarded_for?会追加当前客户端 IP,多级代理时后端能还原真实来源

Nginx 负载与超时

名词 含义 生活例子 为什么?
weight 上游服务器的权重,轮询时分配比例 熟练工多派单、新手少派单 为什么默认 1?权重越高分到的请求越多,用于机器性能不一致时
backup 标记为备份节点,仅当主节点不可用时使用 替补队员,主力都挂了才上 为什么需要?主节点故障时自动切到 backup,避免全挂
max_fails / fail_timeout 连续失败几次视为不可用;不可用持续时间 连续几次叫不应就暂时不派单、过一段时间再试 为什么设?避免把请求一直打到已挂的后端,又能在恢复后自动加回
proxy_connect_timeout 与后端建立连接的超时 叫厨师应答的最长等待时间 为什么不能太长?后端卡住时尽快失败,释放 Nginx 连接
proxy_read_timeout 读后端响应的超时 等菜的最长等待时间 为什么动态请求要设大?某些接口耗时长,太小会 504;静态或 API 可设小

Tomcat 相关

名词 含义 生活例子 为什么?
Connector Tomcat 对外接收请求的组件,每种协议一个 餐厅的「正门」(HTTP)和「员工通道」(AJP) 为什么分 HTTP 和 AJP?HTTP 通用,浏览器、Nginx 都能连;AJP 二进制、效率高,多用于与 Apache/Nginx 同机或内网
Engine 请求处理引擎,下挂多个 Host 后厨总管,管多个档口 为什么 defaultHost?请求的 Host 头匹配不到时,落到默认虚拟主机
Host 虚拟主机,按域名区分应用 同一餐厅的不同包厢区 为什么有 appBase?每个 Host 有自己的部署目录(如 webapps),互不干扰
Context 一个 Web 应用,对应一个 URL 路径 某道菜的制作流程 为什么 path="" 可做默认应用?空 path 表示根路径,访问 / 直接进该应用
AJP Apache JServ Protocol,二进制协议,多用于 Web 服务器与 Tomcat 之间 后厨内部传菜用的简写单,比对外菜单更省 为什么 Nginx 常用 HTTP 连 Tomcat?Nginx 官方模块只支持 HTTP 代理;AJP 需第三方模块,且 AJP 有安全风险需谨慎配置 secret

相近概念对比(为什么容易混淆?)

对比项 A B 区别一句话 何时用 A / 何时用 B
正向代理 vs 反向代理 客户端通过代理访问外网 客户端访问代理,代理转给后端 谁「用」代理:客户用 vs 服务器用 翻墙、加速用正向;隐藏后端、负载均衡用反向
proxy_pass 带 URI vs 不带 proxy_pass http://backend/; proxy_pass http://backend; 带尾斜杠会替换 location 匹配部分 要改路径时带 URI(如 /api/ → 后端 /);原样转发不带
轮询 vs ip_hash vs least_conn 依次派单 同一 IP 固定后端 按连接数最少派单 无状态用轮询;要会话保持用 ip_hash;长连接、耗时不一用 least_conn
Nginx 处理静态 vs Tomcat 处理动态 Nginx 直接读文件或缓存 转发到 Tomcat 执行 Java 谁干活:Nginx 自己干 vs 交给后端 静态用 Nginx 减轻 Tomcat;动态必须 Tomcat(或其它应用服务器)
HTTP Connector vs AJP Connector 标准 HTTP,端口如 8080 二进制 AJP,端口如 8009 协议与端口不同 对外、Nginx 代理多用 HTTP;历史 Apache+Tomcat 同机常用 AJP

🌐 Nginx 详解

什么是 Nginx?

Nginx 是一个高性能的 HTTP 和反向代理服务器,以事件驱动、低内存占用著称。

Nginx 背后的公司与产品线

项目/产品 说明 对应机构
Nginx 开源版 免费、BSD 协议,社区维护,nginx.org 发布 F5, Inc. 下属团队主导开发(Nginx 项目源于俄罗斯开发者 Igor Sysoev,2004 年发布;2011 年成立 Nginx Inc. 提供商业支持;2019 年 Nginx Inc. 被 F5 Networks 收购,现为 F5 旗下产品线)
NGINX Plus 商业版,带负载均衡健康检查、会话保持、监控、技术支持等 F5, Inc.(原 Nginx Inc.)
OpenResty 基于 Nginx 的 Lua 扩展平台,开源 OpenResty Inc.(非 F5,独立项目)

为什么了解公司有用? 开源版下载与文档在 nginx.org;商业支持、企业功能在 F5/NGINX 官网。选型时若需要官方 SLA、高级负载均衡或 WAF,可考虑 NGINX Plus;纯开源方案用社区版即可。

为什么 Nginx 高并发好? 采用事件驱动、非阻塞 I/O,少量 worker 进程即可处理大量连接,不像传统多进程/多线程那样「一个连接一个线程」占内存;适合做反向代理和静态资源服务。

Nginx vs Apache 对比

特性 Nginx Apache
架构模型 事件驱动 进程/线程
并发性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐
内存使用 ⭐⭐⭐⭐⭐ 低 ⭐⭐ 中等
静态文件 ⭐⭐⭐⭐⭐ 快 ⭐⭐⭐ 中等
动态处理 ⭐⭐⭐ 需要配置 ⭐⭐⭐⭐⭐ 强大
配置复杂度 ⭐⭐ 简单 ⭐⭐⭐⭐ 复杂
生活类比 专业的服务员 瑞士全能服务员

Nginx 下载、安装与启动

下载来源
方式 说明 适用场景
官方包仓库 nginx.org/packages 提供各发行版的 repo,安装得到官方构建的稳定版 推荐:版本清晰、便于升级与维护
发行版自带 yum install nginx / apt install nginx 用系统源,版本可能较旧 不追求最新时可选用
源码编译 nginx.org 下载 源码,./configure && make && make install 需要特定模块(如 stub_status、自定义模块)或指定安装路径时

为什么推荐用官方 repo? 版本与 nginx.org 一致,修复与安全更新及时;系统自带的 nginx 可能缺少某些编译选项(如 http_stub_status_module)。

安装(以主流 Linux 为例)

CentOS / RHEL / Rocky / AlmaLinux(使用官方 nginx.org 源)

bash 复制代码
# 1. 安装 yum-utils(用于管理 repo)
sudo yum install -y yum-utils

# 2. 添加 Nginx 官方 repo(以 mainline 为例,稳定版可用 nginx-stable)
sudo yum install -y https://nginx.org/packages/centos/$(rpm -E %rhel)/noarch/RPMS/nginx-release-centos-$(rpm -E %rhel)-0.el$(rpm -E %rhel).ngx.noarch.rpm
# 若上述 URL 不可用,可手动创建 /etc/yum.repos.d/nginx.repo,内容见 nginx.org/en/linux_packages.html

# 3. 安装 Nginx
sudo yum install -y nginx

# 4. 查看版本与安装路径
nginx -v
rpm -ql nginx | head -5

Debian / Ubuntu(使用官方 nginx.org 源)

bash 复制代码
# 1. 安装依赖
sudo apt update
sudo apt install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring

# 2. 导入 Nginx 官方签名密钥并添加 repo(以 Ubuntu 24.04 为例,其他版本见 nginx.org/en/linux_packages.html)
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list

# 3. 安装 Nginx
sudo apt update
sudo apt install -y nginx

# 4. 查看版本
nginx -v

源码编译(简要)

bash 复制代码
# 下载并解压(以 1.26.x 为例,请替换为官网最新稳定版)
wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar -zxf nginx-1.26.2.tar.gz
cd nginx-1.26.2

# 常用选项:--with-http_stub_status_module 用于状态页,--prefix 指定安装目录
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module
make
sudo make install

# 可执行文件在 /usr/local/nginx/sbin/nginx

为什么有时要源码编译? 包仓库里的 nginx 可能未包含 stub_statusrealip 等模块,或需要第三方模块;编译时可 --add-module=... 加入并统一 --prefix 便于运维。

启动、停止、重载与开机自启
操作 命令(systemd) 说明 为什么?
启动 sudo systemctl start nginx 启动 Nginx 服务 安装后首次需 start;或 sudo /usr/sbin/nginx(视安装路径)
停止 sudo systemctl stop nginx 停止服务 维护或排错时使用;会断开当前连接
重载配置 sudo systemctl reload nginxsudo nginx -s reload 不中断服务,加载新配置 为什么用 reload?不杀进程,只让 worker 重新读配置,请求不中断,生产推荐
重启 sudo systemctl restart nginx 先停再起 为什么少用?会短暂断连;仅在 reload 无效或升级二进制时用
查看状态 sudo systemctl status nginx 是否在跑、最近日志 排错第一步看是否 active (running)
开机自启 sudo systemctl enable nginx 设为开机自动启动 为什么开?机器重启后 Nginx 自动起来,无需人工
bash 复制代码
# 常用命令汇总
sudo systemctl start nginx    # 启动
sudo systemctl stop nginx     # 停止
sudo systemctl reload nginx   # 重载配置(推荐)
sudo systemctl restart nginx  # 重启
sudo systemctl status nginx   # 状态
sudo systemctl enable nginx   # 开机自启

# 若为源码安装且未做 systemd 服务,可直接调用二进制
/usr/local/nginx/sbin/nginx           # 启动
/usr/local/nginx/sbin/nginx -s stop   # 停止
/usr/local/nginx/sbin/nginx -s reload # 重载
/usr/local/nginx/sbin/nginx -t       # 仅测试配置文件语法,不启动
验证安装与运行
bash 复制代码
# 1. 版本
nginx -v
# 或 nginx -V 查看编译参数与模块

# 2. 配置文件语法(改配置后先执行,再 reload)
sudo nginx -t

# 3. 进程与端口
ps aux | grep nginx
ss -tlnp | grep :80    # 或 netstat -tlnp | grep :80

# 4. 本机访问测试(若已监听 80)
curl -I http://127.0.0.1/

为什么先 nginx -t 再 reload? 配置有语法错误时,reload 可能不生效或导致异常;-t 只检查不启动,通过后再 reload 更安全。

默认路径速查(包安装)
项目 常见路径(以包安装为例,不同发行版可能略有差异)
主配置 /etc/nginx/nginx.conf
子配置目录 /etc/nginx/conf.d/
二进制 /usr/sbin/nginx
默认站点根目录 /usr/share/nginx/html/var/www/html
日志 /var/log/nginx/access.log/var/log/nginx/error.log
Nginx 版本说明(mainline 与 stable)
分支 版本号规律 说明 适用场景
mainline 奇数次小版本(如 1.27.x、1.29.x) 当前开发主线,新特性、新修复先上,更新较频繁 需要新功能或愿意跟进最新修复时;生产若追求「求稳」可谨慎选用
stable 偶数次小版本(如 1.26.x、1.28.x) 以稳定为主,只收经过验证的修复与安全补丁,新特性滞后 生产环境推荐:变更少、可预期,便于规划升级

为什么会有两条线? mainline 先试新东西,验证后再合到 stable,既保证 stable 可靠,又让愿意尝鲜的人能用上新能力。版本号规则见 nginx.org 各 repo 的说明。

如何选版本? 无特殊需求选 stable ;要某 mainline 才有的特性再选 mainline。安装时注意 repo 名称(如 nginx-mainline vs nginx)对应不同分支。官方下载页:nginx.org/en/download.html

安装与运行常见问题
现象 可能原因 处理思路 为什么?
bind() to 0.0.0.0:80 failed (13: Permission denied) ① 未用 root 启动;② SELinux 禁止监听 80 ① 用 systemctl start nginxsudo nginx,保证以 root 启动(Nginx 会再切到 worker 用户);② 见下 SELinux 为什么 13?端口 <1024 在 Linux 上默认需 root 绑定;SELinux 可进一步限制即使 root 也不能用某端口
SELinux 导致 80 无法绑定 httpd_t 域未允许使用 80 端口 `semanage port -l grep http_port_t看是否含 80;若无则semanage port -a -t http_port_t -p tcp 80;或临时 setenforce 0` 测试(仅排错,不推荐长期关)
Address already in use / 80 端口被占用 本机已有程序监听 80(如 Apache、其他 Nginx) `ss -tlnp grep :80lsof -i :80看占用进程;关掉冲突服务或改 Nginx 的listen` 端口
nginx -t 报错 unknown directive / 模块不存在 当前安装未编译该模块(如 stub_status) nginx -V 看编译参数;包安装一般无 stub_status 时可换官方 repo 或源码编译加 --with-http_stub_status_module 为什么包没有?发行版或旧 repo 的 nginx 可能精简了模块,官方包或自编更全
reload 后不生效 配置有语法错误或 include 路径错误 每次改配置后先 nginx -t,通过再 systemctl reload nginx;看 error.log 是否有 "configuration file ... test failed" 为什么 reload 不报错?reload 会尽量应用新配置,但有严重错误时可能只部分生效或静默失败,-t 能提前发现
403 Forbidden 访问静态页 文件权限或 SELinux 限制 Nginx 读文件 检查 root/alias 路径权限(至少对 Nginx 运行用户可读);SELinux 下可 chcon -R -t httpd_sys_content_t /path/to/web 或关目录的 SELinux 限制 为什么 403?Nginx 能连上但被内核或文件系统拒绝读文件,多为权限或 SELinux 类型不对
yum/apt 安装时 404 或 repo 错误 发行版版本与 repo 不匹配、网络或 repo 未正确添加 对照 nginx.org/en/linux_packages.html 选对系统(CentOS 7/8/9、Ubuntu 版本等);检查 /etc/yum.repos.d/nginx*.repo/etc/apt/sources.list.d/nginx* 的 baseurl/URL 为什么 404?repo 的 URL 里通常带发行版代号,代号错则找不到包

SELinux 排查简要(RHEL/CentOS 等)

bash 复制代码
# 查看 SELinux 状态
getenforce

# 查看是否因 SELinux 拒绝(查看最近审计日志)
grep nginx /var/log/audit/audit.log | audit2why

# 允许 httpd_t 使用 80 端口(需 policycoreutils-python-utils)
semanage port -l | grep http_port_t
semanage port -a -t http_port_t -p tcp 80

# 若为静态文件 403,给站点目录打标签
chcon -R -t httpd_sys_content_t /var/www/html

为什么不要长期关闭 SELinux? 关掉后整体安全策略失效,合规与安全风险大;应针对 Nginx 所需端口和目录做最小授权(semanage、setsebool 等),而不是 setenforce 0

更多发行版与详细步骤见官方:Installing nginxLinux packages

Nginx 反向代理配置

nginx 复制代码
# /etc/nginx/nginx.conf

http {
    # 上游服务器定义
    # 上游服务器定义(为什么用 upstream?集中管理后端列表和负载策略,多个 location 可复用)
    upstream tomcat_servers {
        # 负载均衡:轮询;weight 相等即均分;backup 仅当主节点都不可用时才用
        server 192.168.1.20:8080 weight=1;
        server 192.168.1.21:8080 weight=1;
        server 192.168.1.22:8080 weight=1 backup;

        # 或者使用 ip_hash(会话保持)
        # ip_hash;
        # server 192.168.1.20:8080;
        # server 192.168.1.21:8080;
    }

    server {
        listen 80;
        server_name www.example.com;

        location / {
            # 反向代理到 Tomcat(为什么写 http://tomcat_servers?协议名 + upstream 名,不能写 IP 列表)
            proxy_pass http://tomcat_servers;

            # 传递真实 IP(为什么必须?Tomcat 日志、限流、风控需要真实客户端 IP,否则全是 Nginx 的 IP)
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            # 超时设置(为什么分三种?connect=建连,send=发请求,read=等响应;动态接口耗时长可把 read 调大)
            proxy_connect_timeout 60s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
        }

        # 静态资源缓存
        location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
            root /var/www/static;
            expires 30d;
        }
    }
}

Nginx 负载均衡算法

⚖️ Nginx 负载均衡
🔄 轮询

round-robin
📉 最少连接

least_conn
🔑 IP 哈希

ip_hash
⚖️ 加权

weight

为什么有多种算法? 无状态接口用轮询即可;要会话保持(同一用户固定到同一台 Tomcat)用 ip_hash;后端处理时间差异大时用 least_conn 更公平;机器性能不一致时用 weight 分配比例。

算法 指令 说明 生活类比
轮询 (默认) 依次分发 发牌员轮流发牌
least_conn least_conn 最少连接优先 找最空闲的服务员
ip_hash ip_hash 按 IP 分配 固定服务员服务固定客户
加权 weight=N 权重分配 经验丰富的服务员多派单

🐘 Tomcat 详解

什么是 Tomcat?

Tomcat 是一个开源的 Java Servlet 容器,用于运行 Java Web 应用程序。

Tomcat 背后的项目与机构

项目/产品 说明 对应机构
Apache Tomcat 开源 Java Servlet/JSP 容器,实现 Jakarta EE(原 Java EE)部分规范,Apache 2.0 协议 Apache Software Foundation (ASF) ,无单一商业公司「拥有」;由 Apache 基金会下的 Tomcat 项目 社区开发与发布
起源 最初为 Sun Microsystems 的 Servlet/JSP 参考实现,1999 年 Sun 将代码捐给 ASF,后成为顶级项目;Tomcat 10+ 实现 Jakarta EE,Tomcat 9 及以前实现 Java EE 历史上与 Sun(后为 Oracle 收购的 Java 业务)相关,现完全由 ASF 独立维护
商业支持 Tomcat 本身无官方商业版;企业如需商业支持可选用 Red Hat JBoss EAPVMware TanzuOracle WebLogic 等基于或兼容 Tomcat 技术的商业产品 各厂商(Red Hat、VMware、Oracle 等)提供付费支持,并非「Tomcat 公司」

为什么了解机构有用? 官方下载、文档均在 tomcat.apache.org;无「Tomcat 公司」收费版,选型时若需要企业级 SLA 或集成套件,需看第三方发行版或商业应用服务器。
🐘 Tomcat 架构
🖥️ Server

服务器
📋 Service

服务
🔌 Connector

连接器
⚙️ Engine

引擎
🏠 Host

主机
📂 Context

应用

Tomcat 核心组件

组件 说明 生活类比 为什么?
Server 整个 Tomcat 容器 整个餐厅 为什么是顶层?一个 JVM 里只有一个 Server,关掉 Server 即关掉 Tomcat
Service 服务组件,包含 Connector + Engine 餐厅服务部门(迎宾+后厨) 为什么有 Service?可把多个 Connector 绑到同一 Engine,例如 HTTP 和 AJP 共用一套应用
Connector 连接器,接收请求并交给 Engine 餐厅入口大门 为什么分端口?8080 给 HTTP、8009 给 AJP,不同协议不同入口
Engine 引擎,按 Host 头选 Host 并处理请求 后厨管理 为什么有 defaultHost?请求的 Host 不匹配任何 Host 时,用 defaultHost 避免 404
Host 虚拟主机,对应域名,有 appBase 餐厅分区(包厢名) 为什么多个 Host?一机多站点时按域名区分,各用各的 webapps 目录
Context 应用上下文,一个 Web 应用 具体的餐桌/菜单 为什么 path=""?表示根应用,访问 / 即该应用,常用于单应用部署

Tomcat 连接器类型

连接器 协议 说明 为什么选?
HTTP HTTP/1.1 标准 HTTP 连接,端口如 8080 为什么常用?Nginx、浏览器、压测工具都直接连;无需额外模块,配置简单
AJP AJP/1.3 二进制协议,端口如 8009,多用于与 Apache 配合 为什么有 AJP?历史 Apache+Tomcat 同机时用 AJP 效率高;Nginx 需第三方模块才支持,且要配 secret 防未授权
HTTP/2 HTTP/2 HTTP/2 连接 为什么可选?多路复用、头部压缩,适合现代浏览器;需 TLS 或 h2c 配置

☕ Tomcat 与 JSP、Servlet、Java、Vue 的配置与使用

Tomcat 是 Java 应用服务器,用来跑 ServletJSP ;前端若用 Vue 做前后端分离,通常 Vue 打包成静态资源由 Nginx 或 Tomcat 提供,接口由 Tomcat 提供。本节简要说明四者与 Tomcat 的配置和使用方式。

整体关系一览

🐘 Tomcat
🖥️ Nginx(可选)
👤 浏览器
请求 HTML/JS/API
静态 Vue/HTML
/api → 代理
静态资源

Vue 打包文件
Servlet

接口
JSP

页面

技术 和 Tomcat 的关系 典型用法
Java Tomcat 本身用 Java 跑,应用也是 Java(Servlet/JSP) 装好 JDK、设 JAVA_HOME,应用打成 WAR 或目录放到 webapps
Servlet Tomcat 是 Servlet 容器,请求由 Servlet 处理 写 Java 类实现 Servlet 接口,在 web.xml 或注解里配置 URL 映射
JSP JSP 会被编译成 Servlet,由 Tomcat 的 Jasper 引擎执行 把 .jsp 文件放到 webapp 下,通过 URL 访问即可
Vue Vue 打包后是静态 HTML/JS/CSS,可放 Tomcat 或 Nginx;接口调 Tomcat 提供的 Servlet/Spring 等 开发时 Vue devServer 代理到 Tomcat;生产可 Nginx 放静态 + 反向代理 /api 到 Tomcat

Tomcat 与 Java:运行环境与启动参数

Tomcat 依赖 JDK (建议 JDK 8 或 11+,与 Tomcat 版本匹配)。需要配置两件事:JAVA_HOMEJVM 启动参数

1. 设置 JAVA_HOME

bash 复制代码
# Linux / macOS:在 /etc/profile 或 ~/.bashrc 中
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$JAVA_HOME/bin:$PATH

# 验证
java -version
echo $JAVA_HOME

2. Tomcat 启动参数(setenv.sh / setenv.bat)

Tomcat 启动脚本会读取 CATALINA_HOME/bin/setenv.sh(Linux)或 setenv.bat(Windows),在这里设 JVM 参数,不修改 catalina.sh 本身。

bash 复制代码
# 创建 $CATALINA_HOME/bin/setenv.sh(无则新建)
# 示例:堆内存、GC、编码

export JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx1024m"
export JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"
export JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC"

3. 可选:CATALINA_OPTS 与 JAVA_OPTS 区别

  • JAVA_OPTS:所有 Tomcat 进程(包括 stop)都会用,一般放通用参数。
  • CATALINA_OPTS :仅 start / run 时用,一般放运行时的堆、GC 等。
bash 复制代码
# 仅 start 时生效的堆与 GC
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m -Xmx1024m -XX:+UseG1GC"

Tomcat 与 Servlet:部署与 URL 映射

Servlet 是 Java 里处理 HTTP 请求的规范,Tomcat 根据配置把某个 URL 交给哪个 Servlet 处理。

1. 标准目录结构(WAR 解压后等价)

复制代码
myapp/
├── WEB-INF/
│   ├── web.xml          # 配置 Servlet、过滤器、欢迎页等
│   ├── classes/         # 你的 .class 或源码(若用 IDE 导出)
│   └── lib/             # 依赖 jar
├── index.html           # 静态页面
└── hello.jsp            # JSP 页面

2. web.xml 里配置 Servlet(传统方式)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.example.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/api/hello</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

访问:http://host:8080/myapp/api/hello 会由 HelloServlet 处理。

3. 注解方式(Servlet 3.0+,无需在 web.xml 写 mapping)

java 复制代码
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/api/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/plain;charset=UTF-8");
        resp.getWriter().write("Hello from Servlet");
    }
}

4. 部署方式

  • 方式 A :把 myapp 目录放到 Tomcat 的 webapps/ 下,访问路径为 /myapp
  • 方式 B :打成 myapp.war 放到 webapps/,Tomcat 会自动解压成 myapp/
  • 方式 C:在 server.xml 的 Host 下加 Context,指定 docBase 指向磁盘上的目录(如独立部署目录)。
xml 复制代码
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
    <Context path="/myapp" docBase="/opt/myapp" />
</Host>

Tomcat 与 JSP:放置与访问

JSP 本质是「带 Java 片段的 HTML」,第一次访问时会被编译成 Servlet 再执行。

1. 放哪里

  • 放在应用的根目录或子目录下,不要 放在 WEB-INF 里(WEB-INF 下的 JSP 不能通过 URL 直接访问,只能内部转发)。
  • 例如:myapp/hello.jspmyapp/page/foo.jsp

2. 如何访问

  • 直接访问:http://host:8080/myapp/hello.jsp
  • 若配置了欢迎页且是 JSP,访问 http://host:8080/myapp/ 也会打开该 JSP。

3. 简单示例

jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>JSP 示例</title></head>
<body>
    <h1>当前时间:<%= new java.util.Date() %></h1>
    <%
        String name = request.getParameter("name");
        if (name != null) {
            out.println("<p>Hello, " + name + "</p>");
        }
    %>
</body>
</html>

4. 与 Servlet 的关系

  • 请求先到 Tomcat,根据 URL 决定是走 JSP 还是 Servlet。
  • JSP 第一次访问时由 Jasper 编译成 *_jsp.java 再编译成 class,之后以 Servlet 形式运行;可理解为「写 JSP = 写一种自动生成的 Servlet」。

Tomcat 与 Vue:前后端分离的配置与使用

Vue 是前端框架,打包后得到静态文件(HTML、JS、CSS)。后端接口通常由 Tomcat(Servlet、Spring MVC 等)提供。有两种常见部署方式。

方式一:Vue 打包结果放 Tomcat,接口同域

  • Vue 执行 npm run build,生成 dist/(默认有 index.htmlassets/)。

  • dist 里的内容放到 Tomcat 应用的根目录,例如:

    webapps/myapp/
    ├── index.html
    ├── assets/
    │ ├── index-xxx.js
    │ └── index-xxx.css
    └── WEB-INF/
    └── ...(后端 Servlet、JSP 等)

  • 前端请求接口用相对路径,如 /myapp/api/xxx,和 Servlet 的 url-pattern 一致。

  • 若 Vue 的 publicPath 是 /myapp/,打包后资源路径会带 /myapp/,与上述结构一致。

Vue 侧(vue.config.js)示例

javascript 复制代码
module.exports = {
  publicPath: '/myapp/',
  devServer: {
    port: 8081,
    proxy: {
      '/myapp/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
}
  • 开发时:访问 http://localhost:8081,请求 /myapp/api/* 会被代理到 Tomcat 的 8080。
  • 生产:同一应用下既有静态(Vue)也有 /myapp/api(Tomcat Servlet)。

方式二:Vue 放 Nginx,接口反向代理到 Tomcat(更常见)

  • Nginx 提供 Vue 的静态文件(root 指向打包后的 dist 或单独目录)。
  • 接口统一用前缀(如 /api)反向代理到 Tomcat。
nginx 复制代码
server {
    listen 80;
    server_name www.example.com;

    root /var/www/vue-dist;
    index index.html;
    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://tomcat_cluster;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
  • Vue 里接口 baseURL 设为 /api(或完整域名),这样生产环境请求会发到 Nginx,再由 Nginx 转给 Tomcat。
  • 开发时同样用 devServer proxy 把 /api 转到本机 Tomcat。

小结

场景 建议
小项目、希望一个 Tomcat 同时提供页面和接口 Vue 打包到 Tomcat 应用目录,publicPath 与 context path 一致;接口用相对路径 /myapp/api/...
生产环境、前后端分离清晰 Nginx 托管 Vue 静态,/api 反向代理到 Tomcat;Vue 的 baseURL 用 /api 或完整域名
本地开发 Vue devServer proxy 到 http://localhost:8080,避免跨域

🚀 Nginx 反向代理 Tomcat 实战

环境拓扑

🐘 Tomcat 层
🖥️ Nginx 层
🌐 Nginx

192.168.1.10:80

反向代理
📦 Tomcat1

192.168.1.20:8080
📦 Tomcat2

192.168.1.21:8080
📦 Tomcat3

192.168.1.22:8080

Nginx 配置

nginx 复制代码
# /etc/nginx/conf.d/proxy_tomcat.conf

upstream tomcat_cluster {
    # 负载均衡配置
    server 192.168.1.20:8080 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.1.21:8080 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.1.22:8080 weight=1 max_fails=2 fail_timeout=30s;

    # 健康检查(需要商业版或 openresty)
    # check interval=3000 rise=2 fall=3 timeout=1000;
}

server {
    listen 80;
    server_name www.example.com;

    # 访问日志
    access_log /var/log/nginx/tomcat_access.log;
    error_log /var/log/nginx/tomcat_error.log;

    # 根路径代理到 Tomcat
    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;

        # WebSocket 支持
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # 超时配置
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # 缓冲配置
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;
    }

    # 静态资源直接由 Nginx 处理
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|html|htm)$ {
        root /var/www/static;
        expires 30d;
        access_log off;
    }
}

Tomcat 配置

xml 复制代码
<!-- /etc/tomcat/server.xml -->

<!-- 配置 AJP 连接器 -->
<Connector port="8009"
           protocol="AJP/1.3"
           redirectPort="8443"
           secret="your-secret"
           address="192.168.1.20" />

<!-- 配置 HTTP 连接器 -->
<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           address="192.168.1.20"
           maxThreads="500"
           minSpareThreads="50"
           enableLookups="false" />

<!-- 配置 Engine -->
<Engine name="Catalina" defaultHost="localhost">
    <!-- 配置虚拟主机 -->
    <Host name="localhost"
           appBase="webapps"
           unpackWARs="true"
           autoDeploy="true">
        <!-- Context 配置 -->
        <Context path="" docBase="/opt/myapp" />
    </Host>
</Engine>

会话保持

📌 会话保持需求
🔑 ip_hash
🍪 sticky_cookie
🔄 session 复制
同一 IP 到同一 Tomcat
基于 Cookie 分配
Session 集群复制

为什么需要会话保持? 用户登录后 Session 存在某台 Tomcat 上,若下次请求被分到另一台,会丢登录状态。ip_hash 或 sticky 让同一用户始终落到同一台;或通过 Tomcat 集群把 Session 复制到多台,任一台都能识别。

方法1:ip_hash

nginx 复制代码
upstream tomcat_cluster {
    ip_hash;  # 会话保持
    server 192.168.1.20:8080;
    server 192.168.1.21:8080;
}

方法2:sticky_cookie(商业版)

nginx 复制代码
upstream tomcat_cluster {
    sticky cookie srv_id expires=1h domain=.example.com path=/;
    server 192.168.1.20:8080;
    server 192.168.1.21:8080;
}

方法3:Tomcat Session 复制

xml 复制代码
<!-- 在 Tomcat 的 context.xml 中配置 -->
<Manager className="org.apache.catalina.ha.session.BackupManager"
          expireSessionsOnShutdown="false"
          notifyListenersOnReplication="true"
          mapSendOptions="6"/>

<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.nio.NioSender"
            address="auto"
            port="4000"
            autoBind="true"
            connectTimeout="5000"
            maxWait="10000"/>
</Channel>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>

Tomcat 集群配置与架构

多台 Tomcat 在 Nginx 后做负载均衡即构成「Tomcat 集群」。集群要解决两类问题:高可用/分流Session 一致性(有状态应用)。本节说明集群架构、Session 方案选择,以及 Tomcat 自带的集群与 Session 复制配置。

集群架构示意

📡 可选:Session 同步
🐘 Tomcat 集群
🖥️ 接入层
Nginx

负载均衡
Tomcat-1

jvmRoute=node1
Tomcat-2

jvmRoute=node2
Tomcat-3

jvmRoute=node3
多播/组播

或 TCP

  • 无 Session 复制:Nginx 用 ip_hash 或 sticky,同一用户始终到同一台 Tomcat,无需多机同步 Session。
  • 有 Session 复制:多台 Tomcat 通过 Tomcat 集群(多播或 TCP)同步 Session,任意一台都能识别同一用户;Nginx 可用轮询。
集群下 Session 的三种做法
方式 做法 优点 缺点 适用
会话保持 Nginx ip_hash 或 sticky,同一用户固定到一台 Tomcat 配置简单、无集群通信 该节点宕机则该用户 Session 丢失;扩容要 rehash 节点少、可接受单点 Session 丢失
Tomcat Session 复制 每台 Tomcat 加入同一 Cluster,Session 变更通过组播/TCP 同步到其他节点 无单点、任意节点可服务 占带宽和内存、节点多时复制量大;要开多播或端口 节点数不多(如 2~4)、内网稳定
外部 Session 存储 Session 存 Redis/Memcached,所有 Tomcat 读同一存储 扩容方便、Session 与进程解耦 依赖 Redis、序列化与网络延迟 多机水平扩展、已有 Redis 时
Tomcat 集群配置要点

1. 为每台 Tomcat 设置 jvmRoute

同一集群内每台 Tomcat 的 jvmRoute 必须唯一,用于 Session 亲和与故障转移时识别节点。在 server.xml 的 Engine 上配置:

xml 复制代码
<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">
  • 机器 1:jvmRoute="node1"
  • 机器 2:jvmRoute="node2"
  • 机器 3:jvmRoute="node3"

若使用 Nginx sticky,Cookie 里会带上类似 node1 的路由信息,Nginx 可据此把请求送到对应节点。

2. 在 server.xml 中启用 Cluster

server.xmlEngine 内增加 Cluster 配置,使该 Engine 下的应用可参与 Session 复制。下面示例使用多播做成员发现与复制(同一网段、允许多播时可用):

xml 复制代码
<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">
    <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.nio.NioSender"
                    address="auto"
                    connectTimeout="5000"
                    maxWait="10000"/>
        </Channel>
        <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
               filter=""/>
        <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
    </Cluster>

    <Realm>...</Realm>
    <Host name="localhost" appBase="webapps" ...>
        ...
    </Host>
</Engine>
  • DeltaManager:Session 变更(delta)同步到集群内所有其他节点;节点多时复制量大。
  • BackupManager:每台 Session 只备份到另一台,复制量小,但单台宕机可能丢部分 Session(取决于备份策略)。
  • Membership :多播地址 address/port 集群内必须一致,且防火墙/云环境需放行多播或改用 TCP 成员发现。
  • Receiverport="4000" 为集群通信端口,每台机器端口可相同(不同 IP)或不同(同机多实例);防火墙需放行。
  • ReplicationValve :在响应提交前把 Session 变更复制出去;filter 可过滤不复制的内容。

3. 应用开启分布式 Session(web.xml)

参与集群复制的 Web 应用需在 WEB-INF/web.xml 中声明 distributable

xml 复制代码
<web-app ...>
    <distributable/>
    ...
</web-app>

否则 Tomcat 不会把该应用的 Session 纳入集群复制。且 Session 里放的对象需要可序列化 (实现 java.io.Serializable),否则复制会报错。

4. 多播与防火墙注意

  • 多播地址如 228.0.0.4、端口 45564:集群内所有节点要能收到多播包;云主机或 Docker 常不支持多播,需改用 StaticMember 等 TCP 成员配置。
  • 集群通信端口(如 Receiver 的 4000):节点之间要能互通,防火墙需放行。
部署与运维要点
项目 说明
同一应用 每台 Tomcat 部署同一版本 WAR/目录,避免请求到不同节点时行为不一致。
jvmRoute 唯一 每台 jvmRoute 不同,且与 Nginx 的 upstream 对应(若用 sticky)。
多播/端口 多播不通时改用 TCP 成员;Receiver 端口不冲突(多实例同机时改不同 port)。
序列化 Session 中对象必须可序列化,避免大对象或不可序列化对象。
监控 观察集群视图(Tomcat Manager 或 JMX)、复制是否失败、网络与内存占用。
与 Redis Session 的简单对比
  • Tomcat Session 复制:不依赖外部组件,Session 存在各节点内存,通过 Tomcat 集群通道同步;适合节点少、内网稳定。
  • Redis Session:Session 存 Redis,所有 Tomcat 无状态;需应用或 Filter 把 Session 存 Redis(如 Spring Session),适合多机扩展、已有 Redis 的场景。

若已有 Redis,多数项目更倾向用 Redis Session 做多机 Session 共享;若无 Redis 且只有 2~4 台 Tomcat,可用 Tomcat 自带的 Session 复制 做集群。


🖼️ 静态资源缓存

缓存策略

静态文件
动态请求
👤 客户端请求
资源类型?
📁 Nginx 直接返回

有缓存则返回缓存
➡️ 转发到 Tomcat
减少 Tomcat 负载
Tomcat 处理

为什么动静分离? 静态文件(图片、CSS、JS)由 Nginx 读磁盘或缓存返回,比经过 Tomcat 解析、再读文件省 CPU 和线程;Tomcat 专注处理 JSP、Servlet、接口,整体吞吐更高。

Nginx 静态资源配置

nginx 复制代码
server {
    listen 80;
    server_name www.example.com;

    # 静态文件缓存配置(为什么 1y?图片/CSS/JS 带版本号时长期缓存安全,immutable 告诉浏览器不必再验)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot|svg)$ {
        root /var/www/static;
        expires 1y;
        access_log off;
        add_header Cache-Control "public, immutable";
    }

    # HTML 文件缓存(为什么 7d?HTML 可能引用新资源,不宜过长;无版本号时适中即可)
    location ~* \.html$ {
        root /var/www/static;
        expires 7d;
        add_header Cache-Control "public";
    }

    # 禁止缓存(为什么 jsp/do no-cache?动态内容每次可能不同,缓存会导致看到旧数据或错乱)
    location ~* \.(php|jsp|do)$ {
        proxy_pass http://tomcat_cluster;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }
}

📊 性能优化

Nginx 优化

nginx 复制代码
# /etc/nginx/nginx.conf

user nginx;
worker_processes auto;        # 为什么 auto?一般等于 CPU 核数,充分利用多核
worker_rlimit_nofile 65535;   # 为什么设?每个连接占一个 fd,默认限制可能不够高并发

events {
    worker_connections 10240; # 为什么 10240?单 worker 最大连接数,总连接 ≈ worker_processes × worker_connections
    use epoll;                 # 为什么 epoll?Linux 下高效多路复用,高并发必备
    multi_accept on;           # 为什么 on?一次 accept 多个新连接,减少唤醒次数
}

http {
    # 基础优化(为什么 sendfile?内核直接在内核态把文件写到 socket,少一次用户态拷贝,静态文件更快)
    sendfile on;
    tcp_nopush on;   # 与 sendfile 搭配,攒包再发,减少小包
    tcp_nodelay on;  # 关闭 Nagle,小响应及时发,为什么?动态内容往往小包,延迟敏感
    keepalive_timeout 65;
    types_hash_max_size 2048;
    client_max_body_size 50M;  # 为什么限制?防止大 body 占满内存或磁盘,按业务上传需求设

    # Gzip 压缩(为什么开?文本类响应体积明显减小,带宽和延迟都改善;为什么 gzip_min_length?太小的包压缩反而变大)
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # 隐藏版本号(为什么?减少暴露版本信息,降低被针对攻击的概率)
    server_tokens off;
}

Tomcat 优化

xml 复制代码
<!-- /etc/tomcat/server.xml -->

<Connector port="8080"
           protocol="HTTP/1.1"
           maxThreads="500"
           minSpareThreads="50"
           maxSpareThreads="200"
           acceptCount="500"
           acceptorThreadCount="20"
           connectionTimeout="20000"
           enableLookups="false"
           compression="on"
           compressionMinSize="2048"
           noCompressionUserAgents="gozilla-trident"
           compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json"
           URIEncoding="UTF-8" />

<!-- JVM 优化 -->
<!-- 在 catalina.sh 或 setenv.sh 中添加 -->
JAVA_OPTS="$JAVA_OPTS -Xms2048m -Xmx2048m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:+UseConcMarkSweepGC -XX:+UseCMSClassLoader"

📈 监控与日志

Nginx 监控

命令/配置 作用 为什么常用?
systemctl status nginx 看 Nginx 是否在跑 为什么先看?进程挂了则所有请求失败,排错第一步
`netstat -antp grep :80 wc -l`
tail -f .../tomcat_access.log 实时看访问日志 为什么看日志?404、502、慢请求、IP 分布都在这里,排查问题必看
stub_status on 输出简单状态页(需编译 --with-http_stub_status_module) 为什么有用?可看到 active/reading/writing/waiting 连接数,监控大盘常用
bash 复制代码
# 1. 基础状态查看
systemctl status nginx

# 2. 连接数统计
netstat -antp | grep :80 | wc -l

# 3. 访问日志分析
tail -f /var/log/nginx/tomcat_access.log

# 4. Nginx 状态模块(需要编译时添加)
location /nginx_status {
    stub_status on;
    access_log off;
    allow 192.168.1.0/24;
    deny all;
}

Tomcat 监控

bash 复制代码
# 1. 查看 Tomcat 状态
systemctl status tomcat

# 2. 查看 Tomcat 日志
tail -f /opt/tomcat/logs/catalina.out

# 3. Tomcat 管理界面
# 访问 http://ip:8080/manager

🔧 Nginx 线上常见问题与解决方案

实际运行中常遇到 502/504、上传失败、连接数爆满、响应慢等问题。本节按现象 → 原因 → 排查 → 配置方案整理,便于快速定位与修复。

问题定位思路(概览)

🚨 线上异常
看 Nginx 状态码/日志
502 Bad Gateway
504 Gateway Timeout
413 / 499 / 5xx
慢 / 超时 / 连接满
查 upstream 是否可达

max_fails/fail_timeout
查 proxy_read_timeout

与后端耗时
client_max_body_size

或客户端断开 499
缓冲/连接数/限流

502 Bad Gateway

现象:用户看到 502,Nginx 把请求转给后端时失败。

常见原因与处理

原因 排查方法 解决方案(配置示例)
后端 Tomcat 未启动或宕机 curl -I http://后端IP:8080/、看 Tomcat 进程与日志 启动 Tomcat;在 upstream 中配多台 + max_fails/fail_timeout,避免把请求打到已挂节点
后端端口/防火墙不通 telnet 后端IP 8080、本机 firewall/iptables 放行应用端口;确保 Nginx 能访问后端网段
连接被拒绝(connection refused) Nginx error.log 有 "connect() failed (111: Connection refused)" 同上,并检查 Tomcat 的 Connector address 是否绑定到 0.0.0.0 或正确 IP
upstream 全部被摘除 所有 server 都因 max_fails 被标记 down 恢复至少一台后端;或临时调大 max_fails、缩短 fail_timeout,或使用 backup 节点

推荐 upstream 配置(减少 502)

nginx 复制代码
upstream tomcat_cluster {
    # 连续 2 次失败则 30s 内不再转发到该节点,避免持续 502
    server 192.168.1.20:8080 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.1.21:8080 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.1.22:8080 weight=1 max_fails=2 fail_timeout=30s backup;

    # 与后端保持长连接,减少建连开销(需 Nginx 1.1+)
    keepalive 32;
}

server {
    location / {
        proxy_pass http://tomcat_cluster;
        proxy_http_version 1.1;
        proxy_set_header Connection "";   # 与 keepalive 配合
        proxy_connect_timeout 5s;          # 建连快失败,不长时间挂住
        proxy_read_timeout 60s;            # 按业务调整,避免后端慢导致长时间占连接
    }
}

为什么 max_fails 不宜过大? 设太大则故障节点被摘除过慢,用户会持续打到坏节点;过小则网络抖动就摘掉,一般 2~3 次即可。

504 Gateway Timeout

现象:用户等待较久后出现 504,表示 Nginx 在规定时间内没拿到后端完整响应。

常见原因 :后端处理时间超过 Nginx 的 proxy_read_timeout(默认 60s),或后端卡死。

排查 :看 Nginx error.log 是否有 "upstream timed out";看 Tomcat 日志是否有慢 SQL、死锁、GC 长等。

解决方案

nginx 复制代码
# 对耗时较长的接口单独加大读超时(按业务区分 location)
location /api/ {
    proxy_pass http://tomcat_cluster;
    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 120s;   # 长任务接口可适当调大,如 120s 或 300s
}

# 普通页面保持较短超时,避免慢请求占满连接
location / {
    proxy_pass http://tomcat_cluster;
    proxy_read_timeout 30s;
}

为什么不能全局设很大? 若所有请求都 300s 超时,慢或卡住的后端会占满 Nginx 与后端连接,影响其他请求;应对真正慢的接口单独设 location 和超时。

413 Request Entity Too Large

现象:上传较大文件时返回 413。

原因 :请求体超过 Nginx 的 client_max_body_size(默认 1m)。

解决方案

nginx 复制代码
# 在 http 或 server 或 location 中设置(单位 k/m/g)
client_max_body_size 50m;

# 仅上传接口放大
location /api/upload {
    client_max_body_size 100m;
    proxy_pass http://tomcat_cluster;
    proxy_request_buffering on;   # 先收完再转给后端,避免后端超时(可选)
}

499 Client Closed Request

现象 :Nginx 日志中状态码为 499,表示客户端在 Nginx 收到后端响应之前就断开了(如用户关页面、超时重试)。

原因:前端/客户端超时时间小于 Nginx 或后端处理时间;或用户主动关闭。

可做措施

  • 适当增大 proxy_read_timeout,减少「后端还没返回客户端就断」的情况(需与业务平衡)。
  • 在日志中记录 499 便于区分「用户取消」与「后端太慢」:access_log ... upstream_status 等。
  • 优化后端与接口耗时,从根上减少长时间占用。

为什么 499 不能完全消除? 用户关页面、网络断开等必然会产生;可通过监控 499 占比判断是否多为「后端慢导致前端先断」。

连接数满、文件描述符不足

现象:error.log 出现 "too many open files" 或 "worker_connections are not enough";并发高时新连接失败。

原因 :单机连接数超过 worker_connections × worker_processes,或进程的 fd 限制低于实际需求。

解决方案

nginx 复制代码
# nginx.conf 顶层
worker_processes auto;
worker_rlimit_nofile 65535;   # 每个 worker 能打开的 fd 上限,需 ≥ worker_connections

events {
    worker_connections 10240;  # 单 worker 最大并发连接数
    use epoll;
    multi_accept on;
}

系统级限制需同步放大(否则 worker_rlimit_nofile 受限于 ulimit):

bash 复制代码
# 临时
ulimit -n 65535

# 持久(示例:systemd 服务)
# 在 /etc/systemd/system/nginx.service.d/override.conf 或 unit 里加:
# LimitNOFILE=65535

响应慢、缓冲与缓冲区

现象:后端很快返回,但用户感觉慢;或大响应时出现不完整、超时。

可能原因 :Nginx 默认会缓冲后端响应,若 proxy_buffers 过小或关闭缓冲,大响应会阻塞或多次读;或后端本身慢。

可调配置

nginx 复制代码
location / {
    proxy_pass http://tomcat_cluster;

    # 缓冲:先接后端数据再发给客户端,减轻后端「写慢」的影响
    proxy_buffering on;
    proxy_buffer_size 4k;           # 响应头缓冲
    proxy_buffers 8 8k;             # 响应体缓冲块数与大小
    proxy_busy_buffers_size 16k;    # 正在往客户端发时占用的缓冲
    proxy_max_temp_file_size 256m;  # 缓冲不够时写临时文件的上限

    # 若希望尽快把后端数据流式传给客户端(如大文件下载),可关缓冲
    # proxy_buffering off;
}

为什么默认开缓冲? 后端先写完,Nginx 再统一发给客户端,能复用连接、减少后端占用时间;流式场景再按 location 关缓冲。

日志与排查配置(便于线上排错)

在 access_log 中记录上游与耗时,便于查 502/504 和慢请求:

nginx 复制代码
# 在 http 或 server 中定义 log_format
log_format main_with_upstream '$remote_addr - $remote_user [$time_local] '
                              '"$request" $status $body_bytes_sent '
                              '"$http_referer" "$http_user_agent" '
                              'upstream: $upstream_addr '
                              'rt=$request_time uct="$upstream_connect_time" '
                              'uht="$upstream_header_time" urt="$upstream_response_time"';

access_log /var/log/nginx/tomcat_access.log main_with_upstream;
  • $upstream_addr:实际转发的后端地址,502 时看是否为空或异常。
  • $request_time:请求在 Nginx 内总耗时。
  • $upstream_response_time:后端响应耗时,用于判断慢在 Nginx 还是后端。

排查 502 时grep 502 /var/log/nginx/tomcat_access.log 看对应 upstream_addr;同时看 error.log 的 "upstream" 与 "connect() failed"。

限流与简单防护(避免打满后端)

现象:突发流量或恶意请求把 Tomcat 打满,需要在前端限流。

示例配置

nginx 复制代码
# 在 http 中定义限流区
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;   # 每 IP 每秒 10 请求
limit_conn_zone $binary_remote_addr zone=addr:10m;            # 每 IP 并发连接数

server {
    location / {
        limit_req zone=one burst=20 nodelay;   # 超过 rate 的请求可排队 burst 个,其余 503
        limit_conn addr 20;                     # 每 IP 最多 20 个并发连接

        proxy_pass http://tomcat_cluster;
    }
}

为什么用 limit_req + limit_conn? limit_req 控制请求速率,limit_conn 控制单 IP 并发连接数,两者结合可减轻突发与单点滥用对后端的影响。

小结(线上问题处理顺序)

  1. 看状态码与 Nginx/Tomcat 日志:502 先看 upstream 是否可达、max_fails;504 看 proxy_read_timeout 与后端耗时。
  2. 调超时与缓冲:按业务区分 location 设置 read_timeout;大响应或流式按需调 proxy_buffers 或关缓冲。
  3. 控连接与限流:worker_rlimit_nofile、worker_connections、limit_req/limit_conn 防止连接与请求打满。
  4. 上传与大小:413 时调大 client_max_body_size;499 结合日志与后端耗时优化。

💡 最佳实践

配置建议

建议 说明 为什么?
动静分离 Nginx 处理静态,Tomcat 处理动态 为什么?静态走 Nginx 省 Tomcat 线程和 CPU,整体 QPS 更高
会话保持 根据应用选 ip_hash、sticky 或 Session 复制 为什么?有状态应用必须保证同一用户落到同一台或 Session 共享,否则登录态丢失
负载均衡 多台 Tomcat 分担压力 为什么?单机有上限,多机 + 负载均衡才能提高可用性和吞吐
缓存策略 静态设较长 expires,动态 no-cache 为什么?减少重复请求和后端压力;动态内容缓存会看到旧数据
日志轮转 logrotate 或 Nginx 的 access_log 按天/按大小切 为什么?单文件过大会影响写性能和排查,轮转后便于按日期查看和归档

安全建议

nginx 复制代码
# 隐藏后端服务器版本
proxy_hide_header X-Powered-By;

# 限制请求大小
client_max_body_size 20M;

# 限制请求方法
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
    return 405;
}

# 限制访问
allow 192.168.1.0/24;
deny all;

🔀 与其他软件配合使用及方案对比

实际项目中 Nginx 与 Tomcat 常与数据库、缓存、消息队列、高可用组件等一起使用;同时,前端代理或 Java 容器也有多种替代方案。本节先介绍常见组合架构 ,再对同类方案做简要对比,便于选型与扩展。

与其他软件配合使用

典型组合架构示意

💾 数据与中间件
📦 应用层
🖥️ 接入层
👤 用户
浏览器/客户端
Nginx

反向代理/静态
Tomcat 1
Tomcat 2
Redis

Session/缓存
MySQL

主库
MQ

RabbitMQ/Kafka

常见组合与用途
组合 角色 典型用途 为什么这样配?
Nginx + Tomcat + MySQL Nginx 代理与静态,Tomcat 跑 Java,MySQL 存业务数据 经典 Web 三层:展示→应用→数据 为什么加 MySQL?Tomcat 只负责计算,持久化交给数据库;Nginx 不直连 MySQL,安全与职责清晰
Nginx + Tomcat + Redis Redis 做 Session 共享或页面/接口缓存 多机 Tomcat 时 Session 共享;热点数据缓存 为什么用 Redis?多台 Tomcat 无状态扩展时,Session 存 Redis 比 ip_hash 或 Tomcat 集群复制简单;缓存可减轻 DB 压力
Nginx + Tomcat + MySQL + Redis 上述两者结合 生产常见:静态→应用→缓存→数据库 为什么四件套?动静分离、应用水平扩展、缓存降 DB 负载、数据库做主存储,是通用 Web 架构
Nginx + Tomcat + 消息队列(RabbitMQ/Kafka) 请求进 Tomcat,耗时或解耦任务投递 MQ,异步消费 下单写库+发消息、日志/事件采集 为什么加 MQ?同步写库+发邮件/短信会拖慢响应;MQ 解耦、削峰,Tomcat 只负责发消息,消费者另起服务
Nginx + Tomcat + Keepalived/LVS Nginx 或 LVS 前再加一层 VIP,做高可用 单 Nginx 故障时自动切到备机 为什么需要?Nginx 是单点,配合 Keepalived 双机或 LVS 多机,实现入口高可用
Nginx + Tomcat + ELK/日志中心 Nginx、Tomcat 日志统一采集到 Elasticsearch,Kibana 查询 统一日志、排查问题、审计 为什么集中日志?多机多实例时日志分散,集中后便于检索和监控告警
Nginx + Tomcat + Docker/K8s 容器化部署,Nginx 做 Ingress 或侧车,Tomcat 跑在 Pod 里 弹性伸缩、灰度、环境一致 为什么容器化?镜像一次构建多环境复用;K8s 可自动扩缩容、滚动发布,Nginx 或 Ingress 做流量入口

生活类比:Nginx = 商场大门与导购,Tomcat = 各专柜,MySQL = 仓库账本,Redis = 前台暂存柜,MQ = 内部传话员,Keepalived = 双大门轮流值班。

方案对比:谁来做 Tomcat 的前端代理?

对比项 Nginx + Tomcat Apache + Tomcat(mod_jk / mod_proxy_ajp) Caddy + Tomcat OpenResty + Tomcat
协议 HTTP 代理(proxy_pass) AJP 或 HTTP(mod_jk 用 AJP,mod_proxy 可 HTTP/AJP) HTTP 反向代理 HTTP 反向代理(可嵌 Lua)
静态能力 很强,事件驱动 中等,多进程/线程 较好,Go 实现 同 Nginx,且可 Lua 动态处理
配置方式 手写 conf,语法简洁 手写 conf,模块多 Caddyfile 自动 HTTPS 同 Nginx + Lua 脚本
AJP 支持 需第三方模块,一般不推荐 原生 mod_jk / mod_proxy_ajp 需第三方或自写 Lua
适用场景 通用反向代理 + 负载均衡,静态多 历史 Apache 栈、需 AJP 时 要自动 HTTPS、配置极简 要复杂逻辑、动态路由、WAF 等
生活类比 专业前台 + 多柜台 全能前台(也做静态)+ 多柜台 自动办卡的前台 会编程的前台,规则可自己写

为什么 Nginx 不官方支持 AJP? Nginx 以事件驱动、HTTP 为中心设计;AJP 是二进制协议且多与 Apache 生态绑定,社区版一直用 HTTP 连 Tomcat 即可满足大多数场景,性能足够。若必须用 AJP,可选 Apache + mod_jk 或 Nginx 第三方 AJP 模块(需自行评估维护成本)。

方案对比:Java 应用服务器 / Servlet 容器

对比项 Tomcat Jetty Undertow
定位 Servlet/JSP 容器,实现 Jakarta EE 部分规范 轻量嵌入式,常内嵌到应用 轻量、高性能,内嵌或独立
典型用法 独立部署,Nginx 反向代理多实例 打成 jar 内嵌 Jetty 启动;或独立 Spring Boot 默认内嵌之一;或独立
性能/资源 稳定、功能全,资源中等 轻量、启动快 高并发、低内存,性能指标好
适用 传统 Java Web 项目、多应用同机 微服务、嵌入式、云原生 高并发 API、微服务、与 Spring Boot 搭配

为什么很多项目仍选 Tomcat? 生态成熟、文档多、与 Spring 等框架兼容好,独立部署时运维熟悉度高;Jetty/Undertow 更常见于「内嵌」或对资源/延迟极敏感的场景。

小结(为什么了解配合与对比有用?)

  • 配合使用:按业务加 MySQL、Redis、MQ、Keepalived、日志中心或容器平台,可逐步从「Nginx+Tomcat」扩展为完整生产架构。
  • 前端代理对比:要简单稳定、静态多选 Nginx;历史 Apache 栈或必须 AJP 选 Apache+mod_jk;要自动 HTTPS 和极简配置选 Caddy;要复杂逻辑选 OpenResty。
  • Java 容器对比:独立部署、多应用选 Tomcat;内嵌、微服务可考虑 Jetty/Undertow。

🎯 总结与官方参考

Nginx + Tomcat 是经典的反向代理架构组合,发挥各自优势,实现高性能的 Web 服务。

核心要点记忆口诀

  • Nginx 做代理,负载均衡能力强
  • Tomcat 做 Java,动态应用它最行
  • 动静要分离,静态 Nginx 处理
  • 会话需保持,ip_hash 或 sticky
  • 缓存要合理,过期时间要设置

生活类比总结

  • Nginx = 智能引导员,分发客流
  • Tomcat = 专柜服务员,提供专业服务
  • 负载均衡 = 根据客流量分配柜台
  • 缓存 = 把常用商品放在前台

最后提醒:Nginx 和 Tomcat 各有优势,合理配置才能发挥最大效能!

📋 配置项说明(含义与用途,以官网为准)

以下列出文档中出现的 Nginx、Tomcat 配置项,并给出含义典型用途 ,便于查阅与调优。Nginx 以 ngx_http_proxy_modulengx_http_upstream_modulengx_http_core_module 为准;Tomcat 以 HTTP Connector 为准。

Nginx 主进程与 events
配置项 含义(官方) 用途 / 建议
user 运行 worker 进程的用户(及可选的组) 不用 root 跑 worker,安全;如 nginx
worker_processes worker 进程数量 auto 一般等于 CPU 核数,充分利用多核
worker_rlimit_nofile 每个进程可打开的文件描述符上限 高并发时需足够大(如 65535),否则 "too many open files"
worker_connections 每个 worker 可同时处理的最大连接数 总并发约等于 worker_processes × worker_connections;按预期 QPS 与长连接估算
use 指定连接复用方法(如 epoll、kqueue) Linux 下 epoll,高并发必备
multi_accept 是否一次 accept 多个新连接 on 可减少唤醒次数,建议开启

示例(主进程 + events 写在一起)

nginx 复制代码
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 10240;
    use epoll;
    multi_accept on;
}
Nginx http 核心(常用)
配置项 含义(官方) 用途 / 建议
sendfile 是否使用内核 sendfile 直接在内核态把文件写到 socket on 时静态文件少一次用户态拷贝,性能更好
tcp_nopush 与 sendfile 配合,在包填满或最后一块时再发 减少小包数量,适合大文件;与 tcp_nodelay 互斥语义(针对不同场景)
tcp_nodelay 是否禁用 Nagle(小包立即发) 动态、小响应时 on,降低延迟
keepalive_timeout 与客户端的 keep-alive 超时时间(秒) 长连接场景可适当加大(如 65)
types_hash_max_size 影响 MIME 类型哈希表大小 虚拟主机很多时可增大,避免 hash bucket size 告警
client_max_body_size 允许的客户端请求体最大大小(k/m/g) 上传场景必须设(如 50m),默认 1m 易触发 413
server_tokens 是否在响应头中输出 Nginx 版本 off 隐藏版本,减少信息暴露
listen 监听地址与端口 80443 ssl[::]:80
server_name 虚拟主机名,用于匹配 Host 头 域名或 _ 默认
location 按 URI 匹配并应用一组指令 location /、`location ~* .(jpg
root / alias 请求对应到文件系统的路径 root 把 location 拼到路径后;alias 替换 location 部分
expires 响应头 Expires / Cache-Control 的缓存时间 30d1y,静态资源加速
add_header 给响应添加自定义头 Cache-ControlX-Frame-Options
access_log 访问日志路径及可选 format access_log /var/log/nginx/access.log main;off 关闭
log_format 定义日志格式名称与格式串 可包含 $upstream_addr$request_time$upstream_response_time 等便于排错
stub_status 是否在该 location 输出简单状态页 需编译 --with-http_stub_status_module;用于监控 active/reading/writing/waiting 连接数
allow / deny 按 IP/网段允许或拒绝访问 allow 192.168.1.0/24; deny all; 限制管理或状态页访问

示例(一个 server:静态站 + 状态页只允许内网)

nginx 复制代码
server {
    listen 80;
    server_name www.example.com;

    # 静态资源:路径用 root,缓存 30 天
    location /static/ {
        root /var/www;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # 状态页只允许内网访问
    location /nginx_status {
        stub_status on;
        access_log off;
        allow 192.168.1.0/24;
        deny all;
    }

    access_log /var/log/nginx/access.log;
}
Nginx Gzip
配置项 含义(官方) 用途 / 建议
gzip 是否启用 gzip 压缩 on 减小文本类响应体积,节省带宽
gzip_vary 是否在响应中加 Vary: Accept-Encoding on 便于正确缓存压缩/未压缩版本
gzip_min_length 仅当响应长度不小于该值(字节)时才压缩 太小的响应压缩后可能更大,一般 1024 或 2048
gzip_types 对哪些 MIME 类型做 gzip 默认仅 text/html;可加 text/css、application/json、application/javascript 等

示例(在 http 里写,对所有 server 生效)

nginx 复制代码
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
Nginx upstream(官方 ngx_http_upstream_module)
配置项 含义(官方) 用途 / 建议
upstream name 定义一组后端服务器,供 proxy_pass 等引用 集中管理后端列表与负载策略
server address [parameters] 组内一台服务器的地址及参数 ip:port;可带 weight、backup、max_fails、fail_timeout
weight 该服务器的权重,默认 1 轮询时按权重分配,机器性能不一时可设不同值
backup 标记为备份服务器 仅当主服务器都不可用时才使用
max_fails 在 fail_timeout 内连续失败多少次则视为不可用 默认 1;一般 2~3,避免网络抖动误判
fail_timeout 判定失败的时间窗口,以及视为不可用的持续时间(秒) 该时间内连续 max_fails 次失败则摘除,过段时间再试
keepalive 与上游保持的空闲长连接数(每 worker) 需配合 proxy_http_version 1.1 与 proxy_set_header Connection "";减少建连开销
ip_hash 按客户端 IP 哈希选服务器 会话保持;同一 IP 尽量到同一后端
least_conn 选当前活跃连接数最少的服务器 适合请求耗时差异大的场景

示例(三台 Tomcat:两台主、一台备份;主节点 2 次失败就摘 30 秒)

nginx 复制代码
upstream tomcat_cluster {
    server 192.168.1.20:8080 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.1.21:8080 weight=1 max_fails=2 fail_timeout=30s;
    server 192.168.1.22:8080 backup;

    keepalive 32;
}

若要同一用户固定到同一台(会话保持),可改为:

nginx 复制代码
upstream tomcat_cluster {
    ip_hash;
    server 192.168.1.20:8080;
    server 192.168.1.21:8080;
}
Nginx proxy(官方 ngx_http_proxy_module)
配置项 含义(官方) 用途 / 建议
proxy_pass 将请求转发到指定 URI 或 upstream http://upstream_namehttp://ip:port/path/;尾有无斜杠影响 URI 是否被替换
proxy_set_header 设置转发给上游的请求头 常用 Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto,后端需真实客户端信息时必设
proxy_connect_timeout 与上游建立连接的超时时间 不宜过长,建议 5~60s
proxy_send_timeout 向上游发送请求的超时时间 一般与 read 同量级或略小
proxy_read_timeout 从上游读取响应的超时时间 动态接口耗时长可设大(如 120s),静态可小
proxy_http_version 与上游通信使用的 HTTP 版本 使用 upstream keepalive 时须为 1.1(或 2)
proxy_set_header Connection "" 清空 Connection 头 与 proxy_http_version 1.1、upstream keepalive 配合
proxy_buffering 是否缓冲上游响应 on 时先收完再发给客户端,减轻上游占用;流式可 off
proxy_buffer_size 存放上游响应的缓冲区大小 一般 4k~8k 即可
proxy_buffers 存放上游响应的缓冲区数量与大小 8 4k8 8k;大响应或高并发可适当加大
proxy_busy_buffers_size 正在向客户端发送时,可占用的缓冲总大小 默认约两倍 proxy_buffer_size;大响应时可增大
proxy_max_temp_file_size 缓冲不够时写临时文件的最大大小 大响应时避免占满内存,可设 256m 等
proxy_request_buffering 是否先缓冲客户端请求体再转给上游 on 时上游更稳定;大上传有时关掉以流式转发
proxy_hide_header 不把上游某响应头传给客户端 如隐藏 X-Powered-By
proxy_redirect 重写上游返回的 Location、Refresh 头中的 URL 上游是内网地址时,可改为对外域名,避免客户端直接请求后端

示例(把 / 转发到 Tomcat,带真实 IP、超时、缓冲;并隐藏后端版本头)

nginx 复制代码
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;

    proxy_http_version 1.1;
    proxy_set_header Connection "";

    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 60s;

    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 8k;
    proxy_busy_buffers_size 16k;

    proxy_hide_header X-Powered-By;
}

proxy_pass 带不带 URI 的区别(容易搞混,看例子):

nginx 复制代码
# 请求 /api/user → 转给后端的是 /api/user(完整 URI)
location /api/ {
    proxy_pass http://backend;
}

# 请求 /api/user → 转给后端的是 /user(把 /api 换成了空)
location /api/ {
    proxy_pass http://backend/;
}
Nginx 限流(limit_req / limit_conn)
配置项 含义(官方) 用途 / 建议
limit_req_zone 定义限流用的共享内存区与速率(如按 IP) $binary_remote_addr zone=one:10m rate=10r/s
limit_req 在 location 中应用该 zone,可选 burst、nodelay burst 为突发排队数;nodelay 表示立即按 rate 处理排队
limit_conn_zone 定义按 key(如 IP)计连接数的 zone 与 limit_conn 配合
limit_conn 同一 key 允许的最大并发连接数 防单 IP 占满连接

示例(按 IP 限速:每秒 10 请求、突发 20;每 IP 最多 20 个并发连接)

nginx 复制代码
# 在 http 里定义 zone(按 IP,10m 内存,每秒 10 个请求)
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    location / {
        limit_req zone=one burst=20 nodelay;
        limit_conn addr 20;

        proxy_pass http://tomcat_cluster;
    }
}
Tomcat HTTP Connector 常见属性(官方 HTTP Connector
属性 含义(官方) 用途 / 建议
port Connector 监听的 TCP 端口 如 8080(HTTP)、8443(HTTPS)
protocol 协议实现类 HTTP/1.1(默认,自动选 NIO/NIO2/APR)、AJP/1.3 等
connectionTimeout 接受连接后,等待请求 URI 行出现的时间(毫秒) 默认 20000;过小易在慢网络下断连
maxThreads 处理请求的最大线程数 同时处理的请求数上限;按压测与 CPU 调,如 500
minSpareThreads 始终保持的最小空闲线程数 避免突发时临时创建过多线程,如 50
maxSpareThreads 允许的最大空闲线程数 超过则回收;与 minSpareThreads 一起控制线程池形状
acceptCount 当达到 maxConnections 时,操作系统等待队列的最大长度 队列满后新连接可能被拒绝或超时;如 500
acceptorThreadCount 接受连接的线程数 高并发时可适当增加(如 2~4),默认 1
enableLookups 是否对 getRemoteHost() 做 DNS 反向解析 生产建议 false,避免 DNS 延迟
compression 是否对响应做 GZIP 压缩 on 节省带宽;也可设为最小字节数(如 2048)
compressionMinSize 响应体至少多少字节才压缩(字节) 太小压缩收益低甚至变大;默认 2048
compressableMimeType 对哪些 MIME 类型压缩 如 text/html,text/xml,text/plain,text/css,application/json
noCompressionUserAgents 不压缩的 User-Agent 正则 某些旧客户端实现有问题,可排除,如 gozilla-trident
URIEncoding 解码 URI 使用的字符集 与前端一致,常用 UTF-8
address 绑定监听的 IP 多网卡时可指定,如仅内网 192.168.1.20
redirectPort 需 SSL 时重定向到的端口 如 8443
maxConnections 服务器接受并处理的最大连接数 超过后只接受不处理,等空闲;默认 8192

示例(server.xml 里一段 HTTP Connector,含线程池、超时、压缩)

xml 复制代码
<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           maxThreads="500"
           minSpareThreads="50"
           maxSpareThreads="200"
           acceptCount="500"
           acceptorThreadCount="2"
           enableLookups="false"
           compression="on"
           compressionMinSize="2048"
           compressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json"
           noCompressionUserAgents="gozilla-trident"
           URIEncoding="UTF-8"
           redirectPort="8443" />
  • compressionMinSize="2048":响应体小于 2KB 不压缩(避免越压越大)。
  • noCompressionUserAgents:遇到这些 UA 不压缩,避免老浏览器解压异常。
Tomcat AJP Connector 常见属性(AJP Connector
属性 含义(官方) 用途 / 建议
port AJP 监听端口 默认 8009
protocol AJP/1.3 与 Apache/Nginx AJP 模块通信
secret 与 Web 服务器约定的密钥(可选但推荐) 防止未授权访问 AJP 端口
secretRequired 是否强制要求 secret 生产建议 true
address 绑定 IP 仅内网时可指定
redirectPort 需 SSL 时重定向端口 如 8443

示例(仅内网监听、带密钥,防止未授权连 AJP)

xml 复制代码
<Connector port="8009"
           protocol="AJP/1.3"
           secret="your-secret-string"
           secretRequired="true"
           address="192.168.1.20"
           redirectPort="8443" />
  • secret:和 Nginx/Apache 里配置的 AJP 密钥一致,否则连不上。
  • address="192.168.1.20":只在本机该 IP 上监听,不暴露到公网。

以上配置项均可在官方文档中查到完整语法、默认值与上下文。文档中若还有未列出的指令,以 nginx.orgtomcat.apache.org 为准。上面的示例可直接复制到对应文件里,按自己 IP、端口、域名改一改即可用。

📚 官方文档与参考

资源 链接 用途
Nginx 反向代理 Reverse Proxy proxy_pass、proxy_set_header、超时等
Nginx proxy 模块 ngx_http_proxy_module 官方指令列表与参数
Nginx upstream 模块 ngx_http_upstream_module upstream、server、负载算法、backup
Nginx 负载均衡 HTTP Load Balancing 负载均衡配置与健康检查
Tomcat 9 配置 Apache Tomcat 9 Configuration server.xml、Connector、Host、Context
Tomcat AJP Connector The AJP Connector AJP 端口、secret、address 等
相关推荐
匀泪4 小时前
云原生(nginx实验(4))
运维·nginx·云原生
FJW02081415 小时前
《Nginx 进阶实战:配置详解、站点发布与常用功能大全》(2)
运维·nginx
问道飞鱼17 小时前
【服务器知识】nginx配置负载均衡完全解读
服务器·nginx·负载均衡
一叶星殇1 天前
Windows 下用 Nginx 部署 Vue + .NET WebApi 全流程实战
vue.js·windows·nginx
匀泪1 天前
云原生(nginx实验(3))
运维·nginx·云原生
hzc09876543212 天前
Linux系统下安装配置 Nginx 超详细图文教程_linux安装nginx
linux·服务器·nginx
匀泪2 天前
云原生(nginx实验(2))
运维·nginx·云原生
FJW0208142 天前
《Nginx 高级应用:变量、Rewrite、反向代理与 OpenResty 扩展》(3)
运维·nginx·openresty
t***44232 天前
CORS:跨域访问、如何在Nginx中配置允许跨域访问
运维·nginx