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 等
相关推荐
图图玩ai13 小时前
SSH 命令管理工具怎么选?从命令收藏到批量执行一次讲清
linux·nginx·docker·ai·程序员·ssh·可视化·gmssh·批量命令执行
CXH72814 小时前
nginx——https
运维·nginx·https
博风15 小时前
在tomcat应用里添加了一个线程池对象,向这个线程池发送任务,让其执行。 我希望在tomcat停机时,能等待线程池里的任务执行完了再停机,要如何实现?
java·tomcat
Lentou15 小时前
nginx反向代理
运维·nginx
遇见火星16 小时前
linux设置开启启动服务
linux·运维·服务器·nginx
咸鱼翻身小阿橙17 小时前
QT P4
数据库·qt·nginx
o丁二黄o18 小时前
若依部署Nginx和Tomcat
运维·nginx·tomcat
一个public的class19 小时前
前后端 + Nginx + Gateway + K8s 全链路架构图解
前端·后端·nginx·kubernetes·gateway
bukeyiwanshui20 小时前
20260420 Nginx 服务器
运维·服务器·nginx
unDl IONA1 天前
服务器部署,用 nginx 部署后页面刷新 404 问题,宝塔面板修改(修改 nginx.conf 配置文件)
运维·服务器·nginx