场景
- 项目JDK版本升级, 从JDK8升至JDK 21,Spring Boot设置为3.3.4
- JDK和Spring Boot在该版本均支持虚拟线程
- 业务属于接收数据, 数据清洗, 存储数据库逻辑, 较轻量化
- 开发机服务, 向SIT环境RabbitMQ发送数据
异常
将原本自定义线程池, 直接替换为 ExecutorService executors = Executors.newVirtualThreadPerTaskExecutor() 后,触发了RabbitMQ的 ChannelMax reached异常
定位问题
尝试业务RabbitMQ的Config限制使用channel数量
1
2
3ConnectionFactory factory = new ConnectionFactory();
factory.setChannelMax(50); // 限制每个连接的最大通道数
Connection connection = factory.newConnection();测试无效
由于是测试状态, 直接发送给Queue, 尝试添加fanout交换机, 不等待response
测试无效查看当前每次都是OOM后, 出现该异常
- Heap Dump检查几乎都是byte[]数据, 定位问题到定时抓取数据业务, 细化方法, 提前提取需要的数据, 将传入值设置为null, 触发尽快GC业务
解决了OOM的问题, 但仍然会出现该错误
- 网络问题
- 由于是本地电脑网络, 与服务器在其他国家, 中途需要代理,所以经常出现异常
1
2
3clean channel shutdown; protocol method: #method<channel.close>(reply-code=406, reply-text=TIMEOUT WAITING FOR ACK, class-id=0, method-id=0)
Shutdown Signal: clean channel shutdown; protocol method: #method<channel.close>(reply-code=406, reply-text=TIMEOUT WAITING FOR ACK, class-id=0, method-id=0)
Received a frame on an unknown channel, ignoring it
推测原因
- 虚拟线程无限创建线程
- 猜测为平台数据频率过高, 且每一个虚拟线程都创建Channel
- 网络稳定性问题, 经常出现channel异常
由于接收端频率高, 因此一直发送消息, 但与MQ网络环境不稳定问题, 导致channel经常还未成功销毁时就新建, 并且是虚拟线程, 会无限创建对应数据的新channel
处理
由于MQ是多个服务公用的, 所以不对 channel_max 进行更改
- 还原为原本的自定义ClientPoolComponent, 通过复用线程减少channel频繁创建频率
- 优化网络环境