news 2026/9/30 1:18:28

TongWeb7 Linux部署实战:授权、调优、systemd与排障

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TongWeb7 Linux部署实战:授权、调优、systemd与排障

1. 先把 TongWeb7 这层窗户纸捅破:它到底解决什么问题

前阵子接了个活,客户那边一套跑了七八年的 Java Web 系统要从 Tomcat 挪到 TongWeb7 上,要求在 Linux 上重新部署一遍。我原本估摸着半小时收工,结果整整折腾了一下午——先是解压出来一堆乱码文件名,接着 License 文件放错目录导致启动脚本悄无声息地退出了,最后发现系统的文件句柄数只有 1024,压测一上来连接就被拒。这类部署活儿,难的不是"会不会",而是那些文档里从来不写、但一定会遇到的边角问题。

TongWeb7 是东方通推出的一款企业级 Java 应用服务器,实现了 Java EE 7 规范,涵盖 Servlet 3.1、JSP 2.3、EJB 3.2、JMS 2.0、JPA 2.1 这一整套能力。它和 Tomcat 最大的区别在于:Tomcat 本质是个 Servlet 容器,而 TongWeb 是完整的应用服务器,自带管理控制台、集群会话复制、JNDI 资源管理、国密算法支持这些企业级特性。很多项目之所以指定用它,是因为系统集成的中间件清单里写死了这一款,或者老应用用了 EJB、JMS 这类 Tomcat 原生不支持的东西,迁不过去。

这篇文章面向的是需要在一台干净的 Linux 服务器上,从零把 TongWeb7 跑起来的运维和开发同学。不管你用的是 CentOS、Ubuntu,还是麒麟、统信这类国内发行版,部署路径基本一致。我会把安装、授权、调优、服务化、排障这条完整链路掰开揉碎讲一遍,重点放在那些真正会卡住你的地方。

1.1 它和 Tomcat、WebLogic 的实际差异在哪

先做个横向对比,心里有个谱:

维度TomcatTongWeb7WebLogic
规范覆盖仅 Servlet/JSP完整 Java EE 7完整 Java EE
管理方式改 XML、命令行Web 控制台 + 命令行 + 配置文件控制台 + WLST
部署单元warwar / earwar / ear
集群会话需自行搭建内置会话复制内置
授权模式Apache 开源商业 License商业 License
安装体积十几 MB数百 MB上 GB

从表里能看出来,TongWeb7 的定位是"轻量一些的 WebLogic",但比 Tomcat 重。它的安装包通常有几百兆,解压后目录结构和 Tomcat 有几分神似,但多了license、deploy、domains这类目录。熟悉 Tomcat 的人上手会有种"似曾相识但又处处不同"的感觉,这也是为什么很多人第一次部署会踩坑——按 Tomcat 的习惯去操作,往往行不通。

1.2 版本和 CPU 架构必须先核对清楚

这一步千万别跳过。TongWeb7 的安装包是按 CPU 架构分发的,常见的有x86_64(Intel/AMD/海光)和aarch64(鲲鹏/飞腾)两种,个别版本还有龙芯 LoongArch 的包。拿错包解压后一执行,报错非常直接:

bash: ./startserver.sh: /bin/sh: bad ELF interpreter # 或者 cannot execute binary file: Exec format error

看到这两个报错,别急着怀疑 JDK,先执行下面几条命令确认底数:

uname -m # 输出 x86_64 还是 aarch64 cat /etc/os-release # 看发行版和版本号 getconf LONG_BIT # 32 还是 64

安装包的文件名里一般会带架构标识,比如TongWeb7.0.x.x_x86_64.tar.gz。如果文件名里没有,就找厂商要一份架构对照说明,别靠猜。

2. 装之前的环境底数:JDK、句柄数、安全策略一个都不能少

部署这类中间件,八成以上的"启动失败"其实发生在 TongWeb 启动之前,问题出在操作系统这一层。我见过太多人上来就解压、就跑脚本,然后卡在莫名其妙的报错上。正确的做法是先把系统环境捋一遍,逐项确认,后面能省下大量返工时间。

