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.相关推荐

  1. 解决RabbitMQ的The channelMax limit is reached. Try later.

    The channelMax limit is reached. Try later.顾名思义就是channel达到数量限制 查看源码得出 大概意思就是: 默认最大通道数:2047,因为它在服务器端是 ...

  2. docker RabbitMQ:修改Channel limit

    RabbitMQ:The channelMax limit is reached. Try later. rabbitmq 默认 最大链接默认是2047访问量过大会导致数据丢失 复制 docker 容 ...

  3. 解决使用RabbitTemplate操作RabbitMQ,发生The channelMax limit is reached. Try later.问题

    解决使用RabbitTemplate操作RabbitMQ,发生The channelMax limit is reached. Try later.问题 参考文章: (1)解决使用RabbitTemp ...

  4. 代码层面解决 The channelMax limit is reached. Try later.

    该问题也是非常的严重,没有合适处理即导致消息丢失: 解决方案 设置连接工厂配置 CachingConnectionFactory connectionFactory = new CachingConn ...

  5. java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得

    该文章出自:http://www.cnblogs.com/hucn/p/3572384.html 分析工具:http://www.blogjava.net/jjshcc/archive/2014/03 ...

  6. java.lang.OutOfMemoryError:GC overhead limit exceeded

    我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...

  7. Eclipse报错:gc overhead limit exceeded eclipse

    Eclipse报错:gc overhead limit exceeded eclipse 原因是Eclipse默认配置内存太小需要更改Eclipse安装文件夹下的eclipse.ini文件. 1.打开 ...

  8. RabbitMQ:惰性队列

    RabbitMQ [第一章]RabbitMQ:从认识MQ到安装,学习消息模型等 [第二章]RabbitMQ:常见消息模型 [第三章]RabbitMQ:生产者消息确认.消息持久化.消费者消息确认.消费失 ...

  9. ORA-20000:ORU-10027:buffer overflow,limit of 2000 bytes

    在执行项目中某个存储过程中报如下异常:ORA-20000:ORU-10027:buffer overflow,limit of 2000 bytes. 异常信息: 解决方法: 1. set serve ...

最新文章

  1. 使用WinPcap和libpcap类库读写pcap文件(001)开发环境配置
  2. array_filter php5.4 php5.5,PHP 5.4:我可以使用filter_var_array()将多个标志与过滤器一起使用吗?...
  3. “Razor” – a new view engine for ASP.NET
  4. Android Handler的原理
  5. wifi一阵一阵卡_家里wifi总是过一会就卡一下然后又好了
  6. hprose出现500: Internal Server Error
  7. IBASE component valid to field
  8. _M_invoke(_Index_tuple_Indices...)
  9. git使用的基本流程_git命令的基本使用
  10. LKT系列加密芯片DES加解密以及OpenSSL DES接口实现加解密
  11. Python函数基础3 函数对象、名称空间、装饰器
  12. cas4.0 mysql_【SSO单点系列】:CAS4.0 CAS整合SpringMVC+MyBatis实现数据库校验(04)
  13. 如何选购一款好的人事档案管理系统
  14. Zookeeper 入门指北
  15. [OHOS ERROR] clang not found, install it please
  16. python爬虫学习教程,短短25行代码批量下载豆瓣妹子图片
  17. Katana中设置全局变量
  18. 第三届阿里巴巴全球数学竞赛落下帷幕,这届90后属实优秀!北大恐成最大赢家!
  19. 计算机视觉处理的三大任务(待续)
  20. sa-token使用简单使用

热门文章

  1. JavaScript(5)-内置对象
  2. hive创建角色并赋权
  3. Spring Boot 注入接口 @Autowired interface
  4. 更新win11后vmware出错,VMware Workstation 不可恢复错误: (vcpu-0)
  5. dct变换的主要优点有哪些_【WIX维克斯】自动变速器AT、AMT、CVT、DCT谁更平顺
  6. 邮件退信“Remote Server returned '420 4.2.0 Recipient deferred because there is no Mdb'”
  7. idenet 学习记录:bili
  8. AMOLED 显示面板 Mura 缺陷
  9. 【微信小游戏】游戏性能检测
  10. 【贝叶斯分析①】Metropolis-Hastings算法理解和简单实现