RabbitMQ:The channelMax limit is reached. Try later.
RabbitMQ:The channelMax limit is reached. Try later.
这个问题是我当初写项目时遇到的,因为用RabbitMQ做削峰处理,高并发情况下,channel数到达了限制,所以不能继续创建,相信大家也遇到过。
正常来说,这个错误还是比较少见的,只不过项目需要保证消息的可靠性,所以采取了发送确认和消费手动确认机制,导致并发性能下降,从而出现这个问题。、
这里先上结论,方便着急的小伙伴们改bug。
结论:RabbitMQ java客户端在创建连接时,会向服务端发送一个请求,这个请求会获取到服务端的channelMax值,java客户端会自己进行一个处理,两者都不为0时,会选择一个小的值,如果你没有在rabbitmq.conf文件中修改channel_Max的值,那么java客户端会采用默认的2047或更小,这就会导致你明明在客户端连接上配置了channelMax(比如你配置了4095),但依旧会报错,而且web管理页面最大值依旧是2047
第一次修改配置不生效
出现这种情况经常伴随着消息丢失,而且消息丢失情况非常严重,达到了百分之二十的丢失率,这个丢失率也会因为并发量、每次消费数量等等配置的不同而变化。
由于项目是基于SpringBoot2.2的,yml暂时无法配置RequestChannelMax的值,这里只能采用直接通过set的方式放入值。
@Configuration
@Slf4j
public class RabbitMQConfig {@Beanpublic RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {CachingConnectionFactory cachingConnectionFactory = (CachingConnectionFactory) connectionFactory;//这里我明明设置了4095,但是项目运行之后,压测之后,还是会报异常,而且报异常的时候,RabbitMQ //web管理页面上的channel数依旧是2047,不得已只能分析源码了cachingConnectionFactory.getRabbitConnectionFactory().setRequestedChannelMax(4095);final RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);rabbitTemplate.setMessageConverter(jackson2MessageConverter());rabbitTemplate.setMandatory(true);rabbitTemplate.setConfirmCallback((correlationData, b, s) -> {if(!b){log.error("confirmCallBack 发送失败的数据:{}",correlationData);log.error("confirmCallBack 确认情况:{}",b);log.error("confirmCallBack 发送失败的原因:{}",s);}});rabbitTemplate.setReturnCallback((message, i, s, s1, s2) -> {log.error("returnCallBack 消息:{}",message);log.error("returnCallBack 回应码:{}",i);log.error("returnCallBack 回应信息:{}",s);log.error("returnCallBack 交换机:{}",s1);log.error("returnCallBack 路由键:{}",s2);});return rabbitTemplate;}@Beanpublic Jackson2JsonMessageConverter jackson2MessageConverter() {return new Jackson2JsonMessageConverter();}
}
分析源码
首先是模拟出报错的场景,然后进入报异常的类。
发现是this.delegate.createChannel();方法返回的是一个空channel对象,进入这个方法看一下。
发现有一个ChannelManage对象,顾名思义,就是一个channel管理器,由它负责创建channel,那么看一下这个对象都有什么值呢?
public Channel createChannel() throws IOException {this.ensureIsOpen();ChannelManager cm = this._channelManager;if (cm == null) {return null;} else {Channel channel = cm.createChannel(this);this.metricsCollector.newChannel(channel);return channel;}}
只截取了部分代码,首先可以看到有一个int类型的channelMax,这个值就是channel的最大值,还有一个构造器,很明显,这个值是通过构造器传进来的,通过容器初始化时打断点进行跟踪,发现此时的channelMax依旧是2047,这也进一步证明了,值的覆盖或者处理发生在这个类调用之前。
public class ChannelManager {private static final Logger LOGGER = LoggerFactory.getLogger(ChannelManager.class);private final Object monitor;private final Map<Integer, ChannelN> _channelMap;private final IntAllocator channelNumberAllocator;private final ConsumerWorkService workService;private final Set<CountDownLatch> shutdownSet;private final int _channelMax;private ExecutorService shutdownExecutor;private final ThreadFactory threadFactory;private int channelShutdownTimeout;protected final MetricsCollector metricsCollector;public ChannelManager(ConsumerWorkService workService, int channelMax, ThreadFactory threadFactory, MetricsCollector metricsCollector) {this.monitor = new Object();this._channelMap = new HashMap();this.shutdownSet = new HashSet();this.channelShutdownTimeout = 63000;if (channelMax == 0) {channelMax = 65535;}this._channelMax = channelMax;this.channelNumberAllocator = new IntAllocator(1, channelMax);this.workService = workService;this.threadFactory = threadFactory;this.metricsCollector = metricsCollector;}
}
进一步跟踪之后,发现在AMQConnection类里的instantiateChannelManager()方法调用了构造器,继续往上追踪。
protected ChannelManager instantiateChannelManager(int channelMax, ThreadFactory threadFactory) {ChannelManager result = new ChannelManager(this._workService, channelMax, threadFactory, this.metricsCollector);this.configureChannelManager(result);return result;}
在AMQConnetion类的start()方法中最终发现了值改变的地方。
this.requestedChannelMax值是我在配置类中配置的4095
connTune.getChannelMax()是2047
也就是说,negotiateChannelMax()方法对这两个值进行了处理,最终选择了2047
int channelMax = this.negotiateChannelMax(this.requestedChannelMax, connTune.getChannelMax());this._channelManager = this.instantiateChannelManager(channelMax, this.threadFactory);
最终发现这么一段处理逻辑,如果两个数字都不为0,那么就取最小的,反之取最大的,看到这里是明白做了什么处理,但是还是有一处不明白,2047的值究竟从何处来的?
protected int negotiateChannelMax(int requestedChannelMax, int serverMax) {return negotiatedMaxValue(requestedChannelMax, serverMax);}private static int negotiatedMaxValue(int clientValue, int serverValue) {return clientValue != 0 && serverValue != 0 ? Math.min(clientValue, serverValue) : Math.max(clientValue, serverValue);}
其实重点是connTune.getChannelMax()这个方法
int channelMax = this.negotiateChannelMax(this.requestedChannelMax, connTune.getChannelMax());
通过对connTune的追寻,发现了这段处理,debug也证明了确实在这里获取的2047这个值,
其实不管从方法名rpc()还是变量名serverResponse来看,这个都是做了一个请求,那么向谁请求其实很显而易见了,这里向RabbitMQ端做了一个请求,用来索取MQ端的channelMax、frameMax、heartBeat值等等
Tune connTune = null;try {......try {Method serverResponse = this._channel0.rpc((Method)method, this.handshakeTimeout / 2).getMethod();if (serverResponse instanceof Tune) {connTune = (Tune)serverResponse;}......
到现在其实就很明确了,我们只在客户端修改边界值配置是无效的,必须同步修改MQ服务端的配置,也就是rabbitmq.conf文件
## Set the max permissible number of channels per connection.
## 0 means "no limit".
##在配置文件中,输入以下参数和自己想要设置的值即可,如果用不到2047,那就不用配置
# channel_max = 128
其实问题并不大,主要还是不了解MQ的一个客户端连接过程,导致耗费了大量时间。
这里还是推荐大家,先用百度搜索,第一页看不到正确解决方案,那就去StackOverflow网站,还不行的话,那就使用终极大法,要么官网逐行看文档,要么走一波源码,也是锻炼自己解决问题的思路和能力。
RabbitMQ:The channelMax limit is reached. Try later.相关推荐
- 解决RabbitMQ的The channelMax limit is reached. Try later.
The channelMax limit is reached. Try later.顾名思义就是channel达到数量限制 查看源码得出 大概意思就是: 默认最大通道数:2047,因为它在服务器端是 ...
- docker RabbitMQ:修改Channel limit
RabbitMQ:The channelMax limit is reached. Try later. rabbitmq 默认 最大链接默认是2047访问量过大会导致数据丢失 复制 docker 容 ...
- 解决使用RabbitTemplate操作RabbitMQ,发生The channelMax limit is reached. Try later.问题
解决使用RabbitTemplate操作RabbitMQ,发生The channelMax limit is reached. Try later.问题 参考文章: (1)解决使用RabbitTemp ...
- 代码层面解决 The channelMax limit is reached. Try later.
该问题也是非常的严重,没有合适处理即导致消息丢失: 解决方案 设置连接工厂配置 CachingConnectionFactory connectionFactory = new CachingConn ...
- java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得
该文章出自:http://www.cnblogs.com/hucn/p/3572384.html 分析工具:http://www.blogjava.net/jjshcc/archive/2014/03 ...
- java.lang.OutOfMemoryError:GC overhead limit exceeded
我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...
- Eclipse报错:gc overhead limit exceeded eclipse
Eclipse报错:gc overhead limit exceeded eclipse 原因是Eclipse默认配置内存太小需要更改Eclipse安装文件夹下的eclipse.ini文件. 1.打开 ...
- RabbitMQ:惰性队列
RabbitMQ [第一章]RabbitMQ:从认识MQ到安装,学习消息模型等 [第二章]RabbitMQ:常见消息模型 [第三章]RabbitMQ:生产者消息确认.消息持久化.消费者消息确认.消费失 ...
- ORA-20000:ORU-10027:buffer overflow,limit of 2000 bytes
在执行项目中某个存储过程中报如下异常:ORA-20000:ORU-10027:buffer overflow,limit of 2000 bytes. 异常信息: 解决方法: 1. set serve ...
最新文章
- 使用WinPcap和libpcap类库读写pcap文件(001)开发环境配置
- array_filter php5.4 php5.5,PHP 5.4:我可以使用filter_var_array()将多个标志与过滤器一起使用吗?...
- “Razor” – a new view engine for ASP.NET
- Android Handler的原理
- wifi一阵一阵卡_家里wifi总是过一会就卡一下然后又好了
- hprose出现500: Internal Server Error
- IBASE component valid to field
- _M_invoke(_Index_tuple_Indices...)
- git使用的基本流程_git命令的基本使用
- LKT系列加密芯片DES加解密以及OpenSSL DES接口实现加解密
- Python函数基础3 函数对象、名称空间、装饰器
- cas4.0 mysql_【SSO单点系列】:CAS4.0 CAS整合SpringMVC+MyBatis实现数据库校验(04)
- 如何选购一款好的人事档案管理系统
- Zookeeper 入门指北
- [OHOS ERROR] clang not found, install it please
- python爬虫学习教程,短短25行代码批量下载豆瓣妹子图片
- Katana中设置全局变量
- 第三届阿里巴巴全球数学竞赛落下帷幕,这届90后属实优秀!北大恐成最大赢家!
- 计算机视觉处理的三大任务(待续)
- sa-token使用简单使用
热门文章
- JavaScript(5)-内置对象
- hive创建角色并赋权
- Spring Boot 注入接口 @Autowired interface
- 更新win11后vmware出错,VMware Workstation 不可恢复错误: (vcpu-0)
- dct变换的主要优点有哪些_【WIX维克斯】自动变速器AT、AMT、CVT、DCT谁更平顺
- 邮件退信“Remote Server returned '420 4.2.0 Recipient deferred because there is no Mdb'”
- idenet 学习记录:bili
- AMOLED 显示面板 Mura 缺陷
- 【微信小游戏】游戏性能检测
- 【贝叶斯分析①】Metropolis-Hastings算法理解和简单实现