2.1 JDK 的版本选择和 JAVA_HOME 的坑

TongWeb7 主流版本要求 JDK 8 及以上(多数项目用 1.8),较新的小版本开始支持 JDK 11 和 JDK 17。选哪个版本取决于你的应用编译时用的字节码版本,不是越新越好——老应用在 JDK 17 上很可能因为模块化限制直接起不来。

装完 JDK 后有几件事必须做:

which java # 看当前用的是哪个 readlink -f $(which java) # 追到真实路径 java -version # 确认版本和位数

关键在于JAVA_HOME。TongWeb 的启动脚本会去读这个变量,如果没设或者设错了,脚本会在早期阶段就退出。建议写进/etc/profile.d/java.sh:

export JAVA_HOME=/usr/local/jdk1.8.0_361 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

写完之后source /etc/profile生效,再用echo $JAVA_HOME验证。这里有个细节:如果你是通过su - tongweb切换用户,环境变量会重新加载;但如果用su tongweb(不带减号),环境变量不会刷新,很容易出现"root 下测得好好的,切用户就起不来"的情况。这是我踩过的第一个坑。

另外提一句,如果系统里装了多个 JDK,比如系统自带一个 openjdk、你手动又装了一个,务必确认 TongWeb 用的到底是哪个。有些启动脚本内部会硬编码 JDK 路径,改配置文件比改环境变量更可靠。

2.2 文件句柄数和内核参数要提前放量

默认的 Linux 单进程文件句柄限制通常是 1024,这个数值对中间件来说完全不够用。TongWeb 一个进程打开的 socket、日志文件、jar 包句柄加起来轻松超过这个数,压测或者并发一高就是Too many open files。

修改/etc/security/limits.conf,追加:

tongweb soft nofile 65535 tongweb hard nofile 65535 root soft nofile 65535 root hard nofile 65535

同时在/etc/security/limits.d/下确认没有被别的文件覆盖掉。改完重新登录该用户,用ulimit -n验证。注意这个参数对已经登录的会话不生效,必须重新开一个终端。

还有两个常被忽略的参数:

sysctl -w net.core.somaxconn=32768 sysctl -w net.ipv4.tcp_max_syn_backlog=16384

somaxconn决定了 accept 队列长度,默认值在很多发行版上只有 128,高并发下会出现连接被丢弃的情况。写进/etc/sysctl.conf持久化。

2.3 SELinux、防火墙、时区、字符集这四件事

这四个每一件单独拎出来都能让你排查半天。

SELinux:先getenforce看状态,如果是Enforcing,TongWeb 读写某些目录、绑定非标准端口时会被拦。临时用setenforce 0验证是不是它的问题,确认后要么改成Permissive,要么老老实实写 SELinux 策略。生产环境我倾向于配置策略而不是直接关,但过渡阶段先关掉确认问题来源也是常规做法。

防火墙:别以为开放了 Web 端口就完事了。TongWeb 至少涉及三个端口——业务端口(默认 8080)、管理控制台端口(通常是 9060,以启动日志为准)、关闭端口(默认 8005)。少开一个,就会出现"能访问应用但进不去控制台"的迷惑现象。

时区:用timedatectl确认。时区不对会让日志时间线错乱,更麻烦的是 License 校验依赖系统时间,时间偏差大了会直接判为无效。

字符集:locale看一下,如果输出里全是POSIX,那中文乱码几乎是必然的。生成 UTF-8 locale:

localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 localedef -c -f UTF-8 -i en_US en_US.UTF-8

然后写进/etc/locale.conf,设LANG=zh_CN.UTF-8。

3. 安装包落地:账号、解压、权限的完整链路

环境捋顺了,接下来才是真正动手。这一段的操作看着简单,但每一步都有讲究,尤其是解压和权限这两块,做错了后面全是麻烦。

3.1 创建一个专用的运行账号

绝对不要用 root 跑 TongWeb。这是原则问题,不是习惯问题——中间件被拿下的例子不少,root 身份运行意味着整个系统都在风险敞口里。

