欢迎光临
我们一直在努力

Nginx + Tomcat 反向代理完全指南

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


📑 目录

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

  • 🎯 什么是反向代理?

    正向代理 vs 反向代理

    #mermaid-svg-HXrpPBGHBBLkXbx8{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HXrpPBGHBBLkXbx8 .error-icon{fill:#552222;}#mermaid-svg-HXrpPBGHBBLkXbx8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HXrpPBGHBBLkXbx8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .marker.cross{stroke:#333333;}#mermaid-svg-HXrpPBGHBBLkXbx8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HXrpPBGHBBLkXbx8 p{margin:0;}#mermaid-svg-HXrpPBGHBBLkXbx8 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .cluster-label text{fill:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .cluster-label span{color:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .cluster-label span p{background-color:transparent;}#mermaid-svg-HXrpPBGHBBLkXbx8 .label text,#mermaid-svg-HXrpPBGHBBLkXbx8 span{fill:#333;color:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .node rect,#mermaid-svg-HXrpPBGHBBLkXbx8 .node circle,#mermaid-svg-HXrpPBGHBBLkXbx8 .node ellipse,#mermaid-svg-HXrpPBGHBBLkXbx8 .node polygon,#mermaid-svg-HXrpPBGHBBLkXbx8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .rough-node .label text,#mermaid-svg-HXrpPBGHBBLkXbx8 .node .label text,#mermaid-svg-HXrpPBGHBBLkXbx8 .image-shape .label,#mermaid-svg-HXrpPBGHBBLkXbx8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-HXrpPBGHBBLkXbx8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .rough-node .label,#mermaid-svg-HXrpPBGHBBLkXbx8 .node .label,#mermaid-svg-HXrpPBGHBBLkXbx8 .image-shape .label,#mermaid-svg-HXrpPBGHBBLkXbx8 .icon-shape .label{text-align:center;}#mermaid-svg-HXrpPBGHBBLkXbx8 .node.clickable{cursor:pointer;}#mermaid-svg-HXrpPBGHBBLkXbx8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .arrowheadPath{fill:#333333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HXrpPBGHBBLkXbx8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HXrpPBGHBBLkXbx8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HXrpPBGHBBLkXbx8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HXrpPBGHBBLkXbx8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .cluster text{fill:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 .cluster span{color:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HXrpPBGHBBLkXbx8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HXrpPBGHBBLkXbx8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-HXrpPBGHBBLkXbx8 .icon-shape,#mermaid-svg-HXrpPBGHBBLkXbx8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HXrpPBGHBBLkXbx8 .icon-shape p,#mermaid-svg-HXrpPBGHBBLkXbx8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HXrpPBGHBBLkXbx8 .icon-shape rect,#mermaid-svg-HXrpPBGHBBLkXbx8 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HXrpPBGHBBLkXbx8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HXrpPBGHBBLkXbx8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HXrpPBGHBBLkXbx8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    🔒 反向代理

    请求目标服务器

    转发

    转发

    👤 客户端

    🖥️ 反向代理

    📦 后端服务器1

    📦 后端服务器2

    🔓 正向代理

    请求

    转发

    👤 客户端

    🖥️ 代理服务器

    🌐 目标服务器

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

    反向代理的优势:

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

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

    以下对文档中出现的指令、概念、术语做简要解释,并配上生活例子与「为什么」,便于记忆与理解。Nginx 以 ngx_http_proxy_module、ngx_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

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

    对比项AB区别一句话何时用 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 对比

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

    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 源)

    # 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 源)

    # 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

    源码编译(简要)

    # 下载并解压(以 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_status、realip 等模块,或需要第三方模块;编译时可 –add-module=… 加入并统一 –prefix 便于运维。

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

    # 常用命令汇总
    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 # 仅测试配置文件语法,不启动

    验证安装与运行

    # 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 nginx 或 sudo 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 测试(仅排错,不推荐长期关) 为什么先查 SELinux?RHEL/CentOS 等默认开启,报 13 时若已是 root 启动,多半是 SELinux 拦截
    Address already in use / 80 端口被占用 本机已有程序监听 80(如 Apache、其他 Nginx) ss -tlnp | grep :80 或 lsof -i :80 看占用进程;关掉冲突服务或改 Nginx 的 listen 端口 为什么会冲突?一机多 Web 服务时只一个能占 80,需改端口或停掉其一
    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 等)

    # 查看 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 nginx、Linux packages。

    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 负载均衡算法

    #mermaid-svg-7ub5Qu05E8oolCmI{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7ub5Qu05E8oolCmI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7ub5Qu05E8oolCmI .error-icon{fill:#552222;}#mermaid-svg-7ub5Qu05E8oolCmI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7ub5Qu05E8oolCmI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7ub5Qu05E8oolCmI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7ub5Qu05E8oolCmI .marker.cross{stroke:#333333;}#mermaid-svg-7ub5Qu05E8oolCmI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7ub5Qu05E8oolCmI p{margin:0;}#mermaid-svg-7ub5Qu05E8oolCmI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-7ub5Qu05E8oolCmI .cluster-label text{fill:#333;}#mermaid-svg-7ub5Qu05E8oolCmI .cluster-label span{color:#333;}#mermaid-svg-7ub5Qu05E8oolCmI .cluster-label span p{background-color:transparent;}#mermaid-svg-7ub5Qu05E8oolCmI .label text,#mermaid-svg-7ub5Qu05E8oolCmI span{fill:#333;color:#333;}#mermaid-svg-7ub5Qu05E8oolCmI .node rect,#mermaid-svg-7ub5Qu05E8oolCmI .node circle,#mermaid-svg-7ub5Qu05E8oolCmI .node ellipse,#mermaid-svg-7ub5Qu05E8oolCmI .node polygon,#mermaid-svg-7ub5Qu05E8oolCmI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7ub5Qu05E8oolCmI .rough-node .label text,#mermaid-svg-7ub5Qu05E8oolCmI .node .label text,#mermaid-svg-7ub5Qu05E8oolCmI .image-shape .label,#mermaid-svg-7ub5Qu05E8oolCmI .icon-shape .label{text-anchor:middle;}#mermaid-svg-7ub5Qu05E8oolCmI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7ub5Qu05E8oolCmI .rough-node .label,#mermaid-svg-7ub5Qu05E8oolCmI .node .label,#mermaid-svg-7ub5Qu05E8oolCmI .image-shape .label,#mermaid-svg-7ub5Qu05E8oolCmI .icon-shape .label{text-align:center;}#mermaid-svg-7ub5Qu05E8oolCmI .node.clickable{cursor:pointer;}#mermaid-svg-7ub5Qu05E8oolCmI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7ub5Qu05E8oolCmI .arrowheadPath{fill:#333333;}#mermaid-svg-7ub5Qu05E8oolCmI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7ub5Qu05E8oolCmI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7ub5Qu05E8oolCmI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7ub5Qu05E8oolCmI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7ub5Qu05E8oolCmI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7ub5Qu05E8oolCmI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7ub5Qu05E8oolCmI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7ub5Qu05E8oolCmI .cluster text{fill:#333;}#mermaid-svg-7ub5Qu05E8oolCmI .cluster span{color:#333;}#mermaid-svg-7ub5Qu05E8oolCmI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7ub5Qu05E8oolCmI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7ub5Qu05E8oolCmI rect.text{fill:none;stroke-width:0;}#mermaid-svg-7ub5Qu05E8oolCmI .icon-shape,#mermaid-svg-7ub5Qu05E8oolCmI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7ub5Qu05E8oolCmI .icon-shape p,#mermaid-svg-7ub5Qu05E8oolCmI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7ub5Qu05E8oolCmI .icon-shape rect,#mermaid-svg-7ub5Qu05E8oolCmI .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7ub5Qu05E8oolCmI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7ub5Qu05E8oolCmI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7ub5Qu05E8oolCmI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    ⚖️ 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 EAP、VMware Tanzu、Oracle WebLogic 等基于或兼容 Tomcat 技术的商业产品 各厂商(Red Hat、VMware、Oracle 等)提供付费支持,并非「Tomcat 公司」

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

    #mermaid-svg-OOPVseptKDR0kZNL{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OOPVseptKDR0kZNL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OOPVseptKDR0kZNL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OOPVseptKDR0kZNL .error-icon{fill:#552222;}#mermaid-svg-OOPVseptKDR0kZNL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OOPVseptKDR0kZNL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OOPVseptKDR0kZNL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OOPVseptKDR0kZNL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OOPVseptKDR0kZNL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OOPVseptKDR0kZNL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OOPVseptKDR0kZNL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OOPVseptKDR0kZNL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OOPVseptKDR0kZNL .marker.cross{stroke:#333333;}#mermaid-svg-OOPVseptKDR0kZNL svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OOPVseptKDR0kZNL p{margin:0;}#mermaid-svg-OOPVseptKDR0kZNL .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-OOPVseptKDR0kZNL .cluster-label text{fill:#333;}#mermaid-svg-OOPVseptKDR0kZNL .cluster-label span{color:#333;}#mermaid-svg-OOPVseptKDR0kZNL .cluster-label span p{background-color:transparent;}#mermaid-svg-OOPVseptKDR0kZNL .label text,#mermaid-svg-OOPVseptKDR0kZNL span{fill:#333;color:#333;}#mermaid-svg-OOPVseptKDR0kZNL .node rect,#mermaid-svg-OOPVseptKDR0kZNL .node circle,#mermaid-svg-OOPVseptKDR0kZNL .node ellipse,#mermaid-svg-OOPVseptKDR0kZNL .node polygon,#mermaid-svg-OOPVseptKDR0kZNL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OOPVseptKDR0kZNL .rough-node .label text,#mermaid-svg-OOPVseptKDR0kZNL .node .label text,#mermaid-svg-OOPVseptKDR0kZNL .image-shape .label,#mermaid-svg-OOPVseptKDR0kZNL .icon-shape .label{text-anchor:middle;}#mermaid-svg-OOPVseptKDR0kZNL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OOPVseptKDR0kZNL .rough-node .label,#mermaid-svg-OOPVseptKDR0kZNL .node .label,#mermaid-svg-OOPVseptKDR0kZNL .image-shape .label,#mermaid-svg-OOPVseptKDR0kZNL .icon-shape .label{text-align:center;}#mermaid-svg-OOPVseptKDR0kZNL .node.clickable{cursor:pointer;}#mermaid-svg-OOPVseptKDR0kZNL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OOPVseptKDR0kZNL .arrowheadPath{fill:#333333;}#mermaid-svg-OOPVseptKDR0kZNL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OOPVseptKDR0kZNL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OOPVseptKDR0kZNL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OOPVseptKDR0kZNL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OOPVseptKDR0kZNL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OOPVseptKDR0kZNL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OOPVseptKDR0kZNL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OOPVseptKDR0kZNL .cluster text{fill:#333;}#mermaid-svg-OOPVseptKDR0kZNL .cluster span{color:#333;}#mermaid-svg-OOPVseptKDR0kZNL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OOPVseptKDR0kZNL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OOPVseptKDR0kZNL rect.text{fill:none;stroke-width:0;}#mermaid-svg-OOPVseptKDR0kZNL .icon-shape,#mermaid-svg-OOPVseptKDR0kZNL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OOPVseptKDR0kZNL .icon-shape p,#mermaid-svg-OOPVseptKDR0kZNL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OOPVseptKDR0kZNL .icon-shape rect,#mermaid-svg-OOPVseptKDR0kZNL .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OOPVseptKDR0kZNL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OOPVseptKDR0kZNL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OOPVseptKDR0kZNL :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    🐘 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 应用服务器,用来跑 Servlet 和 JSP;前端若用 Vue 做前后端分离,通常 Vue 打包成静态资源由 Nginx 或 Tomcat 提供,接口由 Tomcat 提供。本节简要说明四者与 Tomcat 的配置和使用方式。

    整体关系一览

    #mermaid-svg-Un60Nw4J17oq2AAX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Un60Nw4J17oq2AAX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Un60Nw4J17oq2AAX .error-icon{fill:#552222;}#mermaid-svg-Un60Nw4J17oq2AAX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Un60Nw4J17oq2AAX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Un60Nw4J17oq2AAX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Un60Nw4J17oq2AAX .marker.cross{stroke:#333333;}#mermaid-svg-Un60Nw4J17oq2AAX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Un60Nw4J17oq2AAX p{margin:0;}#mermaid-svg-Un60Nw4J17oq2AAX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Un60Nw4J17oq2AAX .cluster-label text{fill:#333;}#mermaid-svg-Un60Nw4J17oq2AAX .cluster-label span{color:#333;}#mermaid-svg-Un60Nw4J17oq2AAX .cluster-label span p{background-color:transparent;}#mermaid-svg-Un60Nw4J17oq2AAX .label text,#mermaid-svg-Un60Nw4J17oq2AAX span{fill:#333;color:#333;}#mermaid-svg-Un60Nw4J17oq2AAX .node rect,#mermaid-svg-Un60Nw4J17oq2AAX .node circle,#mermaid-svg-Un60Nw4J17oq2AAX .node ellipse,#mermaid-svg-Un60Nw4J17oq2AAX .node polygon,#mermaid-svg-Un60Nw4J17oq2AAX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Un60Nw4J17oq2AAX .rough-node .label text,#mermaid-svg-Un60Nw4J17oq2AAX .node .label text,#mermaid-svg-Un60Nw4J17oq2AAX .image-shape .label,#mermaid-svg-Un60Nw4J17oq2AAX .icon-shape .label{text-anchor:middle;}#mermaid-svg-Un60Nw4J17oq2AAX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Un60Nw4J17oq2AAX .rough-node .label,#mermaid-svg-Un60Nw4J17oq2AAX .node .label,#mermaid-svg-Un60Nw4J17oq2AAX .image-shape .label,#mermaid-svg-Un60Nw4J17oq2AAX .icon-shape .label{text-align:center;}#mermaid-svg-Un60Nw4J17oq2AAX .node.clickable{cursor:pointer;}#mermaid-svg-Un60Nw4J17oq2AAX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Un60Nw4J17oq2AAX .arrowheadPath{fill:#333333;}#mermaid-svg-Un60Nw4J17oq2AAX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Un60Nw4J17oq2AAX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Un60Nw4J17oq2AAX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Un60Nw4J17oq2AAX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Un60Nw4J17oq2AAX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Un60Nw4J17oq2AAX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Un60Nw4J17oq2AAX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Un60Nw4J17oq2AAX .cluster text{fill:#333;}#mermaid-svg-Un60Nw4J17oq2AAX .cluster span{color:#333;}#mermaid-svg-Un60Nw4J17oq2AAX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Un60Nw4J17oq2AAX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Un60Nw4J17oq2AAX rect.text{fill:none;stroke-width:0;}#mermaid-svg-Un60Nw4J17oq2AAX .icon-shape,#mermaid-svg-Un60Nw4J17oq2AAX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Un60Nw4J17oq2AAX .icon-shape p,#mermaid-svg-Un60Nw4J17oq2AAX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Un60Nw4J17oq2AAX .icon-shape rect,#mermaid-svg-Un60Nw4J17oq2AAX .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Un60Nw4J17oq2AAX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Un60Nw4J17oq2AAX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Un60Nw4J17oq2AAX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    🐘 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_HOME 和 JVM 启动参数。

    1. 设置 JAVA_HOME

    # 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 本身。

    # 创建 $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 等。

    # 仅 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 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)

    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 指向磁盘上的目录(如独立部署目录)。

    <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.jsp、myapp/page/foo.jsp。

    2. 如何访问

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

    3. 简单示例

    <%@ 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.html 和 assets/)。
    • 把 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)示例:

    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。

    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 实战

    环境拓扑

    #mermaid-svg-ZcVP1tmGzYal0i1h{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZcVP1tmGzYal0i1h .error-icon{fill:#552222;}#mermaid-svg-ZcVP1tmGzYal0i1h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZcVP1tmGzYal0i1h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZcVP1tmGzYal0i1h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZcVP1tmGzYal0i1h .marker.cross{stroke:#333333;}#mermaid-svg-ZcVP1tmGzYal0i1h svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZcVP1tmGzYal0i1h p{margin:0;}#mermaid-svg-ZcVP1tmGzYal0i1h .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h .cluster-label text{fill:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h .cluster-label span{color:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h .cluster-label span p{background-color:transparent;}#mermaid-svg-ZcVP1tmGzYal0i1h .label text,#mermaid-svg-ZcVP1tmGzYal0i1h span{fill:#333;color:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h .node rect,#mermaid-svg-ZcVP1tmGzYal0i1h .node circle,#mermaid-svg-ZcVP1tmGzYal0i1h .node ellipse,#mermaid-svg-ZcVP1tmGzYal0i1h .node polygon,#mermaid-svg-ZcVP1tmGzYal0i1h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZcVP1tmGzYal0i1h .rough-node .label text,#mermaid-svg-ZcVP1tmGzYal0i1h .node .label text,#mermaid-svg-ZcVP1tmGzYal0i1h .image-shape .label,#mermaid-svg-ZcVP1tmGzYal0i1h .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZcVP1tmGzYal0i1h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZcVP1tmGzYal0i1h .rough-node .label,#mermaid-svg-ZcVP1tmGzYal0i1h .node .label,#mermaid-svg-ZcVP1tmGzYal0i1h .image-shape .label,#mermaid-svg-ZcVP1tmGzYal0i1h .icon-shape .label{text-align:center;}#mermaid-svg-ZcVP1tmGzYal0i1h .node.clickable{cursor:pointer;}#mermaid-svg-ZcVP1tmGzYal0i1h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZcVP1tmGzYal0i1h .arrowheadPath{fill:#333333;}#mermaid-svg-ZcVP1tmGzYal0i1h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZcVP1tmGzYal0i1h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZcVP1tmGzYal0i1h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZcVP1tmGzYal0i1h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZcVP1tmGzYal0i1h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZcVP1tmGzYal0i1h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZcVP1tmGzYal0i1h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZcVP1tmGzYal0i1h .cluster text{fill:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h .cluster span{color:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZcVP1tmGzYal0i1h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZcVP1tmGzYal0i1h rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZcVP1tmGzYal0i1h .icon-shape,#mermaid-svg-ZcVP1tmGzYal0i1h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZcVP1tmGzYal0i1h .icon-shape p,#mermaid-svg-ZcVP1tmGzYal0i1h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZcVP1tmGzYal0i1h .icon-shape rect,#mermaid-svg-ZcVP1tmGzYal0i1h .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZcVP1tmGzYal0i1h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZcVP1tmGzYal0i1h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZcVP1tmGzYal0i1h :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    🐘 Tomcat 层

    🖥️ Nginx 层

    🌐 Nginx192.168.1.10:80反向代理

    📦 Tomcat1192.168.1.20:8080

    📦 Tomcat2192.168.1.21:8080

    📦 Tomcat3192.168.1.22:8080

    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 配置

    <!– /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>

    会话保持

    #mermaid-svg-6zH3vwCbg5ujf6xK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6zH3vwCbg5ujf6xK .error-icon{fill:#552222;}#mermaid-svg-6zH3vwCbg5ujf6xK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6zH3vwCbg5ujf6xK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6zH3vwCbg5ujf6xK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6zH3vwCbg5ujf6xK .marker.cross{stroke:#333333;}#mermaid-svg-6zH3vwCbg5ujf6xK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6zH3vwCbg5ujf6xK p{margin:0;}#mermaid-svg-6zH3vwCbg5ujf6xK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK .cluster-label text{fill:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK .cluster-label span{color:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK .cluster-label span p{background-color:transparent;}#mermaid-svg-6zH3vwCbg5ujf6xK .label text,#mermaid-svg-6zH3vwCbg5ujf6xK span{fill:#333;color:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK .node rect,#mermaid-svg-6zH3vwCbg5ujf6xK .node circle,#mermaid-svg-6zH3vwCbg5ujf6xK .node ellipse,#mermaid-svg-6zH3vwCbg5ujf6xK .node polygon,#mermaid-svg-6zH3vwCbg5ujf6xK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6zH3vwCbg5ujf6xK .rough-node .label text,#mermaid-svg-6zH3vwCbg5ujf6xK .node .label text,#mermaid-svg-6zH3vwCbg5ujf6xK .image-shape .label,#mermaid-svg-6zH3vwCbg5ujf6xK .icon-shape .label{text-anchor:middle;}#mermaid-svg-6zH3vwCbg5ujf6xK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6zH3vwCbg5ujf6xK .rough-node .label,#mermaid-svg-6zH3vwCbg5ujf6xK .node .label,#mermaid-svg-6zH3vwCbg5ujf6xK .image-shape .label,#mermaid-svg-6zH3vwCbg5ujf6xK .icon-shape .label{text-align:center;}#mermaid-svg-6zH3vwCbg5ujf6xK .node.clickable{cursor:pointer;}#mermaid-svg-6zH3vwCbg5ujf6xK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6zH3vwCbg5ujf6xK .arrowheadPath{fill:#333333;}#mermaid-svg-6zH3vwCbg5ujf6xK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6zH3vwCbg5ujf6xK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6zH3vwCbg5ujf6xK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6zH3vwCbg5ujf6xK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6zH3vwCbg5ujf6xK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6zH3vwCbg5ujf6xK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6zH3vwCbg5ujf6xK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6zH3vwCbg5ujf6xK .cluster text{fill:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK .cluster span{color:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6zH3vwCbg5ujf6xK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6zH3vwCbg5ujf6xK rect.text{fill:none;stroke-width:0;}#mermaid-svg-6zH3vwCbg5ujf6xK .icon-shape,#mermaid-svg-6zH3vwCbg5ujf6xK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6zH3vwCbg5ujf6xK .icon-shape p,#mermaid-svg-6zH3vwCbg5ujf6xK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6zH3vwCbg5ujf6xK .icon-shape rect,#mermaid-svg-6zH3vwCbg5ujf6xK .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6zH3vwCbg5ujf6xK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6zH3vwCbg5ujf6xK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6zH3vwCbg5ujf6xK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    📌 会话保持需求

    🔑 ip_hash

    🍪 sticky_cookie

    🔄 session 复制

    同一 IP 到同一 Tomcat

    基于 Cookie 分配

    Session 集群复制

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

    方法1:ip_hash

    upstream tomcat_cluster {
    ip_hash; # 会话保持
    server 192.168.1.20:8080;
    server 192.168.1.21:8080;
    }

    方法2:sticky_cookie(商业版)

    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 复制

    <!– 在 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 复制配置。

    集群架构示意

    #mermaid-svg-JNK81zK5g7BjJNZ1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JNK81zK5g7BjJNZ1 .error-icon{fill:#552222;}#mermaid-svg-JNK81zK5g7BjJNZ1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JNK81zK5g7BjJNZ1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .marker.cross{stroke:#333333;}#mermaid-svg-JNK81zK5g7BjJNZ1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JNK81zK5g7BjJNZ1 p{margin:0;}#mermaid-svg-JNK81zK5g7BjJNZ1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .cluster-label text{fill:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .cluster-label span{color:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .cluster-label span p{background-color:transparent;}#mermaid-svg-JNK81zK5g7BjJNZ1 .label text,#mermaid-svg-JNK81zK5g7BjJNZ1 span{fill:#333;color:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .node rect,#mermaid-svg-JNK81zK5g7BjJNZ1 .node circle,#mermaid-svg-JNK81zK5g7BjJNZ1 .node ellipse,#mermaid-svg-JNK81zK5g7BjJNZ1 .node polygon,#mermaid-svg-JNK81zK5g7BjJNZ1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .rough-node .label text,#mermaid-svg-JNK81zK5g7BjJNZ1 .node .label text,#mermaid-svg-JNK81zK5g7BjJNZ1 .image-shape .label,#mermaid-svg-JNK81zK5g7BjJNZ1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-JNK81zK5g7BjJNZ1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .rough-node .label,#mermaid-svg-JNK81zK5g7BjJNZ1 .node .label,#mermaid-svg-JNK81zK5g7BjJNZ1 .image-shape .label,#mermaid-svg-JNK81zK5g7BjJNZ1 .icon-shape .label{text-align:center;}#mermaid-svg-JNK81zK5g7BjJNZ1 .node.clickable{cursor:pointer;}#mermaid-svg-JNK81zK5g7BjJNZ1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .arrowheadPath{fill:#333333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JNK81zK5g7BjJNZ1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JNK81zK5g7BjJNZ1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JNK81zK5g7BjJNZ1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JNK81zK5g7BjJNZ1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .cluster text{fill:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 .cluster span{color:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JNK81zK5g7BjJNZ1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JNK81zK5g7BjJNZ1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-JNK81zK5g7BjJNZ1 .icon-shape,#mermaid-svg-JNK81zK5g7BjJNZ1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JNK81zK5g7BjJNZ1 .icon-shape p,#mermaid-svg-JNK81zK5g7BjJNZ1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JNK81zK5g7BjJNZ1 .icon-shape rect,#mermaid-svg-JNK81zK5g7BjJNZ1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JNK81zK5g7BjJNZ1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JNK81zK5g7BjJNZ1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JNK81zK5g7BjJNZ1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    📡 可选:Session 同步

    🐘 Tomcat 集群

    🖥️ 接入层

    Nginx负载均衡

    Tomcat-1jvmRoute=node1

    Tomcat-2jvmRoute=node2

    Tomcat-3jvmRoute=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 上配置:

    <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.xml 的 Engine 内增加 Cluster 配置,使该 Engine 下的应用可参与 Session 复制。下面示例使用多播做成员发现与复制(同一网段、允许多播时可用):

    <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 成员发现。
    • Receiver:port="4000" 为集群通信端口,每台机器端口可相同(不同 IP)或不同(同机多实例);防火墙需放行。
    • ReplicationValve:在响应提交前把 Session 变更复制出去;filter 可过滤不复制的内容。

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

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

    <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 复制 做集群。


    🖼️ 静态资源缓存

    缓存策略

    #mermaid-svg-d1tVJyF1RTMhqmhp{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d1tVJyF1RTMhqmhp .error-icon{fill:#552222;}#mermaid-svg-d1tVJyF1RTMhqmhp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d1tVJyF1RTMhqmhp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d1tVJyF1RTMhqmhp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d1tVJyF1RTMhqmhp .marker.cross{stroke:#333333;}#mermaid-svg-d1tVJyF1RTMhqmhp svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d1tVJyF1RTMhqmhp p{margin:0;}#mermaid-svg-d1tVJyF1RTMhqmhp .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp .cluster-label text{fill:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp .cluster-label span{color:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp .cluster-label span p{background-color:transparent;}#mermaid-svg-d1tVJyF1RTMhqmhp .label text,#mermaid-svg-d1tVJyF1RTMhqmhp span{fill:#333;color:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp .node rect,#mermaid-svg-d1tVJyF1RTMhqmhp .node circle,#mermaid-svg-d1tVJyF1RTMhqmhp .node ellipse,#mermaid-svg-d1tVJyF1RTMhqmhp .node polygon,#mermaid-svg-d1tVJyF1RTMhqmhp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d1tVJyF1RTMhqmhp .rough-node .label text,#mermaid-svg-d1tVJyF1RTMhqmhp .node .label text,#mermaid-svg-d1tVJyF1RTMhqmhp .image-shape .label,#mermaid-svg-d1tVJyF1RTMhqmhp .icon-shape .label{text-anchor:middle;}#mermaid-svg-d1tVJyF1RTMhqmhp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d1tVJyF1RTMhqmhp .rough-node .label,#mermaid-svg-d1tVJyF1RTMhqmhp .node .label,#mermaid-svg-d1tVJyF1RTMhqmhp .image-shape .label,#mermaid-svg-d1tVJyF1RTMhqmhp .icon-shape .label{text-align:center;}#mermaid-svg-d1tVJyF1RTMhqmhp .node.clickable{cursor:pointer;}#mermaid-svg-d1tVJyF1RTMhqmhp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d1tVJyF1RTMhqmhp .arrowheadPath{fill:#333333;}#mermaid-svg-d1tVJyF1RTMhqmhp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d1tVJyF1RTMhqmhp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d1tVJyF1RTMhqmhp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d1tVJyF1RTMhqmhp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d1tVJyF1RTMhqmhp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d1tVJyF1RTMhqmhp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d1tVJyF1RTMhqmhp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d1tVJyF1RTMhqmhp .cluster text{fill:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp .cluster span{color:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-d1tVJyF1RTMhqmhp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d1tVJyF1RTMhqmhp rect.text{fill:none;stroke-width:0;}#mermaid-svg-d1tVJyF1RTMhqmhp .icon-shape,#mermaid-svg-d1tVJyF1RTMhqmhp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d1tVJyF1RTMhqmhp .icon-shape p,#mermaid-svg-d1tVJyF1RTMhqmhp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d1tVJyF1RTMhqmhp .icon-shape rect,#mermaid-svg-d1tVJyF1RTMhqmhp .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d1tVJyF1RTMhqmhp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d1tVJyF1RTMhqmhp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d1tVJyF1RTMhqmhp :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    静态文件

    动态请求

    👤 客户端请求

    资源类型?

    📁 Nginx 直接返回有缓存则返回缓存

    ➡️ 转发到 Tomcat

    减少 Tomcat 负载

    Tomcat 处理

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

    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 优化

    # /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 优化

    <!– /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 统计 80 端口连接数 为什么看连接数?连接数异常高可能是慢请求或攻击,需配合 limit_conn 等
    tail -f …/tomcat_access.log 实时看访问日志 为什么看日志?404、502、慢请求、IP 分布都在这里,排查问题必看
    stub_status on 输出简单状态页(需编译 –with-http_stub_status_module) 为什么有用?可看到 active/reading/writing/waiting 连接数,监控大盘常用

    # 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 监控

    # 1. 查看 Tomcat 状态
    systemctl status tomcat

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

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


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

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

    问题定位思路(概览)

    #mermaid-svg-5Ns83AIgnkJkuDDx{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Ns83AIgnkJkuDDx .error-icon{fill:#552222;}#mermaid-svg-5Ns83AIgnkJkuDDx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Ns83AIgnkJkuDDx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Ns83AIgnkJkuDDx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Ns83AIgnkJkuDDx .marker.cross{stroke:#333333;}#mermaid-svg-5Ns83AIgnkJkuDDx svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Ns83AIgnkJkuDDx p{margin:0;}#mermaid-svg-5Ns83AIgnkJkuDDx .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx .cluster-label text{fill:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx .cluster-label span{color:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx .cluster-label span p{background-color:transparent;}#mermaid-svg-5Ns83AIgnkJkuDDx .label text,#mermaid-svg-5Ns83AIgnkJkuDDx span{fill:#333;color:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx .node rect,#mermaid-svg-5Ns83AIgnkJkuDDx .node circle,#mermaid-svg-5Ns83AIgnkJkuDDx .node ellipse,#mermaid-svg-5Ns83AIgnkJkuDDx .node polygon,#mermaid-svg-5Ns83AIgnkJkuDDx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Ns83AIgnkJkuDDx .rough-node .label text,#mermaid-svg-5Ns83AIgnkJkuDDx .node .label text,#mermaid-svg-5Ns83AIgnkJkuDDx .image-shape .label,#mermaid-svg-5Ns83AIgnkJkuDDx .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Ns83AIgnkJkuDDx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Ns83AIgnkJkuDDx .rough-node .label,#mermaid-svg-5Ns83AIgnkJkuDDx .node .label,#mermaid-svg-5Ns83AIgnkJkuDDx .image-shape .label,#mermaid-svg-5Ns83AIgnkJkuDDx .icon-shape .label{text-align:center;}#mermaid-svg-5Ns83AIgnkJkuDDx .node.clickable{cursor:pointer;}#mermaid-svg-5Ns83AIgnkJkuDDx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Ns83AIgnkJkuDDx .arrowheadPath{fill:#333333;}#mermaid-svg-5Ns83AIgnkJkuDDx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Ns83AIgnkJkuDDx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Ns83AIgnkJkuDDx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Ns83AIgnkJkuDDx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Ns83AIgnkJkuDDx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Ns83AIgnkJkuDDx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Ns83AIgnkJkuDDx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Ns83AIgnkJkuDDx .cluster text{fill:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx .cluster span{color:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Ns83AIgnkJkuDDx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Ns83AIgnkJkuDDx rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Ns83AIgnkJkuDDx .icon-shape,#mermaid-svg-5Ns83AIgnkJkuDDx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Ns83AIgnkJkuDDx .icon-shape p,#mermaid-svg-5Ns83AIgnkJkuDDx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Ns83AIgnkJkuDDx .icon-shape rect,#mermaid-svg-5Ns83AIgnkJkuDDx .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Ns83AIgnkJkuDDx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Ns83AIgnkJkuDDx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Ns83AIgnkJkuDDx :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    🚨 线上异常

    看 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):

    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 长等。

    解决方案:

    # 对耗时较长的接口单独加大读超时(按业务区分 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)。

    解决方案:

    # 在 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.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):

    # 临时
    ulimit -n 65535

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

    响应慢、缓冲与缓冲区

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

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

    可调配置:

    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 和慢请求:

    # 在 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 打满,需要在前端限流。

    示例配置:

    # 在 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 并发连接数,两者结合可减轻突发与单点滥用对后端的影响。

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

  • 看状态码与 Nginx/Tomcat 日志:502 先看 upstream 是否可达、max_fails;504 看 proxy_read_timeout 与后端耗时。
  • 调超时与缓冲:按业务区分 location 设置 read_timeout;大响应或流式按需调 proxy_buffers 或关缓冲。
  • 控连接与限流:worker_rlimit_nofile、worker_connections、limit_req/limit_conn 防止连接与请求打满。
  • 上传与大小: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 按天/按大小切 为什么?单文件过大会影响写性能和排查,轮转后便于按日期查看和归档

    安全建议

    # 隐藏后端服务器版本
    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 容器也有多种替代方案。本节先介绍常见组合架构,再对同类方案做简要对比,便于选型与扩展。

    与其他软件配合使用

    典型组合架构示意

    #mermaid-svg-ceRXm0kAu04HkpIE{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ceRXm0kAu04HkpIE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ceRXm0kAu04HkpIE .error-icon{fill:#552222;}#mermaid-svg-ceRXm0kAu04HkpIE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ceRXm0kAu04HkpIE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ceRXm0kAu04HkpIE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ceRXm0kAu04HkpIE .marker.cross{stroke:#333333;}#mermaid-svg-ceRXm0kAu04HkpIE svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ceRXm0kAu04HkpIE p{margin:0;}#mermaid-svg-ceRXm0kAu04HkpIE .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ceRXm0kAu04HkpIE .cluster-label text{fill:#333;}#mermaid-svg-ceRXm0kAu04HkpIE .cluster-label span{color:#333;}#mermaid-svg-ceRXm0kAu04HkpIE .cluster-label span p{background-color:transparent;}#mermaid-svg-ceRXm0kAu04HkpIE .label text,#mermaid-svg-ceRXm0kAu04HkpIE span{fill:#333;color:#333;}#mermaid-svg-ceRXm0kAu04HkpIE .node rect,#mermaid-svg-ceRXm0kAu04HkpIE .node circle,#mermaid-svg-ceRXm0kAu04HkpIE .node ellipse,#mermaid-svg-ceRXm0kAu04HkpIE .node polygon,#mermaid-svg-ceRXm0kAu04HkpIE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ceRXm0kAu04HkpIE .rough-node .label text,#mermaid-svg-ceRXm0kAu04HkpIE .node .label text,#mermaid-svg-ceRXm0kAu04HkpIE .image-shape .label,#mermaid-svg-ceRXm0kAu04HkpIE .icon-shape .label{text-anchor:middle;}#mermaid-svg-ceRXm0kAu04HkpIE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ceRXm0kAu04HkpIE .rough-node .label,#mermaid-svg-ceRXm0kAu04HkpIE .node .label,#mermaid-svg-ceRXm0kAu04HkpIE .image-shape .label,#mermaid-svg-ceRXm0kAu04HkpIE .icon-shape .label{text-align:center;}#mermaid-svg-ceRXm0kAu04HkpIE .node.clickable{cursor:pointer;}#mermaid-svg-ceRXm0kAu04HkpIE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ceRXm0kAu04HkpIE .arrowheadPath{fill:#333333;}#mermaid-svg-ceRXm0kAu04HkpIE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ceRXm0kAu04HkpIE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ceRXm0kAu04HkpIE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ceRXm0kAu04HkpIE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ceRXm0kAu04HkpIE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ceRXm0kAu04HkpIE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ceRXm0kAu04HkpIE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ceRXm0kAu04HkpIE .cluster text{fill:#333;}#mermaid-svg-ceRXm0kAu04HkpIE .cluster span{color:#333;}#mermaid-svg-ceRXm0kAu04HkpIE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ceRXm0kAu04HkpIE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ceRXm0kAu04HkpIE rect.text{fill:none;stroke-width:0;}#mermaid-svg-ceRXm0kAu04HkpIE .icon-shape,#mermaid-svg-ceRXm0kAu04HkpIE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ceRXm0kAu04HkpIE .icon-shape p,#mermaid-svg-ceRXm0kAu04HkpIE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ceRXm0kAu04HkpIE .icon-shape rect,#mermaid-svg-ceRXm0kAu04HkpIE .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ceRXm0kAu04HkpIE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ceRXm0kAu04HkpIE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ceRXm0kAu04HkpIE :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    💾 数据与中间件

    📦 应用层

    🖥️ 接入层

    👤 用户

    浏览器/客户端

    Nginx反向代理/静态

    Tomcat 1

    Tomcat 2

    RedisSession/缓存

    MySQL主库

    MQRabbitMQ/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 + TomcatApache + Tomcat(mod_jk / mod_proxy_ajp)Caddy + TomcatOpenResty + 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 容器

    对比项TomcatJettyUndertow
    定位 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_module、ngx_http_upstream_module、ngx_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 写在一起):

    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 监听地址与端口 如 80、443 ssl、[::]:80
    server_name 虚拟主机名,用于匹配 Host 头 域名或 _ 默认
    location 按 URI 匹配并应用一组指令 如 location /、`location ~* .(jpg
    root / alias 请求对应到文件系统的路径 root 把 location 拼到路径后;alias 替换 location 部分
    expires 响应头 Expires / Cache-Control 的缓存时间 如 30d、1y,静态资源加速
    add_header 给响应添加自定义头 如 Cache-Control、X-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:静态站 + 状态页只允许内网):

    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 生效):

    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 秒):

    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;
    }

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

    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_name 或 http://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 4k、8 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、超时、缓冲;并隐藏后端版本头):

    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 的区别(容易搞混,看例子):

    # 请求 /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 个并发连接):

    # 在 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,含线程池、超时、压缩):

    <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):

    <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.org 与 tomcat.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 等
    赞(0)
    未经允许不得转载:171主机测评 » Nginx + Tomcat 反向代理完全指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址