今天一线开发同学反馈批量发送优惠券任务触发发送MQ时报异常,因为用RabbitMQ做异步任务处理,channel数到达了限制,所以不能继续创建,相信大家也遇到过。
项目需要保证消息的可靠性,所以采取了发送确认和消费手动确认机制,导致并发性能下降,从而出现这个问题。
以下内容转载自:https://blog.csdn.net/qq_35374224/article/details/106721801
1.结论
RabbitMQ java客户端在创建连接时,会向服务端发送一个请求,这个请求会获取到服务端的channelMax
值,java客户端会自己进行一个处理,两者都不为0时,会选择一个小的值,如果你没有在rabbitmq.conf文件中修改channel_max的值,那么java客户端会采用默认的2047或更小,这就会导致你明明在客户端连接上配置了channelMax(比如你配置了4095),但依旧会报错,而且web管理页面最大值依旧是2047
2.第一次修改配置不生效
出现这种情况经常伴随着消息丢失,而且消息丢失情况非常严重,达到了百分之二十的丢失率,这个丢失率也会因为并发量、每次消费数量等等配置的不同而变化。
由于项目是基于SpringBoot2.2的,yml暂时无法配置RequestChannelMax的值,这里只能采用直接通过set的方式放入值。
3.分析源码
首先是模拟出报错的场景,然后进入报异常的类。
发现是this.delegate.createChannel();方法返回的是一个空channel对象,进入这个方法看一下。
发现有一个ChannelManage对象,顾名思义,就是一个channel管理器,由它负责创建channel,那么看一下这个对象都有什么值呢?
只截取了部分代码,首先可以看到有一个int类型的channelMax,这个值就是channel的最大值,还有一个构造器,很明显,这个值是通过构造器传进来的,通过容器初始化时打断点进行跟踪,发现此时的channelMax依旧是2047,这也进一步证明了,值的覆盖或者处理发生在这个类调用之前。
进一步跟踪之后,发现在AMQConnection类里的instantiateChannelManager()方法调用了构造器,继续往上追踪。
在AMQConnetion类的start()方法中最终发现了值改变的地方。
this.requestedChannelMax值是我在配置类中配置的4095
connTune.getChannelMax()是2047
也就是说,negotiateChannelMax()方法对这两个值进行了处理,最终选择了2047
最终发现这么一段处理逻辑,如果两个数字都不为0,那么就取最小的,反之取最大的,看到这里是明白做了什么处理,但是还是有一处不明白,2047的值究竟从何处来的?
其实重点是connTune.getChannelMax()这个方法
通过对connTune的追寻,发现了这段处理,debug也证明了确实在这里获取的2047这个值,
其实不管从方法名rpc()还是变量名serverResponse来看,这个都是做了一个请求,那么向谁请求其实很显而易见了,这里向RabbitMQ端做了一个请求,用来索取MQ端的channelMax、frameMax、heartBeat值等等
到现在其实就很明确了,我们只在客户端修改边界值配置是无效的,必须同步修改MQ服务端的配置,也就是rabbitmq.conf文件
其实问题并不大,主要还是不了解MQ的一个客户端连接过程,导致耗费了大量时间。这里还是推荐大家,先用百度搜索,第一页看不到正确解决方案,那就去StackOverflow网站,还不行的话,那就使用终极大法,要么官网逐行看文档,要么走一波源码,也是锻炼自己解决问题的思路和能力。