1. 项目概述:一次典型的TongWeb部署排障实录
最近在帮一个金融项目做国产化迁移,把原本跑在Tomcat上的老系统搬到TongWeb上。本以为就是换个容器,改改配置的事儿,结果从环境准备到应用启动,踩的坑一个接一个,从经典的
ClassNotFoundException
到诡异的
addServlet
异常,几乎把TongWeb部署的“常见病”都得了一遍。折腾了两天,总算把系统跑起来了。今天就把这次踩坑和填坑的过程完整复盘一下,尤其是那些官方文档里语焉不详,但实际部署中又高频出现的问题。如果你也正在或即将进行TongWeb的部署,特别是从其他应用服务器迁移过来,这篇记录或许能帮你省下不少排查时间。
TongWeb作为一款主流的国产Java应用服务器,在信创项目中越来越常见。它的核心与Tomcat类似,但在类加载机制、配置管理、安全策略等方面有自己的实现,这就导致了直接迁移时容易“水土不服”。这次遇到的问题,表面上是各种报错,根源大多在于对TongWeb特性不熟悉,用Tomcat的思维去套用。接下来,我会按照部署流程,拆解几个最棘手的问题及其解决方案。
2. 环境准备与基础配置的“暗礁”
部署的第一步永远是环境。这次我们用的是TongWeb 7.0,JDK是1.8。看似简单的组合,却埋着第一个坑。
2.1 JDK版本与TongWeb的兼容性陷阱
很多人觉得JDK 1.8是“万金油”,但TongWeb对JDK小版本其实有隐藏要求。我们最初使用了JDK 1.8.0_181,启动TongWeb本身没问题,但在部署一个用到JAXB(Java Architecture for XML Binding)的模块时,抛出了
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
。这个错误在Tomcat 8.5 + JDK 1.8上也可能出现,但原因和解决方式在TongWeb上略有不同。
问题根源 :从JDK 9开始,JAXB等模块被移出了Java SE的核心库,成为了需要单独引入的模块。虽然我们用的是JDK 1.8,但某些应用(特别是Spring Boot或一些老框架)的依赖可能会间接引用到新的API路径,而TongWeb的类加载器在处理这些路径时,策略可能更严格。
我们的解决方案 :
- 首选方案(推荐) :升级JDK到1.8的最新小版本(如1.8.0_381),并确保TongWeb版本与之匹配。这能解决大部分因JDK内部模块化引起的兼容性问题。
-
临时方案
:如果无法升级JDK,需要在应用的
WEB-INF/lib目录下显式添加JAXB的API实现包。但要注意,直接添加javax.xml.bind:jaxb-api和com.sun.xml.bind:jaxb-impl可能会引起包冲突。更稳妥的做法是,检查你的项目构建工具(如Maven),确保相关依赖作用域为provided,让TongWeb的类加载器来管理。
注意 :不要在TongWeb的
lib目录下随意添加JAR包,这可能会破坏其自身的类加载体系,引发更难以预料的问题。应用的依赖应严格放在其自身的WEB-INF/lib中。
2.2 安装目录权限与中文路径的“坑”
在Linux环境下安装TongWeb时,我们习惯性地用了一个带中文的用户目录,结果启动脚本
startserver.sh
直接报错“路径不存在”。这是因为许多Shell脚本对中文路径的支持并不好。
实操要点 :
-
安装路径
:绝对不要使用包含中文、空格或特殊字符(如
&,#)的路径。最简单的就是用全英文路径,例如/opt/tongweb7。 -
权限设置
:TongWeb的运行用户(如
tongweb)需要对整个安装目录有读和执行权限,对logs、work、temp等目录有写权限。一个常见的错误是,用root用户解压安装包,导致文件所有者是root,后续用普通用户启动时没有写入日志的权限,表现为服务静默启动失败。# 假设安装目录是 /opt/tongweb7 chown -R tongweb:tongweb /opt/tongweb7 chmod -R 755 /opt/tongweb7 # 确保运行用户对以下目录有写权限 chmod -R 775 /opt/tongweb7/logs /opt/tongweb7/work /opt/tongweb7/temp
2.3 内存参数调整:不只是Xmx和Xms
在
tongweb7/bin/setenv.sh
(或
setenv.bat
)中配置JVM参数是常规操作。但除了
-Xms
和
-Xmx
,还有几个参数在TongWeb上至关重要:
-
-Dfile.encoding=UTF-8:强制指定文件编码,避免中文字符乱码,这个在国产化环境中尤其重要。 -
-Dsun.jnu.encoding=UTF-8:指定本地字符编码,与上一个参数配合使用。 -
-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath:当内存溢出时自动生成堆转储文件,对于后续分析线上问题是无价之宝。
一个典型的配置示例:
JAVA_OPTS="$JAVA_OPTS -Xms2048m -Xmx4096m -Xmn1024m"
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/tongweb7/logs/heapdump.hprof"
3. 应用部署的核心难题与破解之道
环境搞定后,开始部署WAR包。这里才是“重灾区”,我们遇到了两个最具代表性的错误。
3.1
java.lang.NoClassDefFoundError: java/util/logging/LogRecord
深度解析
这个错误让人非常困惑,
java.util.logging.LogRecord
是JRE的核心类,怎么会找不到呢?错误栈通常指向类似
org.apache.juli.logging.LogFactory
的初始化过程。
问题本质
:这通常不是真的缺少这个类,而是
类加载器冲突
的典型表现。TongWeb可能使用了自定义的类加载器(如ParallelWebappClassLoader),而你的应用或其依赖的某个库(常见的是旧版本的
tomcat-juli
、
log4j-over-slf4j
或某些通信中间件客户端)打包了不同版本的
servlet-api
或
el-api
等J2EE API包。当TongWeb的类加载器试图加载某个类时,先加载到了应用自带的、不完整的API类,而在后续链接依赖的核心JDK类(如
LogRecord
)时,由于类加载器空间隔离或版本不一致,导致链接失败。
排查与解决步骤 :
-
检查WAR包
:使用
jar tvf your-app.war | grep -E "(servlet|el|jsp).*\.jar"命令,检查WAR包的WEB-INF/lib下是否包含了诸如servlet-api-2.4.jar、el-api-2.2.jar等容器应提供的JAR包。这些包必须删除,它们的scope在Maven中应为provided。 -
检查依赖冲突
:运行
mvn dependency:tree(Maven项目)或查看Gradle依赖树,重点排查与日志相关的包,如tomcat-juli,log4j,logback,slf4j的多个版本共存情况。使用exclusion排除掉不需要的传递依赖。 -
检查TongWeb的lib目录
:对比TongWeb自带的
lib目录下的JAR包版本和你项目依赖的版本。有时,应用依赖了更高版本的库(如Fastjson),而TongWeb自带了一个较低版本,可能导致加载顺序问题。这种情况可以考虑升级TongWeb的库,或将应用依赖的版本降低(需测试兼容性),或者确保应用JAR包中的META-INF/MANIFEST.MF不包含有冲突的类路径定义。 -
终极方案——类加载器调试
:如果以上都不行,可以开启类加载器调试。在
setenv.sh中添加-Dtongweb.classloader.debug=true,重启TongWeb并查看日志,你会看到每个类是从哪个JAR包、由哪个类加载器加载的,这对于定位冲突源极其有效。
3.2
addServlet
异常:Servlet 3.0+ 注解扫描的“脾气”
在部署一个使用Spring Boot(内嵌Servlet容器)改造成传统WAR包的项目时,启动过程中抛出了
javax.servlet.ServletException: Error adding servlet with name 'dispatcherServlet'
之类的异常,堆栈信息指向
StandardContext.addServlet
。
问题根源
:Spring Boot等现代框架大量使用Servlet 3.0的
@WebServlet
、
@WebFilter
、
@WebListener
注解进行声明式注册。TongWeb在扫描这些注解时,如果遇到某些不符合其预期的类(例如,抽象类、接口、或者类路径解析异常),就可能会抛出
addServlet
相关异常,导致整个Web应用启动失败。
解决方案 :
-
检查web.xml与注解的共存
:确保
web.xml中没有重复定义通过注解已经注册的Servlet、Filter或Listener。如果有,要么删除web.xml中的配置,要么删除注解。 -
配置注解扫描排除
:这是最有效的办法。在TongWeb的
conf/server.xml中,找到你的应用对应的<Context>标签,或在其conf/context.xml中进行全局配置,添加<JarScanner>和<JarScanFilter>配置来排除不需要扫描的JAR包。
上面的配置示例排除了所有以<Context> <JarScanner> <JarScanFilter defaultPluggabilityScan="true" defaultTldScan="true" pluggabilityScan="spring-*, mybatis-*" tldScan="false"/> </JarScanner> </Context>spring-和mybatis-开头的JAR包中的注解扫描。你可以根据实际情况调整,例如排除tomcat-embed-*(Spring Boot内嵌Tomcat的包,常引起问题)。 -
关闭注解扫描(激进)
:如果应用完全不使用Servlet 3.0注解,可以在
<Context>中设置<Context metadata-complete="true">。这会让TongWeb忽略所有注解,只依赖web.xml。但这对现代Spring应用通常不适用。
4. 配置文件迁移与适配细节
我们的应用从Tomcat迁移过来,配置文件需要做针对性调整。
4.1
server.xml
与
context.xml
的关键差异
TongWeb的配置文件结构与Tomcat高度相似,但有些默认值和标签属性不同。
-
连接器(Connector)配置
:Tomcat默认的HTTP连接器是
org.apache.coyote.http11.Http11NioProtocol,而TongWeb可能有自己的实现类,如com.tongweb.http11.Http11NioProtocol。直接拷贝Tomcat的server.xml可能导致连接器无法初始化。最稳妥的方式是在TongWeb原版server.xml的基础上,只修改你需要定制的端口、超时时间、压缩等参数,不要替换整个连接器定义。 -
资源定义(Resource)
:数据库连接池(如DBCP)的配置方式基本通用。但需要注意,TongWeb可能对某些资源工厂(Factory)的类名有细微差别。如果遇到
Cannot create resource instance错误,请核对TongWeb文档中推荐的资源工厂类名。 -
Session集群配置
:如果使用Session复制,TongWeb的集群配置标签和属性可能与Tomcat不同,需要参照其管理手册进行配置,不能直接复用Tomcat的
<Cluster>配置。
4.2 日志配置:与Log4j2/Logback的集成
TongWeb默认使用JULI(
java.util.logging
的扩展)进行日志管理。如果你的应用使用Log4j2或Logback,需要正确桥接,避免日志丢失或重复。
推荐方案 :使用SLF4J作为统一门面,并添加对应的桥接器。
-
在应用
WEB-INF/lib中确保有以下JAR包:-
slf4j-api-*.jar -
log4j-slf4j-impl-*.jar(如果使用Log4j2) -
logback-classic-*.jar(如果使用Logback) -
关键
:
jul-to-slf4j-*.jar。这个包负责将TongWeb(及其内部组件)通过JULI输出的日志,路由到SLF4J,进而由你的Log4j2或Logback配置接管。
-
-
在应用启动的早期(如ServletContextListener的
contextInitialized方法中,或Spring Boot的ApplicationRunner中),执行一行代码:
这会将SLF4JBridgeHandler.install();java.util.logging的根处理器(Root Handler)替换为SLF4J的桥接处理器。
实操心得 :务必把
jul-to-slf4j的桥接代码放在应用初始化序列的最前面,否则在桥接生效前,TongWeb内部的一些初始化日志可能会“漏掉”,不利于排查早期启动问题。
5. 部署后监控与性能调优初探
应用成功跑起来只是第一步,稳定运行更需要关注。
5.1 利用TongWeb控制台进行基础监控
TongWeb的管理控制台提供了比Tomcat更丰富的运行时信息。部署后,重点查看:
-
“服务器状态”
:查看线程池(
http-nio-8080)的活动线程数、繁忙线程数、队列大小。如果繁忙线程数持续接近最大值,且队列堆积,说明线程池配置可能偏小。 - “Web应用程序” :确认你的应用Session数量、请求处理时间是否正常。
- “数据源” :如果配置了连接池,在这里可以直观看到活动连接数、空闲连接数,判断连接池配置是否合理。
5.2 针对常见性能问题的参数调整
-
线程池优化
:在
conf/server.xml的<Executor>或<Connector>中调整。-
maxThreads:默认值可能偏小(如150),对于并发要求高的应用,可以适当调大到300-500,具体需压测。 -
acceptCount:等待队列长度。当所有线程都繁忙时,新请求会进入此队列。不宜设置过大,否则响应延迟会很高,通常设置为maxThreads的1/2到1倍。 -
connectionTimeout:网络连接超时,根据网络状况设置,内网可设为20000(20秒)。
-
-
JVM GC优化
:如果监控发现Full GC频繁,在
setenv.sh中调整GC参数。对于JDK 1.8,使用G1垃圾收集器是一个不错的起点:JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=45"
6. 高频问题排查清单与应急锦囊
最后,我将这次遇到的其他一些小问题整理成表,方便快速对照排查。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 应用部署成功,但访问404 |
1. 应用上下文路径(Context Path)配置错误。
2. 默认欢迎文件(如index.jsp)缺失或配置错误。 |
1. 登录TongWeb控制台,查看应用的实际上下文路径。
2. 检查应用
WEB-INF/web.xml
中
<welcome-file-list>
配置。
|
| 静态资源(图片、JS、CSS)无法加载 |
1. 静态资源目录不在Web应用根目录下。
2.
web.xml
中配置了过滤所有请求的Filter(如Spring Security),但未放行静态资源。
|
1. 确认静态资源位于
WEB-INF
目录外。
2. 在Filter配置中添加对静态资源路径的排除规则。 |
| 控制台中文乱码 |
1. JVM启动参数未设置文件编码。
2. 操作系统或终端编码不支持UTF-8。 |
1. 确认
setenv.sh
中已设置
-Dfile.encoding=UTF-8
。
2. 将Linux系统语言环境设置为
zh_CN.UTF-8
。
|
| 应用启动特别慢 |
1. 应用依赖JAR包过多,类加载耗时。
2. 正在执行耗时的初始化操作(如数据库连接、缓存加载)。 3. 熵池不足(Linux下常见)。 |
1. 优化依赖,移除无用JAR包。
2. 将非关键初始化改为异步或懒加载。 3. 安装
haveged
服务:
yum install haveged -y && systemctl start haveged
。
|
| 内存使用率持续升高 |
1. 内存泄漏。
2. JVM堆内存配置过小。 |
1. 结合
-XX:+HeapDumpOnOutOfMemoryError
生成的dump文件,使用MAT或JVisualVM分析。
2. 监控GC日志,调整
-Xmx
大小。
|
这次从Tomcat到TongWeb的迁移,更像是一次对应用服务器底层机制的学习。最大的体会是,面对报错不要慌,尤其是
ClassNotFoundException
、
NoClassDefFoundError
这类问题,十有八九是类加载冲突或依赖问题。优先从“应用自身依赖是否干净”、“与容器提供库是否冲突”这两个角度去查,往往能更快定位。TongWeb作为成熟的国产产品,其稳定性和性能是经得起考验的,前期多花些时间理顺配置和依赖,后期运维会省心很多。如果遇到文档中未提及的怪问题,不妨去其官方社区或知识库看看,通常都能找到线索。

117

被折叠的 条评论
为什么被折叠?



