TongWeb部署实战:从Tomcat迁移的典型问题与解决方案

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的类加载器在处理这些路径时,策略可能更严格。

我们的解决方案

  1. 首选方案(推荐) :升级JDK到1.8的最新小版本(如1.8.0_381),并确保TongWeb版本与之匹配。这能解决大部分因JDK内部模块化引起的兼容性问题。
  2. 临时方案 :如果无法升级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 )时,由于类加载器空间隔离或版本不一致,导致链接失败。

排查与解决步骤

  1. 检查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
  2. 检查依赖冲突 :运行 mvn dependency:tree (Maven项目)或查看Gradle依赖树,重点排查与日志相关的包,如 tomcat-juli , log4j , logback , slf4j 的多个版本共存情况。使用 exclusion 排除掉不需要的传递依赖。
  3. 检查TongWeb的lib目录 :对比TongWeb自带的 lib 目录下的JAR包版本和你项目依赖的版本。有时,应用依赖了更高版本的库(如Fastjson),而TongWeb自带了一个较低版本,可能导致加载顺序问题。这种情况可以考虑升级TongWeb的库,或将应用依赖的版本降低(需测试兼容性),或者确保应用JAR包中的 META-INF/MANIFEST.MF 不包含有冲突的类路径定义。
  4. 终极方案——类加载器调试 :如果以上都不行,可以开启类加载器调试。在 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应用启动失败。

解决方案

  1. 检查web.xml与注解的共存 :确保 web.xml 中没有重复定义通过注解已经注册的Servlet、Filter或Listener。如果有,要么删除 web.xml 中的配置,要么删除注解。
  2. 配置注解扫描排除 :这是最有效的办法。在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的包,常引起问题)。
  3. 关闭注解扫描(激进) :如果应用完全不使用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作为统一门面,并添加对应的桥接器。

  1. 在应用 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配置接管。
  2. 在应用启动的早期(如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作为成熟的国产产品,其稳定性和性能是经得起考验的,前期多花些时间理顺配置和依赖,后期运维会省心很多。如果遇到文档中未提及的怪问题,不妨去其官方社区或知识库看看,通常都能找到线索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值