问题描述
在某个websocket的接口中,我们发现客户端在连接数超过16个时,一次业务处理的速度变得非常慢,完全不符合性能要求。
业务组成
在该接口中,接收的数据主要由两部分组成:业务功能和日志记录功能。其中,日志记录功能是由单独的线程池完成的。
问题定位
- 我们首先定位了问题出现的场景,一共有16个客户端同时连接了服务端,且客户端每秒向服务端发送了100次数据。
- 在复现该问题后,我们查看了服务端的监控,发现CPU和内存消耗很低,初步看来不是资源的问题。
- 为了排除资源的影响因素,我们首先调大了资源,发现该问题并没有好转,可以完全排除资源问题。
- 我们开始查看日志,发现日志中websocket使用的是Tomcat的默认线程,我们调整了tomcat线程池的配置,效果有所提升,但是并未彻底解决问题。
- 到此时,我们开始怀疑是否是代码有问题,我们先review了业务代码,业务代码中并未有问题。
- 然后我们review了日志相关的代码,仍然没有发现问题。
- 最后我们只能使用排除法,我们先注释掉了日志代码,然后发现速度一下子就提升上来了,完全能够满足我们的性能要求。
此时,我们终于找到了问题所在:
- 日志记录中有一个特别耗时的操作;
- 线程池的队列数量配置得很低,线程池的拒绝策略是在线程池已满,队列数量已满的情况下放到调用线程执行。
于是,在客户端发送的数据条数特别多的情况下,线程池不能很好的迅速的处理日志记录功能,日志处理的耗时操作回到了调用线程处理,阻塞了主线程的执行。
处理方案
临时方案:先根据现有资源情况,调高了队列的长度;
长期方案:
方案一:优化日志记录中的耗时操作;
方案二:采用削锋的方式来进行耗时操作。
经验教训
- 慎重使用线程池处理耗时操作,特别是线程池的拒绝策略为放到调用线程处理时;
- 在高并发的情况,慎重使用多线程。