groupadd -r tongweb useradd -r -g tongweb -m -d /home/tongweb -s /bin/bash tongweb passwd tongweb

-r表示系统账号,-m创建家目录。建完之后把安装目录的所有权交给它:

mkdir -p /opt/tongweb chown -R tongweb:tongweb /opt/tongweb

这里有个实操建议:安装目录别放在/root下面,也别放在/tmp里。/tmp在很多发行版上配置了noexec挂载选项,脚本根本执行不了,而且系统重启会被清理。

3.2 解压时中文文件名乱码的真正原因

这一条是热词里高频出现的"Linux 解压文件乱码",我专门说一下。

关键点在于:tar.gz 本身几乎不会乱码,zip 才是重灾区。原因是 tar 打包时保存的是文件名的原始字节流,解包时原样写回,跟编码无关。而 zip 格式的历史包袱比较重,Windows 上的压缩工具默认用 GBK 编码存文件名,Linux 解压时按 UTF-8 解释,就成了一堆问号或者方块。

如果你手上是 zip 包,有几种解法:

# unzip 6.0 及以上支持指定编码 unzip -O CP936 package.zip # 用 libarchive 的 bsdtar bsdtar -x --charset=GBK -f package.zip # 用 unar(macOS 移植过来的工具,中文字符处理很稳) unar -e GBK package.zip

如果是 tar.gz 且确实有乱码(少数从特殊工具打包出来的包会这样),GNU tar 1.28 以上支持转换:

tar --iconv=GBK,UTF-8 -xzvf package.tar.gz

上传之前先校验一下文件完整性,别传输中断了都不知道:

md5sum TongWeb7.0.x.x_x86_64.tar.gz # 和厂商给的校验值比对

解压后进入目录,第一件事是ls -l bin/看看脚本都是什么名字。不同小版本的脚本命名可能不一样,我见过startserver.sh、tongweb.sh、startup.sh好几种,别照着某篇教程硬套。看启动脚本里的注释和变量定义,比看教程靠谱得多。

3.3 目录结构与权限规划

解压出来的目录大致长这样:

目录作用
bin启停脚本、工具脚本
conf主配置文件、日志配置、License 相关
lib依赖 jar 包
deploy应用部署目录(热部署扫描)
logs运行日志、访问日志
temp/work临时文件、JSP 编译产物
license授权文件存放位置(部分版本)

