ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux部署东方通TongWeb7:国产化中间件替换与启动排错

Linux部署东方通TongWeb7:国产化中间件替换与启动排错 最近接手的一个内网项目把原来跑在 Tomcat 上的一套业务系统迁到了东方通 TongWeb7 上操作系统是 CentOS 7.9 和麒麟 V10 各一套。整个过程里Linux 部署 东方通 TongWeb7 这件事看着像解压、装、启动三步走真上手才发现中间有一堆细节授权文件放错目录起不来、主机名没在 hosts 里解析导致启动卡两分钟、JVM 参数写在脚本里被升级覆盖、domains 目录权限不对导致日志写不进去。这篇东西就是把我这两套环境的完整落地过程摊开写一遍包括怎么选安装方式、怎么规划端口和目录、怎么做 systemd 自启、怎么排查启动失败以及几个只有踩过才知道的坑。适合手上第一次接触 TongWeb7 的运维兄弟也适合正在做国产化中间件替换、需要一份可复现操作手册的开发同学。我不打算讲太多产品介绍重点放在命令敲什么、参数为什么这么设、报错怎么查这三件事上。1. 部署前的整体思路与方案选型1.1 为什么要在 Linux 上落地 TongWeb7先说清楚这件事的边界。TongWeb7 是一款 Java EE 应用服务器和 Tomcat、JBoss 属于同一类东西只不过它把控制台、集群管理、类加载隔离、会话共享这些企业级能力做进了同一个产品里。业务系统跑在上面你得到的不只是一个 Servlet 容器还有一套可视化的域管理界面和一整套配置持久化机制。项目里选它通常不是因为想换而是因为既有系统对国产生态适配有要求或者甲方指定了中间件目录这事儿没什么可纠结的重要的是把它稳稳当当地跑起来。Linux 作为宿主环境好处很直接一是资源占用可控去掉图形界面后一台 4C8G 的虚拟机就能撑起测试环境二是脚本化能力强安装、启动、巡检、备份全部可以写成 shell做成流水线三是文件权限模型清晰能把中间件进程和业务应用拆到不同用户下运行。坏处也有就是所有的配置都得自己去啃文档和试错没有 Windows 上那种下一步下一步的顺滑感。所以我一般建议测试环境先用图形化安装摸清楚流程摸透了再沉淀成静默安装脚本正式环境全部走脚本。1.2 图形安装和静默安装到底选哪个安装方式是部署环节第一个分叉口。TongWeb7 的 Linux 安装包通常同时支持图形化GUI、控制台console和静默silent三种模式区别在于交互程度和可复制性。安装方式触发参数适合场景优点坑点图形化直接执行安装程序本地有桌面环境的测试机步骤直观能实时看到端口占用提示服务器一般没装 X11需要额外装依赖远程 X11 转发还容易断控制台-i console无图形界面的测试/预发纯文本交互能看清楚每一步问什么交互项多敲错一个要重来静默-i silent -f 应答文件正式环境、批量部署完全可复制一条命令跑完应答文件写错不会报错直接装歪我自己的做法是第一次装的时候用-i console走一遍把每一个交互项的答案记下来然后照着生成一份应答文件后面所有环境都用静默模式。这样做的好处是第一次搞明白后面全自动而且应答文件本身就是部署文档交接的时候直接给对方就行。注意静默安装对路径极其敏感。应答文件里的安装目录如果带有末尾斜杠部分版本会把它当成非法路径安装直接静默失败日志里只留一行很难看懂的错误。写路径时统一不带末尾斜杠。1.3 环境规划目录、用户、端口一次定死动手之前先把三件事定死后面基本不会返工。第一是目录规划我习惯用/opt/tongweb也就是把安装目录软链到这块放程序/data/tongweb放域实例和日志这样程序目录升级重装不影响数据和日志回滚也方便。第二是运行用户单独建一个tongweb用户禁止登录 shell所有中间件进程都用它起绝不 root 裸跑。第三是端口提前和网络、开发对齐避免装完发现端口被占。端口规划我一般是这么排的用途默认端口我的取值说明HTTP 服务端口80808080应用对外访问入口前面一般挂 Nginx控制台管理端口90609060控制台地址只对内网管理段开放HTTPS 端口84438443需要证书时用不用可以空着AJP 端口8009不启用现在基本用不到关掉减少攻击面集群通信端口按安装向导9200 段单机部署不需要集群时统一规划端口这块有个经验控制台端口一定要用防火墙限死来源 IP。控制台是管理入口一旦暴露在业务网段等于把整个中间件的配置权限敞开风险很大。2. 环境准备与系统参数调优2.1 JDK 的版本选择和落地方式TongWeb7 本质上是 Java 应用JDK 就是它的地基。以我手上这套 7.0.4.x 为例主流搭配是 JDK 81.8.0_191 及以上高版本适配包也能跑 JDK 11但要注意安装时选定的 JDK 路径会被写进域的启动脚本装完之后再换 JDK 需要手动改脚本或者重新配置所以在安装前就把 JDK 定好。我用的做法是手动安装 OpenJDK 或者用发行版自带的 JDK 包两者都行但千万别混。检查方式很简单java -version echo $JAVA_HOME which java三条命令的输出必须指向同一个 JDK。我见过最典型的翻车现场是JAVA_HOME指向 JDK 8但 PATH 里/usr/bin/java是个 JDK 17 的软链结果启动脚本用JAVA_HOME起服务没问题运维手动敲java -jar做别的操作时用的却是另一个版本排查半天以为是中间件的问题。如果你的发行版仓库里 JDK 版本比较老或者需要特定小版本建议直接下载 tar.gz 解压到/usr/local/jdk1.8.0_xxx然后在/etc/profile.d/java.sh里统一导出环境变量。这样升级、回滚都只是改一个软链的事。# /etc/profile.d/java.sh export JAVA_HOME/usr/local/jdk1.8.0_341 export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar改完记得source /etc/profile.d/java.sh并且重新登录一次验证因为sudo默认不继承环境变量这是个高频坑。2.2 内核参数与文件句柄的调优依据Java 应用服务器在 Linux 上跑瓶颈很少出现在 CPU 上绝大多数跑着跑着就崩的问题都跟资源限制有关。下面这几项是我部署每套环境都会调的参数值不是拍脑袋来的每一项都有依据。# /etc/security/limits.conf 追加 tongweb soft nofile 65535 tongweb hard nofile 65535 tongweb soft nproc 65535 tongweb hard nproc 65535nofile设成 65535 的原因很实在一个 Java 进程的线程数、打开的 socket、读的 jar 包文件全都算在文件句柄上。默认 1024 的情况下应用并发一上来就会抛Too many open files而且报错位置往往在业务代码里很难第一时间联想到系统限制。nproc同理控制在用户能创建的进程/线程数上限。内核参数我一般这么调# /etc/sysctl.d/99-tongweb.conf net.core.somaxconn 32768 net.ipv4.tcp_max_syn_backlog 16384 net.ipv4.tcp_tw_reuse 1 net.ipv4.ip_local_port_range 10000 65000 vm.swappiness 10 vm.max_map_count 262144somaxconn是 accept 队列长度Java 里的 backlog 设得再大内核这一层卡住也没用所以两边一起调。tcp_tw_reuse允许复用 TIME_WAIT 状态的连接短连接密集的业务能明显减少端口耗尽。ip_local_port_range扩大可用本地端口范围配合上面那条一起用。swappiness降到 10是因为 Java 堆一旦被换到 swap 上GC 停顿会变得极其难看宁可让它 OOM 也不要让它慢慢换页。max_map_count主要影响大量 mmap 的场景日志文件多、jar 包多的时候会用到。注意limits.conf里的用户名必须和实际运行中间件的用户一致。如果你的启动脚本用 root 执行但通过su - tongweb切换用户限制是生效的如果直接用sudo -u tongweb且没配pam_limits可能不生效。最稳的验证方式是切到该用户后执行ulimit -n看实际值别只看配置文件。2.3 主机名解析和系统时区这类看不见的坑有两件小事出问题的时候排查成本极高提前做掉能省很多事。第一件是主机名解析。TongWeb 启动过程中会获取本机主机名并尝试解析如果/etc/hosts里没有对应记录很多 Linux 发行版会走 DNS 查询超时后才回退表现就是启动脚本执行后卡住不动一到两分钟。解决办法是把主机名和 IP 显式写进去hostname # 假设输出 tw-node01则写入 echo 192.168.10.21 tw-node01 /etc/hosts第二件是时区。日志时间戳如果和业务系统对不上排查问题时会很痛苦。统一用timedatectl set-timezone Asia/Shanghai设置并确认date输出正确。同时建议配置 NTP 或 chrony 做时间同步集群环境下时间漂移会导致会话共享、日志对齐出现各种诡异问题。第三件容易被忽略的是/tmp挂载。有些加固基线会把/tmp设成noexec而部分安装程序会把临时文件解压到/tmp下再执行这时候安装会直接失败。如果确实需要保持noexec可以在执行安装脚本时通过环境变量把临时目录指到别的路径mkdir -p /opt/tmpinstall chmod 700 /opt/tmpinstall TMPDIR/opt/tmpinstall sh install.sh -i console3. TongWeb7 安装与实例创建实操3.1 安装包解压、授权文件与目录权限处理拿到安装介质后先校验这一步别省。校验内容至少包括文件大小和 md5避免传输过程中损坏导致安装到一半报奇怪的错。md5sum TongWeb7.0.x.x_Enterprise_Linux.tar.gz tar -zxf TongWeb7.0.x.x_Enterprise_Linux.tar.gz -C /opt/ ls -l /opt/ | grep -i tongweb解压出来的通常是安装程序本身或者是已经解好的产品目录。授权文件是整个部署里最容易翻车的一环。这个文件一般由厂商按你的机器信息比如 MAC 地址、主机名或者 IP生成拿到后需要放到指定的 license 目录下。不同小版本对目录位置的要求不完全一样常见的是安装根目录下的license/或者conf/目录。我的建议是拿到包之后先在测试机上装一遍用find /opt/tongweb -name *license*确认一下产品实际认哪个路径再往正式环境放。授权文件相关的几个高频问题一是文件名被下载工具改成了xxx(1).dat这种产品认不出来二是文件权限不对tongweb用户读不到三是换机器后授权失效因为绑定信息变了。这三点基本覆盖了授权类报错的大部分情况。目录权限这块我一般这么处理useradd -r -s /sbin/nologin tongweb chown -R tongweb:tongweb /opt/tongweb chown -R tongweb:tongweb /data/tongweb chmod -R 755 /opt/tongweb chmod 700 /data/tongweb程序目录给读执行权限就够了数据目录给 700因为里面可能有数据源密码之类的敏感配置。3.2 走一遍控制台安装并生成静默应答文件我第一次装的时候用的是 console 模式目的是把每一步都看清楚。执行的命令形式大致是这样具体文件名以你拿到的包为准cd /opt/tongweb sh install.sh -i console交互过程里通常会问这几类信息安装目录、JDK 路径、是否安装为系统服务、管理员账号密码、域名称、各端口号。这些答案我建议边装边记下来装完立刻整理成应答文件。应答文件本身是个纯文本的键值对文件把上面的答案逐条对应填进去然后用静默方式执行sh install.sh -i silent -f /opt/tongweb_silent.txt静默安装跑完之后一定要做三件事来验证一是检查安装日志看有没有 WARNING二是看目标目录里的文件数量和结构是否和测试机一致三是直接尝试启动一次确认能起来。静默安装最大的问题是错了不吭声所以验证环节必须做扎实不能装完就认为成功了。实操心得把应答文件和安装包放在同一个版本目录下一起归档并在文件名里带上版本号和日期比如tw7_silent_7042_20240612.txt。半年后你要重装环境翻到这份文件会非常省事。3.3 创建域实例把配置和数据分隔开安装完成后中间件本身还没有可以直接跑业务的实例需要创建域domain。域是 TongWeb 里配置隔离的基本单位一个域对应一套端口、一份配置、一个日志目录。单机部署一般建一个域就够了集群部署会建多个。创建方式有两种控制台图形界面创建以及命令行创建。命令行更通用形式大概是cd /opt/tongweb/bin sh domain.sh create --name appsvr01 --port 8080 --admin-port 9060创建时最容易出错的地方是端口冲突和路径不存在。建议提前用ss -lntp | grep -E 8080|9060确认端口空闲并确认数据目录已经提前建好且属主正确。创建完成后域的目录结构大致是这样路径作用关注点domains/appsvr01/config/域的核心配置端口、数据源、JVM 配置都在附近domains/appsvr01/logs/服务端日志和访问日志排查问题的第一现场domains/appsvr01/deployment/应用自动部署目录支持目录方式部署时可以放这里domains/appsvr01/applications/已部署应用的展开目录看实际生效的配置bin/启停脚本升级时会被覆盖别改这里这个目录划分说明一件事要改的配置都在域目录下程序目录只读。很多人习惯直接改bin里的启动脚本加 JVM 参数结果一次小版本升级全没了。正确的做法是通过域级别的 JVM 配置文件或者控制台来设置。3.4 启停命令、控制台访问与状态确认启停是日常操作频率最高的动作命令本身不复杂# 启动 cd /opt/tongweb/bin sh startdomain.sh appsvr01 # 停止 cd /opt/tongweb/bin sh stopdomain.sh appsvr01 # 查看进程 ps -ef | grep -v grep | grep tongweb # 看端口是否监听 ss -lntp | grep -E 8080|9060新手最常问的问题是启动完了到底算不算成功。我的判断顺序是这样的先看进程在不在再看端口有没有监听然后看日志里有没有出现启动完成的关键行最后打开控制台页面确认。四步都过才算成功只看进程是绝对不够的因为 Java 进程可能在启动过程中抛异常但没退出处于僵着的状态。控制台默认是http://内网IP:9060/console管理员账号通常为admin密码是安装时设置的如果走静默安装密码在应答文件里能找到。第一次登录后建议立刻做两件事改掉默认密码以及把控制台的访问来源限制到管理网段。日志方面服务端主日志在域的logs目录下日志级别可以在控制台调整。刚开始排查问题时我一般把级别调到 FINE 或者 DEBUG问题定位后立刻调回去因为 DEBUG 级别下日志量增长非常快磁盘很容易被写满。4. 应用部署与参数调优实战4.1 部署 war 包的两种姿势和各自适用场景应用部署方式主要有两种控制台上传/指定 war 包以及直接把 war 放到部署目录里让系统自动扫描。前者适合正式环境过程可见、有回显、能配置上下文路径后者适合开发和测试环境改完扔进去就行。控制台部署的操作路径一般是登录控制台 → 应用管理 → 部署 → 选择 war 文件 → 指定上下文路径 → 完成。这里有几个参数值得留意配置项作用建议值上下文路径应用的访问前缀与前端 Nginx 转发规则保持一致类加载策略决定优先用应用自带类还是中间件自带类有 Spring 等框架冲突时选父类最后加载会话超时用户无操作多久失效按业务合理设置默认 30 分钟通常够用部署顺序多应用依赖时的启动次序有依赖关系的应用显式排顺序类加载策略是这一节里最重要的知识点。Java 应用服务器默认遵循双亲委派也就是先找中间件自带的类再找应用里的类。如果应用打包时带了和中间件同名的库比如某些日志实现、JSON 库就可能出现版本冲突典型表现是启动时报NoSuchMethodError或者ClassNotFoundException而且报错位置看起来毫无道理。这时候把类加载策略改成先加载应用类问题往往立刻消失。4.2 JVM 参数怎么算、写在哪里JVM 参数直接决定服务稳不稳但很多环境是复制粘贴别人的参数完全不看机器配置。我给一个可以照着算的方法。假设机器是 8 核 16G业务系统属于中等并发峰值并发几百可以这么算堆内存-Xms和-Xmx设成一样的值避免运行期反复扩容引起停顿。取值建议是物理内存的 40% 到 50%这里取-Xms8g -Xmx8g。之所以不给更多是因为堆外的开销也要留空间线程栈、直接内存、元空间、JIT 编译缓存、文件缓存加起来通常要 3G 以上。元空间-XX:MaxMetaspaceSize512m。应用依赖多、动态生成类多的场景给 512m 比较稳妥给太小会在运行一段时间后突然 OOM且报错和堆内存无关很容易误判。线程栈-Xss512k。默认值一般是 1M压到 512k 能在同等内存下支撑更多线程。但如果应用里有深层递归压太狠会栈溢出所以这个值要结合代码特点。直接内存-XX:MaxDirectMemorySize1g。用 NIO、Netty 或者大量文件传输的应用必须显式限制否则它会在堆外无限增长最后被操作系统 OOM Killer 干掉日志里连个 Java 异常都看不到。GC 选择上JDK 8 场景我一般用 G1加-XX:UseG1GC -XX:MaxGCPauseMillis200停顿更平滑。一个完整的参数示例-Xms8g -Xmx8g -XX:MaxMetaspaceSize512m -Xss512k -XX:MaxDirectMemorySize1g -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/tongweb/dump -Xloggc:/data/tongweb/logs/gc.log -XX:PrintGCDetails -XX:PrintGCDateStamps这些参数写在域级别的 JVM 配置文件里或者在控制台的 JVM 配置页面里填。不要写进bin目录下的启动脚本前面说过升级会覆盖。HeapDumpPath指向的目录要提前创建好并给tongweb用户写权限否则真出事的时候 dump 文件生成不出来等于白配。注意事项如果服务器上同时跑多个 Java 进程堆内存一定要分开算总账。我见过一台 16G 的机器上跑三个 Java 进程各配 8G 堆的配置结果就是系统频繁 swap谁都不好用。4.3 数据源、连接池和日志的常用配置数据源配置在控制台里完成配置项和一般的连接池差不多但有几个容易被忽略的连接有效性检测一定要开。数据库重启、网络抖动之后池子里的死连接如果不检测业务会持续报错直到池子里的连接被耗完。检测方式用一个轻量 SQL比如查系统表。最大连接数不是越大越好。数据库那边能承受的连接数是有限的应用侧把池子开到 200数据库侧max_connections只有 300多开几个应用就全崩了。经验值是按数据库承载能力的 60% 来分多个应用之间摊。超时设置获取连接超时、语句超时都要设。不设的话一旦数据库慢查询堆积应用线程会被全部挂住表现为服务还在但完全不响应。日志配置上我一般把服务端日志按天滚动保留 15 天以上访问日志单独输出方便做流量分析。日志目录要单独挂盘或者至少做好容量监控因为日志把根分区写满导致服务不可用是运维事故里排名靠前的一类。4.4 做成 systemd 服务别再用 nohup 挂后台测试环境用sh startdomain.sh 挂着跑可以正式环境一定要做成 systemd 服务。好处是明确的开机自启、崩溃自动拉起、日志统一收口、启停状态可查。一个可用的 unit 文件长这样[Unit] DescriptionTongWeb7 Domain appsvr01 Afternetwork.target remote-fs.target Wantsnetwork.target [Service] Typeforking Usertongweb Grouptongweb EnvironmentJAVA_HOME/usr/local/jdk1.8.0_341 EnvironmentLANGzh_CN.UTF-8 ExecStart/opt/tongweb/bin/startdomain.sh appsvr01 ExecStop/opt/tongweb/bin/stopdomain.sh appsvr01 TimeoutStartSec180 TimeoutStopSec120 Restarton-failure RestartSec10 LimitNOFILE65535 [Install] WantedBymulti-user.target几个细节值得说。Typeforking是因为启动脚本会派生后台进程用simple会导致 systemd 认为服务已经退出。TimeoutStartSec给到 180 秒是因为应用多的域启动确实慢给太短会被 systemd 判定为启动失败然后杀掉。LimitNOFILE在 unit 文件里再写一遍避免limits.conf在某些发行版上不生效。EnvironmentLANG显式指定编码能规避一部分中文乱码问题。写完执行systemctl daemon-reload systemctl enable tongweb-appsvr01 systemctl start tongweb-appsvr01 systemctl status tongweb-appsvr01实操心得ExecStop如果只是发停止信号遇到应用里有卡死线程时可能停不下来。建议在停止脚本后面加重试逻辑或者设置KillModemixed配合TimeoutStopSec超时后由 systemd 强制清理避免停不掉又起不来的尴尬局面。5. 常见问题排查与避坑实录5.1 启动失败类问题的排查链路启动失败是最常见也最让人头疼的一类。我总结的排查顺序是固定的照着走基本能定位看进程ps -ef | grep tongweb。如果进程根本没有说明脚本在早期就退出了。看启动输出启动脚本的标准输出往往会打出第一现场的错误比如找不到 licenseJAVA_HOME 未设置。很多人直接去看日志文件反而错过了最直白的提示。看域日志logs目录下的服务端日志从头往下扫重点找SEVERE、ERROR、Exception。看端口ss -lntp | grep 端口确认有没有被别的进程占用。看权限检查日志目录、临时目录、dump 目录的属主和权限。现象可能原因处理方式启动脚本瞬间退出无日志JAVA_HOME 未设置或路径错误检查环境变量注意 sudo 不继承环境变量提示授权无效授权文件路径不对、文件名被改、绑定信息不匹配找到产品实际读取的目录核对文件名和权限启动卡住一两分钟才起来主机名无法解析在 /etc/hosts 中补充主机名与 IP 映射端口被占用8080 被其他服务占用或上次进程未完全退出ss -lntp定位占用进程必要时清理残留日志目录无文件生成目录权限不足或磁盘满检查属主、权限和df -h启动报 ClassNotFound类加载策略或依赖缺失调整类加载顺序检查应用打包是否完整5.2 中文乱码从解压到日志的全链路处理中文乱码这个问题在国产化环境里出现频率很高而且往往同时出现在好几个地方解压安装包时文件名乱码、应用读配置文件时中文乱码、日志里中文变成问号。它们的原因其实不是同一个要分开治。解压乱码是最常见的。Linux 上unzip默认按 UTF-8 处理文件名而很多从 Windows 环境打包出来的 zip 用的是 GBK 编码解出来就是一堆问号。解决办法# 指定编码解压 unzip -O CP936 filename.zip -d target_dir # 或者先看看里面有什么确认编码 unzip -l filename.zip | head如果版本较老的unzip不支持-O参数可以用7z或者iconv转换文件名列表后再处理。tar.gz 包一般不存在这个问题因为 tar 不记录编码直接按字节流处理。应用层乱码的根源通常是 JVM 的默认字符集。检查方式# 切到运行用户后执行 java -XshowSettings:properties -version 21 | grep -i encoding如果file.encoding不是 UTF-8就要在 JVM 参数里显式加上-Dfile.encodingUTF-8同时确认系统 locale 设置正确。另外 systemd 单元文件里的LANG也要设因为 systemd 启动的进程环境和你登录后的 shell 环境是两套东西。日志乱码有时候不是编码问题而是字体或者查看工具的问题。用file命令确认日志文件的实际编码如果确实是 UTF-8 但显示异常那多半是终端的问题换个终端或者设置LANG就能解决。5.3 内存与性能类问题的定位思路服务跑起来之后最常见的一类反馈是用着用着就慢了或者半夜重启了一次。这类问题没有快捷公式但有固定的观察点。第一步看 GC 日志。前面配的-Xloggc这时候就派上用场了。重点看两件事Full GC 的频率和单次耗时。如果 Full GC 每分钟都在发生说明堆内存不够或者有内存泄漏如果单次 Full GC 超过一秒业务侧会有明显的卡顿感需要调整堆大小或者 GC 参数。第二步看系统层面。用top -Hp pid找出占用 CPU 最高的线程再把线程 ID 转成十六进制到 jstack 输出里去找对应的栈能定位到具体是哪段代码在烧 CPU。这个方法的原理很朴素Java 线程在操作系统层面就是线程两者的 ID 可以对应上。# 找出 CPU 占用最高的线程 top -Hp 12345 # 假设最高的线程 ID 是 12378转十六进制 printf %x\n 12378 # 在 jstack 输出中查找 nid0x305a 的线程 jstack 12345 | grep -A 30 nid0x305a第三步看连接数。用ss -s看整体的 socket 状态分布如果 TIME_WAIT 数量巨大说明短连接太多考虑上连接池或者调内核参数如果 ESTABLISHED 长时间居高不下可能是应用没有正确关闭连接。注意jstack、jmap这类工具在正式环境使用要谨慎jmap -dump会触发一次较长时间的停顿。建议在业务低峰期操作或者先用-XX:HeapDumpOnOutOfMemoryError在出事的时候自动抓取事后分析。5.4 一个真实案例的完整复盘说一个我印象比较深的。某次上线后应用能访问但每隔大约两小时就会无响应一次几十秒后自己恢复。看日志没有任何异常CPU 和内存指标也正常。排查路径是这样的先看 GC 日志发现每两小时有一次持续二十多秒的 Full GC堆内存使用曲线呈现缓慢上升然后骤降的锯齿形说明有对象在持续累积。然后用 jmap 抓了一份堆快照用分析工具看占用最多的对象发现是一个本地缓存 Map 没有设置上限随着用户会话增加无限膨胀直到触发 Full GC 才被清掉。解决办法很直接把那个 Map 换成带容量上限和过期策略的缓存实现。改完之后服务再没出现过周期性卡顿。这个案例的价值在于性能问题不一定是配置问题有时候配置调得再漂亮代码层面的问题也得从代码层面解决。作为运维我们能做的是把观测手段准备好——GC 日志、堆快照、线程栈让开发能快速定位。6. 上线后的巡检、备份与持续维护6.1 日常巡检该看哪几个指标服务上线只是开始稳定运行靠的是日常巡检。我一般会把这些检查项做成一个脚本每天早上跑一遍输出成一份简短报告检查项命令判断标准进程状态systemctl status tongweb-appsvr01active (running)端口监听ss -lntp | grep 8080存在 LISTENHTTP 响应curl -s -o /dev/null -w %{http_code} http://127.0.0.1:8080/健康检查路径返回 200堆内存使用率从控制台或 JMX 取值长期低于 70%日志错误数grep -c ERROR 当日日志无异常增长磁盘使用率df -h /data低于 80%文件句柄ls /proc/pid/fd | wc -l远低于限制值这里我要特别强调健康检查接口。用curl探测首页是不够的因为首页可能是静态页服务内部已经出问题了它照样返回 200。最好的做法是让开发提供一个能反映真实依赖状态的检查接口比如会去 ping 一下数据库的接口。配合定时任务或者上一层的监控平台就能做到问题早发现。6.2 备份策略与回滚预案中间件的备份不是简单地把整个目录打包那样文件太多太碎恢复也慢。我的做法是分层备份第一层是配置备份也就是域目录下的配置文件夹。这部分文件小、变更少每次变更前手动备份一份带时间戳的压缩包就够。这类备份的价值最高因为配置错了最容易出问题。第二层是应用包备份保留最近三到五个版本的 war 包。回滚时只需要重新部署旧版本的 war这是最快的回滚方式。第三层是授权文件和安装包备份放到独立的归档目录里。这类文件不常变但一旦丢失重新申请要走流程很耽误事。回滚预案要写清楚什么时候判定需要回滚、回滚的步骤是什么、谁有权决定回滚。我的习惯是上线后半小时内如果健康检查持续失败或者核心接口错误率超过阈值直接回滚不做现场调试。这个原则救过我好几次因为线上调试往往会把问题搞得更复杂。6.3 和监控体系对接的几种方式如果你们已经有监控平台把 TongWeb 接进去会让运维轻松很多。常见的接入方式有几种一是JMX 接入。Java 应用天然支持 JMX开启之后可以采集到堆内存、线程数、连接池状态等细粒度指标。方式是在 JVM 参数里加上 JMX 相关配置然后用采集器去拉数据。要注意的是 JMX 端口不能暴露在业务网段且建议开启认证否则等于开了一个可以远程操作应用的后门。二是日志采集。把域的日志目录配到采集器里做集中存储和关键字告警。这种方式最直接成本也最低适合刚开始建立监控体系的团队。三是黑盒探测。就是从外部定期请求健康检查接口判断服务可用性。这种方式不依赖应用内部实现作为兜底手段非常合适。三种方式各有侧重我的建议是至少做日志采集和黑盒探测这两层前者帮你定位问题后者帮你第一时间发现问题。两者结合起来基本能覆盖大部分故障场景。最后分享一个我在实际维护中养成的习惯每次对中间件做变更无论是改参数、换应用包还是调数据源都在一个固定的记录文件里写一笔包括时间、操作内容、执行人、验证结果。这个动作花不了两分钟但半年后你回头看或者在出问题时需要快速判断是不是最近那次变更引起的这份记录的价值就体现出来了。中间件这东西配置项多、相互影响复杂靠脑子记是靠不住的写下来最省事。
返回列表