
1. 从一句“TongWeb卡卡卡”说起性能问题的真实场景如果你是个经常跟国产中间件打交道的运维或者开发大概率经历过这种场面项目上线跑了两三周某天业务方突然在群里连发几条消息——“系统好卡”“页面一直转圈”“接口又超时了”紧接着你手忙脚乱地登录服务器top一看CPU 飙到百分之两三百TongWeb 进程占了七八个 G 内存日志里全是连接超时和线程池打满的报错。我第一次被 TongWeb 卡顿问题折腾是在一个政务类系统上。那套系统部署了七八个 Web 应用平时并发不高撑死几十个人同时在线。结果某天晚上跑完数据迁移任务第二天早上所有人都反馈系统“卡成 PPT”点一个菜单要等十几秒部分接口直接 502。我当时第一反应是数据库出了问题结果一查数据库负载完全正常反而是 TongWeb 所在机器 swap 占用一路飙升Java 进程 GC 日志里 Full GC 频率高得吓人。后来排查了一圈才发现问题根源压根不在某个单一环节而是 JVM 参数、线程池配置、连接池管理、部署包结构等多个因素叠加后的综合结果。这也是我写这篇内容的原因TongWeb 卡顿不是“把内存调大一点”就能解决的它是一个需要从全局视角去定位、分析和优化的系统性问题。这篇文章会从我在实际运维中遇到的几种典型卡顿场景出发逐步拆解 TongWeb 性能优化的关键环节包括 JVM 参数调优、线程池与连接池配置、部署包瘦身、日志与监控、常见问题排查等。不管你是刚接触 TongWeb 的新手还是已经被生产环境折磨过几轮的“老运维”这篇文章都会给你一套可以落地执行的排查思路和优化方案。先说明一点TongWeb 作为国产化应用服务器在信创环境下使用非常广泛很多单位的核心业务系统都跑在它上面。正因为如此TongWeb 的性能问题往往不是孤立的它和操作系统、JDK 版本、数据库驱动、应用框架等多个层面都有深度关联。下面我会把这些影响因素拆开来讲力求让你看完之后能形成自己的排查方法论而不是每次遇到问题都只能瞎猜或者重启。2. TongWeb 卡顿的核心原因拆解2.1 内存管理JVM 堆内存与 GC 策略的影响TongWeb 本质上是一个 Java 应用服务器部署在它上面的所有应用都运行在同一个 JVM 进程里。所以排查 TongWeb 卡顿第一个要盯紧的就是 JVM 的内存配置是否合理。很多默认安装的 TongWebJVM 初始堆大小和最大堆大小都设置得非常保守。我见过不少生产环境的 TongWeb-Xms和-Xmx都是默认的 1G而机器本身有 16G 甚至 32G 内存。这种情况下一旦并发量稍微上来一点JVM 堆内存就会迅速吃紧频繁触发 Full GC整个应用就会出现明显停顿。有个客户的环境就是这样TongWeb 堆内存设置的是-Xmx2048m但实际业务并发峰值时堆内存使用量能到 1.8G几乎贴着上限跑。查看 GC 日志Full GC 平均每几分钟触发一次每次停顿时间在 2 到 5 秒。对于普通的 Web 后台管理系统来说用户体感就是“卡得不行”——点一下按钮页面要转好几秒才出结果。我给的建议是TongWeb 的 JVM 堆内存设置要根据机器的物理内存和实际业务量来评估不能照抄默认值。一般来说物理内存 8G 的机器-Xms和-Xmx设置在 2G 到 3G 之间物理内存 16G 的机器-Xms和-Xmx设置在 4G 到 6G 之间物理内存 32G 及以上的机器-Xms和-Xmx设置在 8G 到 12G 之间但这里有个非常重要的提醒JVM 堆内存不是越大越好。堆内存过大GC 扫描和对象迁移的时间会变长反而可能导致更长时间的停顿。而且堆内存设置过大会侵占操作系统内存留给文件缓存和线程栈的空间就少了同样会引发性能问题。我见过有人把 16G 内存的机器直接给 TongWeb 分配 12G 堆内存结果操作系统可用的物理内存只剩 4GPage Cache 严重不足磁盘 IO 频繁应用反而更慢了。除了堆内存大小GC 策略的选择也很关键。不同 JDK 版本下的 TongWeb 默认 GC 策略不太一样。拿 JDK8 来说默认通常使用 Parallel Scavenge 和 Parallel Old 的组合这套组合的优点是吞吐量高适合后台批量处理场景但坏处是在 GC 停顿时间上不够友好尤其是在堆内存偏大的时候一次 Full GC 可能停顿好几秒。如果业务对响应时间比较敏感可以考虑切换为 G1 垃圾收集器配合-XX:MaxGCPauseMillis参数控制停顿目标。不过 G1 也不是万能药它在 JDK8u191 之前有一些已知的性能问题比如 Full GC 退化为串行模式升级到新版本 JDK 更稳妥。2.2 线程池与连接池并发瓶颈的隐形杀手TongWeb 的卡顿很大一部分原因出在并发资源的管理上。这里主要涉及两个池子一个是 HTTP 线程池也就是处理请求的线程池另一个是数据源连接池也就是应用访问数据库时复用的连接池。HTTP 线程池配置得太小比如默认的 100 或者 200在高并发场景下队列里会堆积大量请求每个请求都在等待空闲线程执行表现出来就是接口响应变慢、页面点击无响应。而线程池配置得太大又会造成线程切换的开销增加不一定能提升性能反而可能拖垮 CPU。我遇到过一个典型案例某系统原本跑在 Spring Boot 内嵌 Tomcat 上后来因为信创要求迁移到 TongWeb但是应用代码和配置基本没动仍然按照 Tomcat 的默认线程池参数来用。结果一到业务高峰期大量请求排队等待线程数据库连接池也被全部占满最终整个应用就像死了一样连健康检查接口都返回超时。后来调整了 TongWeb 的 HTTP 线程池参数同时优化了数据库连接池系统才恢复正常。数据源连接池的问题更是常见。很多开发者习惯套用一个固定数值比如initialSize5, maxActive20这在并发量低的内部系统里没有问题但一旦业务量上来数据库连接不够用应用就会在获取连接这一步上阻塞整个请求链路被拖慢。而且连接池的最大连接数设置得过大会对数据库造成压力过小则满足不了并发需求这个平衡要根据实际业务和数据库承载能力来评估。针对连接池我一般会建议设置合理的maxActive、minIdle、maxWait参数。比如一个常规的 OLTP 系统maxActive设置在 50 到 100 是比较常见的做法maxWait设置在 5000 毫秒左右这样获取连接的等待时间不至于太长也不至于无限期挂死。2.3 部署包与应用代码慢 SQL 和循环调用的隐形消耗TongWeb 卡顿还经常和部署在它里面的应用代码有关。这不是 TongWeb 自身的问题而是部署在其上的应用写得不够优化导致整个中间件被拖累。最常见的场景就是慢 SQL。我遇到过某个报表查询接口单次查询就要跑十几秒而且这个接口还经常被前端循环调用比如一个页面发十几次请求每次请求都要去查那张大表。这种情况下数据库连接被长时间占用连接池很快耗尽其他正常的业务请求也被阻塞。最终用户感知到的就是“TongWeb 卡了”但实际上根因是应用里的 SQL 没有建索引、查询条件没写好。另一种常见问题是内存泄漏。比如使用了静态集合缓存数据但没有及时清理或者使用 ThreadLocal 后没有调用 remove或者加载了大量重复的类导致 Metaspace 空间持续增长。这类问题初期可能没什么表现但随着运行时间拉长内存占用越来越高GC 压力越来越大最后导致 JVM 频繁 Full GC 甚至 OOM。还有一点大家容易忽略就是部署包本身的结构也会影响性能。有些应用打出来的 war 包体积特别大动辄几百 MB里面塞满了各种依赖 jar 包、静态资源、模板文件。TongWeb 在部署时会扫描这些类类加载和资源定位的开销都会增加。而且大体积的 war 包在应用启动和热部署时也更慢运维上的体验非常差。2.4 操作系统与硬件层被忽视的外部因素TongWeb 跑在操作系统之上操作系统层的配置也会直接影响它的性能。比如文件句柄数限制、网络连接数限制、TCP 参数配置等这些在并发量上来之后都会成为瓶颈。我记得有一次排查发现 TongWeb 的日志里出现了大量 “Too many open files” 异常。当时挺纳闷以为是连接泄漏结果查了一圈发现系统默认的ulimit -n只有 1024生产环境里 TongWeb 需要打开的文件句柄包括 socket 连接、日志文件、类文件等数量很快就超过这个限制。这种配置在开发环境可能永远不会暴露问题但一旦部署到生产环境并发稍高就会出现连接失败或 IO 阻塞很容易被误认为是 TongWeb 本身性能有问题。另外CPU 核数和内存大小是决定 TongWeb 性能上限的重要因素。并不是所有的卡顿都能靠调优解决有时候基础资源就不够。比如一台只有 2 核 4G 的机器硬要跑三四个 Web 应用JVM 参数怎么优化都扛不住这时候合理的做法是扩容服务器或者把应用拆分到多个实例上负载均衡。3. 治本之策TongWeb 性能优化的实操路径3.1 合理配置 JVM 参数从源头减少 GC 压力不管 TongWeb 跑在什么环境下第一件该做的事就是根据物理资源合理配置 JVM 参数。我的建议是不要用 TongWeb 的默认参数而是根据实际业务场景做评估和调整。以我接手过的一个项目为例生产环境物理机配置是 16G 内存、8 核 CPU上面部署了 2 个 TongWeb 实例。原来的 JVM 参数是-Xms1024m -Xmx1024m -XX:PermSize256m -XX:MaxPermSize256m。这个配置有两个明显问题第一堆内存只有 1G对于两个实例来说明显偏小第二PermSize这种参数在 JDK8 里已经被 Metaspace 取代配置了也基本不生效元空间大小如果没有单独指定会随着类加载不断增加。我调整后的参数如下-Xms4096m -Xmx4096m -XX:MetaspaceSize512m -XX:MaxMetaspaceSize512m -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/tongweb/logs逐个解释一下每个参数的意义-Xms和-Xmx设置为相同值避免 JVM 在运行时动态调整堆大小带来的额外开销同时保证 JVM 启动时就分配足够的堆内存。MetaspaceSize和MaxMetaspaceSize指定元空间大小防止应用加载过多类导致元空间无限增长最终挤占系统内存。UseG1GC启用 G1 垃圾收集器适合堆内存较大的场景可以更好地控制 GC 停顿时间。MaxGCPauseMillis设置 GC 停顿的目标时间比如 200 毫秒让 JVM 尽量在这个目标内完成垃圾回收。HeapDumpOnOutOfMemoryError和HeapDumpPath让 JVM 在发生 OOM 时自动保存堆转储文件方便事后分析内存问题。调整完参数之后我观察了一段时间Full GC 频率从原来的几分钟一次降到基本不触发接口响应时间也从原来的 2 到 3 秒降到几百毫秒。效果非常明显。3.2 调整 TongWeb 线程池与数据库连接池参数TongWeb 的线程池配置主要通过tongweb.xml配置文件或者管理控制台进行调整。具体参数包括核心线程数、最大线程数、队列大小、线程空闲超时时间等。对于普通应用我建议最大线程数设置在 200 到 500 之间。如果并发量特别大可以适当调高但要注意一点线程数超过 CPU 核心数太多并不会带来性能提升反而会因为线程频繁切换增加 CPU 开销得不偿失。合理的目标是让线程数既能覆盖业务并发峰值又不会因为线程过多导致调度开销过大。比如一个典型的后台管理系统日常并发可能只有几十即使高峰期也就一两百那最大线程数设置在 300 左右就够用了。如果系统是面向公众的互联网应用并发量波动大可以适当放宽到 500同时配合队列长度的设置来削峰填谷。数据库连接池方面如果应用使用的是 Druid 连接池可以通过druid.properties或 Spring 配置文件来调整。一个比较稳妥的配置参考如下initialSize5 minIdle5 maxActive50 maxWait5000 timeBetweenEvictionRunsMillis60000 minEvictableIdleTimeMillis300000 validationQuerySELECT 1这里几个关键参数的解释maxActive控制连接池最大连接数设置过大会增加数据库压力过小则满足不了并发需求。minIdle控制连接池最小空闲连接数避免连接频繁创建和销毁导致性能损耗。maxWait控制获取连接的最大等待时间避免请求无限挂起通常设置为 5000 毫秒比较合理。timeBetweenEvictionRunsMillis和minEvictableIdleTimeMillis控制连接池的回收和空闲检查频率防止连接长期空闲被数据库服务端断开导致获取连接报错。这里有个小技巧maxActive并不需要设置得特别大。我曾经把一个系统的maxActive从 200 调整到 50配合合理的initialSize和minIdle数据库压力反而降下来了应用响应时间也没有变慢。因为很多请求的 SQL 执行时间本身很短连接池的大小只要能刚好覆盖并发峰值即可设置过大的连接池反而浪费数据库资源。3.3 部署包瘦身与依赖收敛在 TongWeb 中部署 war 包时war 包体积和依赖的复杂程度直接影响应用的启动速度和运行效率。我见过很多 war 包动辄四五百 MB里面什么 jar 都往里塞这种习惯非常不好轻则拖慢启动速度重则引起依赖冲突和类加载问题。优化方向主要有两个。第一剔除重复和冗余的依赖。Maven 项目里使用dependency:tree查看依赖树检查是否存在重复版本排除不必要传递依赖。常见做法是在pom.xml中使用exclusions标签排除无用依赖或者把 Servlet API、JSP API 等中间件平台已经提供的依赖设置为provided作用域不在 war 包里重复打包。举个例子一个使用 Spring Boot 构建的项目如果把 spring-boot-starter-tomcat 一起打进去war 包会额外带上整套 Tomcat 内嵌依赖而这些依赖在部署到 TongWeb 时根本用不到。正确的做法是把 spring-boot-starter-tomcat 的 scope 设置为 provided让外部容器来提供 Servlet 运行环境。第二将静态资源分离。如果一个应用包含了大量图片、CSS、JavaScript 等静态资源可以考虑把这些资源放到独立的 Web 服务器或 CDN 上让 TongWeb 只处理动态请求。这样不仅减轻了 TongWeb 的 IO 压力也缩短了单个请求的响应时间因为静态资源不需要经过 Java 容器处理。我处理过的一个案例一个综合管理平台的 war 包从原来的 300MB 瘦身到 60MB应用启动时间从原来的 3 分多钟缩减到不到 1 分钟。虽然对运行时卡顿的帮助没有 JVM 参数调优那么直接但部署和运维体验的提升是非常显著的。3.4 日志配置与持续监控还有一个容易被忽略的点就是日志。很多应用在 TongWeb 里配置了过多的日志输出尤其是System.out.println()或者 DEBUG 级别的日志在并发量高的时候会产生大量 IO 开销直接拖慢应用。TongWeb 默认的日志输出目录、日志级别、滚动策略都需要按需调整。比如生产环境建议将日志级别设置为WARN或ERROR同时开启按天或按小时的日志滚动策略避免单个日志文件无限增长导致磁盘空间被撑满。监控方面TongWeb 本身提供了管理控制台可以通过它查看当前活跃的连接数、线程池使用情况、内存占用等基础指标。但我更建议接入独立的监控系统比如使用 JMX 导出指标到 Prometheus/Grafana或者直接使用 SkyWalking、Arthas 等工具对 TongWeb 的 JVM 状态和应用调用链进行持续监控。有了监控数据定位卡顿问题就不再是“盲人摸象”了而是有据可查的定向排障。4. TongWeb 卡顿常见问题排查实录4.1 现象系统突然无响应CPU 飙高这是 TongWeb 是最常见的一种卡顿表现。遇到这种情况我的排查顺序是先看进程状态用top -Hp pid查看 TongWeb 进程内各线程的 CPU 占用情况找出是哪个线程在疯狂消耗 CPU。如果某个线程 CPU 占用特别高用jstack pid thread_dump.txt导出线程栈分析对应线程正在执行什么方法。如果确认是 GC 问题用jstat -gcutil pid 1000观察 GC 频率或者直接查看 GC 日志文件。如果堆内存吃紧用jmap -dump:formatb,fileheap.hprof pid导出堆转储再使用 MAT 或 VisualVM 分析对象占用情况。有一次我通过线程栈发现某个线程一直在执行一段正则表达式的匹配操作单次匹配耗时几十秒导致这个线程一直不释放线程池被这个请求占满其他所有请求都在排队等待。定位到问题后把正则表达式改为预编译模式卡顿问题直接消失。这种问题如果不是通过线程栈分析纯粹靠猜的话可能几天都找不到真正的原因。4.2 现象频繁 Full GC应用停顿明显Full GC 频繁通常意味着堆内存不足或者有大对象在持续分配。可以通过 GC 日志确认grep Full GC /data/tongweb/logs/gc.log | head -20如果 Full GC 日志显示 Old 区几乎占满说明可能存在内存泄漏或者堆设置偏小。这时有两种处理思路调整 JVM 参数适当增大-Xmx。用 MAT 分析堆转储文件看看是哪个对象占据了大部分内存。有一个关键点需要注意调整-Xmx只能暂时缓解症状如果存在内存泄漏调大堆内存只是把 OOM 的时间往后推迟问题迟早还会再次出现。必须找到泄漏的具体对象和引用链才能从根本上解决问题。4.3 现象登录管理控制台报错或超时很多人在使用 TongWeb 时都会遇到管理控制台登录问题。TongWeb 的管理控制台默认端口通常是localhost:9060或localhost:8000具体端口取决于版本和安装时的配置。如果控制台访问卡顿或超时可以先检查防火墙是否放行对应端口然后检查控制台对应的后台服务是否正常必要时重启管理控制台模块。这里顺便提一下TongWeb 的默认用户名和密码通常是thanos/thanos123.com但生产环境强烈建议修改默认密码避免安全风险。除了账号安全还要注意控制台本身的访问权限最好设置 IP 白名单只允许管理员网段访问。4.4 现象部署 war 包后应用长时间不启动这种情况在 war 包特别大或者应用类特别多时经常出现。可以先检查 TongWeb 的logs目录下的启动日志看是卡在哪个阶段。如果是卡在扫描和加载类阶段可以考虑在tongweb.xml中配置扫描路径只扫描必要的包或者使用provided依赖省去对 Servlet API 等类的重复扫描。如果 TongWeb 是集群部署还要注意各节点的 Node 配置和会话保持策略避免某个节点负载过高而其他节点处于空闲状态。负载不均也是造成“某个节点很卡”的重要原因。5. 写在最后TongWeb 优化的几点心得TongWeb 的卡顿问题排查和优化不是一次性的工作而是一个持续迭代的过程。我在实际工作中总结了几点体会这里分享给大家。第一不要迷信默认配置。TongWeb 的默认配置是为了通用场景设计的不一定适合你的业务。务必根据实际的业务量、并发量、机器配置来调整并且每次调整后要做好记录和对比观察调整前后的性能变化。第二性能优化要循序渐进。不要一上来就同时调整十几个参数每次只改一个变量观察一段时间再决定下一步怎么做。否则出了问题你根本不知道是哪个参数导致的。第三监控先行。如果你对系统的运行状态一无所知优化就是盲人摸象。先把监控搭起来让数据告诉你问题在哪里再动手去优化。好的监控体系不仅能在故障发生时帮你快速定位问题还能在日常运行中提前发现隐患。第四应用代码的优化和中间件的优化同等重要。很多时候TongWeb 只是“背锅侠”真正的瓶颈在应用代码。遇到卡顿先看应用层有没有慢 SQL、内存泄漏、线程阻塞再看中间件配置别急着把责任推给 TongWeb。最后如果实在排查不出来可以查看 TongWeb 的官方文档或者联系东方通的技术支持。毕竟 TongWeb 更新迭代很快有些性能问题在新版本中已经有了优化升级版本也是一种解决思路。比如从较老的 TongWeb 6.x 升级到 7.x性能和稳定性都有明显改善很多已知的 bug 也会修复。我在实际运维中还有一个感受TongWeb 卡顿问题的排查最忌讳的就是“头痛医头脚痛医脚”。今天调大内存明天增加连接数看上去每个操作都在优化但如果没有系统性的思路问题会在你意想不到的地方再次爆发。真正有效的做法是一步步建立自己的排查链路——从操作系统到 JVM从中间件到应用代码每一层都搞清楚再复杂的问题也能拆解成一个个可以定位和解决的子问题。希望这篇内容能帮你从“TongWeb 好卡”的焦虑中解脱出来学会系统性地分析和解决问题。以后再遇到卡顿不要只顾着在群里发“卡卡卡”按照上面的思路一步步排查问题大概率能在你掌控之中解决掉。