近期需要对一 Web 工程做一次简单的压力测试,需满足上千用户同时使用系统的要求,这个工程是基于 jetty embedded 然后打包成 war 来运行的,团队的小伙伴是这样在线更新代码的:
1. 在 IDE 里修改了某个或某几个类、或 jsp、css 等页面 2. 由于线上系统基于 war 启动,第一次启动后文件被解压到 work 目录下 3. 于是小伙伴将 IDE 的编译路径(如 Eclipse 下为 target/class 等)下的修改文件通过 sftp 直接覆盖线上对应目录下的文件 4. 搞定,执行重启脚本 简单几步就搞定一个需求,调整起来是多么的敏捷啊(多么的不规范)! 在压测过程,发现压 300、500、1000 登录用户后(登录后对一些业务模块会发起几十个后端请求),在压测过程再人工登录系统就非常缓慢,连打开登录页也非常卡顿。 第一次尝试: 排查进程、负载、网络都 TM 正常,这就奇怪了,于是猜测是数据库等后端资源限制导致?那么就调整了数据库连接数、数据库服务端连接限制等,无解; 第二次尝试: Dump 堆栈、监控内存使用、GC 情况、top -Hp pid 来观察消耗资源的线程,一切似乎都 TM 正常,无解; 第三次尝试: 可能是 jetty 的线程池有问题,于是调整了线程池大小,甚至换 JDK 自带线程池,无解; 第四次尝试: 只压一个场景,且将后端业务逻辑(如数据库访问、ES连接等)注解,正常了,压上去了,瞬间搞定,什么情况?于是将业务逻辑场景用 Thread.sleep(3000) 来模拟,尼玛,又不行。 第五次尝试: 没办法、仔细点,静下心来分析线程堆栈,看到底在干些啥,是否存在死锁或锁竞争。经过多轮排查,也没发现线程本身存在什么问题。于是做下统计,看看多少线程在运行中,多少在等待、多少在阻塞,几轮下来,发现不对劲,运行中线程(线程名为:qtpxxx-xx)怎么老在 245 左右,怎么就上不去,于是在压测过程 watch 'ss -n | grep :8080 -c' 也发现上不去。ok!确定是线程上不去导致,线程池出问题了,但不对,明明调整了 jetty 线程池,而且多次调整,各自尺寸大小都试遍了啊! 最后确定问题根源: 在启动 jetty embedded 的类里加些信息打印,启动后日志文件和控制台都无信息打印。居然是启动类问题,天啊!于是想起来了,war 启动是认 META-INF 里的启动类,而我却看到小伙伴按照套路是去覆盖 work 下的那个启动类。于是让小伙伴将启动类覆盖到 war/META-INF 下的类,并重启,这时看到了打印信息,压测搞定,页面响应非常快,也恢复正常了。 后记: 虽然问题解决了,是 jetty embedded 线程池未设置而采用了默认值导致,调整即可。但修正了 war 包后,war 的文件日期发生了变化,所以在启动后重新解压并覆盖了 work 下的文件。之前一直采用覆盖 work 下的文件方式,导致 work 下的文件与 war 里的文件不一致,这次修改了 war 那个启动类导致重新解压了旧文件又覆盖回来,回到当初第一次启动状态,头都大了,马上重新拉分支将最新代码再覆盖回去,好大的风险啊! 教训: 不规范的操作导致问题频频,打包上线过程必须要慎重,要按套路出牌,工程规范很重要,负责后果自负。