权限上,bin下的.sh脚本需要可执行位:chmod +x bin/*.sh。logs和temp目录需要写权限,用tongweb账号运行就没这个问题。整体做一次chown -R tongweb:tongweb最省心。

4. License 与首次启动:最容易卡住的十分钟

TongWeb 是商业中间件,没有 License 起不来。这一步是新手最容易翻车的地方,因为失败时的表现往往是"脚本跑了一下就退出了",什么提示都没有。

4.1 License 文件的获取与放置位置

License 通常需要向厂商申请,申请时要提供服务器的 MAC 地址、IP、主机名等信息(各家规则略有差异,以厂商交付说明为准)。拿到的是一个license.dat或者类似名字的文件,需要放到指定目录。

放置位置不同版本有差异,常见的是conf目录或者独立的license目录。判断方法很简单:看启动日志。启动失败时把logs下的日志翻出来,一般会有明确提示,比如:

License file not found License is expired License does not match this machine

does not match this machine这句意味着绑定的机器信息变了——换网卡、改主机名、迁移虚拟机都会导致。虚拟化环境下要特别注意,克隆虚拟机时 MAC 会变,License 会直接失效。

提示:拿到 License 后先备份一份到别的地方,并且记录申请时用的主机名和 IP。后面做迁移、扩容时能省很多沟通成本。

4.2 第一次启动要盯住什么

启动命令执行之后别扭头就走,前一两分钟的输出信息量最大。

su - tongweb cd /opt/tongweb/TongWeb7.0 ./bin/startserver.sh

观察几个关键信息:JVM 有没有正常初始化、有没有绑定端口、License 校验有没有通过、最后有没有打印出控制台访问地址。同时另开一个窗口看端口:

ss -lntp | grep -E '8080|9060'

端口起来了不代表应用能用,但端口都起不来那一定是路径、权限或者配置的问题。如果启动脚本一闪就退出,用下面的方式追一下:

bash -x ./bin/startserver.sh

-x会把每条命令的执行过程打出来,卡在哪一行一目了然。

4.3 控制台登录和必须做的三件事

浏览器打开http://服务器IP:管理端口/console(管理端口以启动日志实际打印为准,常见为 9060)。默认凭据各版本不一样,老版本常见admin/admin123,新版本首次登录会强制改密,具体以安装包内说明或厂商交付文档为准。

进去之后先做三件事:

  1. 改掉默认密码,并且改成一个符合密码复杂度要求的强口令。
  2. 确认端口配置,把管理端口限制在内网访问,别暴露到公网。
  3. 检查 JVM 参数,默认的堆大小通常偏小,生产环境必须调整。

5. 按生产标准调优:从能跑到跑得稳

默认配置只保证"能启动",离"能扛住生产流量"还有距离。这一节讲几个关键参数的调整思路。

5.1 JVM 堆内存怎么定

堆大小的经验值:物理内存的 50% 到 60%,且不超过 32GB。为什么强调 32GB?因为 JVM 在堆超过约 32GB 后会失去指针压缩(Compressed OOPs)的优化,对象引用从 4 字节变成 8 字节,实际内存占用反而涨得比堆增长更快,得不偿失。

参数一般在bin下的启动脚本里,或者conf目录下某个vmoptions之类的外部配置文件里。典型配置:

-Xms8g -Xmx8g \ -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/opt/tongweb/dumps \ -Xloggc:/opt/tongweb/logs/gc.log \ -XX:+PrintGCDetails -XX:+PrintGCDateStamps

-Xms和-Xmx设成一样大,避免运行期反复扩容缩容带来抖动。GC 策略上,JDK 8 环境如果堆不大(4GB 以内)用 Parallel 更省 CPU;堆大了就上 G1,停顿更可控。HeapDumpOnOutOfMemoryError一定要开,出问题的时候这份 dump 就是唯一的救命线索。

5.2 端口、连接器和线程池

端口配置在conf下的主配置文件里,或者通过控制台图形界面改。除了改端口本身,还要关注连接器参数:

参数含义建议值
maxThreads最大工作线程数200~500,按业务 IO 特性调
minSpareThreads最小空闲线程maxThreads 的 10%
acceptCount等待队列长度与 somaxconn 匹配
connectionTimeout连接超时20000~30000 毫秒
maxConnections最大连接数视并发量而定

maxThreads不是越大越好。业务如果是 CPU 密集型,线程数超过核数太多只会加剧上下文切换;如果是 IO 密集型(大量数据库、外部接口调用),可以适当放大。我一般的做法是先在测试环境做一轮压测,从 200 起步,观察 CPU、线程栈和响应时间,再往上加。

5.3 字符编码的三层链路

中文乱码几乎每个项目都会碰到,而且往往是"应用代码没问题、数据库也没问题,就是显示乱码"。原因在于编码链路有三层,任何一层断了都会出问题:

  • JVM 层:加-Dfile.encoding=UTF-8,同时确认系统 locale 是 UTF-8。
  • 容器层:连接器上配置 URI 编码为 UTF-8,POST 请求体编码同理。
  • 数据库层:JDBC 连接串里加characterEncoding=utf8,MySQL 还要注意表和列的字符集是不是utf8mb4。

排查的时候用二分法:先写一个最简单的 JSP 或者接口,直接输出中文字符串,看是哪一层开始乱的。如果是终端里cat日志乱码,那多半是 SSH 客户端编码没设对,跟服务器无关——这一条我自己就误判过好几次。

6. 应用部署的三种姿势,按场景挑

TongWeb7 部署应用的方式比较灵活,控制台、目录、脚本三条路各有适用场景。

6.1 控制台部署:适合首次和调试

登录管理控制台,找到应用部署入口,上传 war 包,指定上下文路径,提交。整个过程图形化,能直接看到部署日志和异常堆栈,调试阶段最省事。缺点是上传大包(几百兆)时容易超时,而且每次都要人工操作。

6.2 目录部署:适合自动化发布

把 war 包丢进deploy或autodeploy目录,容器按扫描周期自动检测并部署。这种方式特别适合配合 CI/CD 做自动化——构建流水线最后一步用scp把包推过去,剩下的交给容器。需要注意的是自动扫描有间隔,改完包不会立刻生效,急的时候还是得手动触发或者重启。

上下文路径的命名也值得一提。默认按 war 包文件名生成,中文名和带特殊字符的名字一定要改掉,否则 URL 里会出现百分号编码,前端调接口时容易出玄学问题。

6.3 脚本化部署:批量场景的标配

多实例环境下,人工操作迟早出错。我的做法是写一个发布脚本,固定流程:停服务 → 备份旧包 → 替换新包 → 启动服务 → 健康检查。健康检查这一步别省,简单的curl -I看返回码就够用:

#!/bin/bash set -e APP_DIR=/opt/tongweb/TongWeb7.0 WAR=/tmp/app.war BACKUP=/opt/backup/$(date +%Y%m%d%H%M%S) $APP_DIR/bin/stopserver.sh || true sleep 5 mkdir -p $BACKUP mv $APP_DIR/deploy/app.war $BACKUP/ 2>/dev/null || true cp $WAR $APP_DIR/deploy/app.war chown tongweb:tongweb $APP_DIR/deploy/app.war su - tongweb -c "$APP_DIR/bin/startserver.sh" for i in $(seq 1 30); do if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/app/health | grep -q 200; then echo "deploy success" exit 0 fi sleep 2 done echo "deploy failed, check logs" exit 1

脚本里set -e让任何一步失败就中断,避免带着半成品状态继续跑。健康检查最多等 60 秒,超时就报错退出,让流水线标红。

7. 交给 systemd 托管,让它像系统服务一样活着

手动敲脚本启动的服务,服务器一重启就没了,运维半夜还得爬起来。用 systemd 托管是标准做法。

7.1 unit 文件怎么写

在/etc/systemd/system/tongweb.service里写:

[Unit] Description=TongWeb 7 Application Server After=network.target remote-fs.target Wants=network-online.target [Service] Type=forking User=tongweb Group=tongweb Environment=JAVA_HOME=/usr/local/jdk1.8.0_361 Environment=LANG=zh_CN.UTF-8 PIDFile=/opt/tongweb/TongWeb7.0/logs/tongweb.pid ExecStart=/opt/tongweb/TongWeb7.0/bin/startserver.sh ExecStop=/opt/tongweb/TongWeb7.0/bin/stopserver.sh ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=10 LimitNOFILE=65535 TimeoutStartSec=180 TimeoutStopSec=120 StandardOutput=append:/opt/tongweb/logs/systemd-out.log StandardError=append:/opt/tongweb/logs/systemd-err.log [Install] WantedBy=multi-user.target

几个关键点解释一下:

  • Type=forking适用于脚本自己 fork 到后台的场景。如果启动脚本本身是前台阻塞的,改成simple。
  • PIDFile必须和启动脚本实际写出的 pid 文件路径一致,路径错了 systemd 会认为启动失败,即使进程其实活着。
  • LimitNOFILE在 unit 里再设一遍,防止 limits.conf 没生效。
  • Restart=on-failure让进程意外挂掉时自动拉起,配合RestartSec避免疯狂重启打爆日志。
  • TimeoutStartSec给足 180 秒,中间件启动本来就慢,默认 90 秒经常不够。

7.2 生效与验证

systemctl daemon-reload systemctl enable tongweb systemctl start tongweb systemctl status tongweb

验证的时候别只看active (running),那只能说明主进程还在。真正的验证要访问一次业务接口,确认返回正常。另外重启一次机器,看服务是不是自动起来了。

8. 排障实战:那些让我熬到深夜的问题

前面讲的都是"按部就班",这一节讲"出了事怎么查"。我把这些年遇到的典型问题整理成一张表,后面挑几个展开说。

现象常见原因排查手段
启动脚本闪退JAVA_HOME 未设 / License 缺失bash -x跟踪,看 logs
端口未监听端口被占用 / 权限不足ss -lntp,lsof -i:8080
进程无故消失被 OOM Killer 杀掉dmesg | tail -50
中文乱码locale / file.encoding / DB 字符集三层二分排查
访问缓慢GC 频繁 / 线程池打满jstat、jstack
License 失效主机信息变化 / 时间漂移核对 MAC、timedatectl

8.1 启动失败要按"从下往上"的顺序查

排查链路应该是这样的:先看进程有没有起来,再看端口有没有监听,再看日志有没有报错,最后才怀疑应用本身。顺序颠倒的话,容易在应用层瞎折腾半天,结果发现是端口被占了。

具体操作:

ps -ef | grep -i tongweb | grep -v grep ss -lntp | grep 8080 ls -lt logs/ | head # 看最新日志文件 tail -200 logs/server.log # 看有没有异常堆栈 dmesg | tail -50 # 看有没有 OOM Killer 记录

dmesg这一条特别容易被忽略。进程"自己消失了"且日志里没有任何异常,十有八九是被内核的 OOM Killer 干掉了,dmesg里会有明确的Killed process记录。这种情况下光加堆内存没用,得先看是不是堆设得太大导致物理内存不够。

8.2 启动特别慢,可能是熵池不够

这是个比较冷门但确实存在的问题。JVM 初始化安全随机数生成器时会读/dev/random,在虚拟机上熵池积累很慢,会导致启动卡住几十秒甚至几分钟。表现是日志停在某个位置长时间不动。

验证方式:

cat /proc/sys/kernel/random/entropy_avail

如果数值长期低于 200,那基本可以确定。解决办法是安装haveged或者rng-tools来补充熵源:

yum install -y haveged systemctl enable --now haveged

另一个规避方式是在 JVM 参数里把随机数源指定为非阻塞的/dev/urandom。具体用哪种,看你的安全要求。

8.3 内存溢出和线程泄漏的现场取证

线上跑着跑着变慢,最典型的两类原因:内存回收不掉(内存泄漏)和线程堆积(线程泄漏)。这两个问题事后补救没用,必须在"慢"的时候抓现场。

内存方面:

jstat -gcutil <pid> 1000 10 # 每秒采样,看老年代使用率是否持续上涨 jmap -histo:live <pid> | head -30 # 看哪些对象占得最多 jmap -dump:live,format=b,file=/tmp/heap.hprof <pid> # 抓堆快照

如果老年代使用率在 Full GC 之后还是不降,那就是有对象被长期持有,堆快照拿去用 MAT 一分析就能定位。

线程方面:

jstack <pid> > /tmp/thread.txt grep -c "java.lang.Thread.State" /tmp/thread.txt # 总线程数 grep "java.lang.Thread.State" /tmp/thread.txt | sort | uniq -c

如果发现大量线程卡在WAITING且堆栈相同,那就是有地方在无限制地创建线程或者等待锁。隔几分钟抓两次对比,能看出哪些线程一直没动。

8.4 日志暴涨把磁盘撑爆

这条听起来低级,但我见过不止一次。访问日志加上调试日志,高峰期一天几十 GB,磁盘一满,整个服务全部挂掉,连带数据库连接池都报错。

防护措施有三层:一是日志级别调成INFO或WARN,别在生产开DEBUG;二是配置按天滚动加保留天数,比如保留 15 天;三是配 logrotate 兜底:

/opt/tongweb/TongWeb7.0/logs/*.log { daily rotate 15 missingok notifempty compress copytruncate }

copytruncate这个选项要注意——它不移动文件而是清空,适用于进程一直持有文件句柄的场景。如果不加这个选项,日志会被轮转走但进程还在往老句柄里写,导致磁盘空间不释放,这是个很隐蔽的坑。另外单独给日志挂一个分区,即使写满了也不影响系统盘。

9. 前置代理和日常巡检的几条实战经验

服务跑起来只是开始,后面还有长期的运维。这一节讲几个我认为最有价值的实践。

9.1 Nginx 前置该怎么配

直接让 TongWeb 对外提供服务不是好主意,一来不好做统一入口和证书管理,二来静态资源交给 Nginx 处理效率高得多。典型配置:

upstream tongweb_backend { server 127.0.0.1:8080 weight=1 max_fails=3 fail_timeout=30s; keepalive 64; } server { listen 443 ssl; server_name app.example.com; client_max_body_size 100m; charset utf-8; location / { proxy_pass http://tongweb_backend; proxy_http_version 1.1; proxy_set_header Connection ""; 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_connect_timeout 30s; proxy_read_timeout 120s; proxy_send_timeout 120s; } location /static/ { root /data/www; expires 7d; } }

几个容易忽略的点:proxy_set_header Connection ""配合keepalive才能复用长连接,少了这行等于白配;client_max_body_size默认 1MB,文件上传场景不加会直接返回 413;X-Forwarded-For不传的话,应用里拿到的客户端 IP 全是 127.0.0.1,做风控和审计时会很尴尬。

9.2 日常巡检该看什么

我习惯写一个巡检脚本每天跑一次,输出几个关键指标:进程是否存活、端口是否监听、JVM 堆使用率、GC 次数和耗时、日志里的 ERROR 数量、磁盘使用率。堆使用率持续超过 80% 或者 Full GC 次数陡增,就要提前介入,别等到真出故障。

PID=$(pgrep -f tongweb | head -1) echo "=== 进程 ===" [ -n "$PID" ] && echo "running: $PID" || echo "NOT RUNNING" echo "=== 堆使用 ===" jstat -gcutil $PID 2>/dev/null | tail -1 echo "=== 日志错误 ===" grep -c "ERROR" logs/server.log echo "=== 磁盘 ===" df -h /opt

这类脚本的价值在于把"发现问题的时机"从用户投诉提前到故障发生前。我做过一个统计,加了日常巡检之后,紧急故障的数量下降了一大截,因为大部分问题在变成故障之前就已经被发现了。

9.3 我踩过的几个坑,直接告诉你结论

最后分享几个具体到操作层面的经验,都是真金白银换来的:

  • 别在生产上直接用kill -9。虽然大部分情况下能重启成功,但正在写的会话、日志缓冲可能丢失。先用正常停止脚本,给足 60 秒,实在不行再强杀。
  • 配置文件改动前先备份,改动后用diff核对。中间件的配置项动辄几百个,改错一个字符可能引发连锁反应,尤其是缩进敏感的 XML 和 YAML。
  • JVM 参数不要在启动脚本里硬编码堆大小。把它抽到独立的环境变量文件里,测试环境和生产环境用同一套脚本,只换变量,减少"环境不一致"类问题。
  • 扩容或者迁移之前,先把 License 的绑定规则搞清楚。有的绑定 MAC,有的绑定 IP,有的绑定主机名。搞清楚之后再动,比动完了发现起不来要省事得多。
  • 别在业务高峰期做变更。这条听着像废话,但每年都有团队在流量最大的时候去调 JVM 参数。

部署这件事本身的复杂度其实不高,真正耗时间的是环境差异和那些文档没覆盖的细节。把这套流程跑通一次,写成脚本固化下来,后面再部署就是几分钟的事。我自己维护的那套发布脚本,从最开始手动敲十几个命令,到现在一条命令搞定,省下来的时间全用来处理真正的业务问题了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 1:18:13

Python漏洞扫描系统实战:Django+Docker+Nmap从设计到落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:17:55

墨卡托投影坐标系:从航海导航到在线地图的数学原理与工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:17:09

嵌入式驱动开发:从能跑到量产级稳定的工程化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:16:34

FreeRTOS任务优先级与系统心跳Tick配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:16:30

嵌入式内存深度解析:malloc、栈溢出与物理地址映射

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:15:09

极值点、驻点、拐点辨析:从几何本质到判定逻辑链

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华