问题
遇到这样一个问题,安卓端使用独占队列接收服务端消息,当网络从wifi切换到数据后使用的AutorecoveringChannel
没能自动恢复消费者,报错信息如下:
com.rabbitmq.client.ShutdownSignalException: channel error; protocol method:
#method<channel.close>(reply-code=403, reply-text=ACCESS_REFUSED - queue 'queue-xxxx' in vhost 'vhost-xxxx' in exclusive use, class-id=60, method-id=20)
先简单说下原因和解决方式:
原因
服务端通过心跳检查客户端是否正常,任何客户端消息都算一次心跳,如果在三次心跳间隔时间里没有收到客户端一条消息,那么服务认为这个连接异常了,可以释放;所以当客户端切换网络,mq自动重连时会重新创建一个连接,用新的连接去恢复channel上的消费者,而在这期间,独占队列因为还没释放之前的连接,导致无法恢复新的消费者去监听这个队列
-
报错是因为队列独占,mq尝试恢复消费者的时候发现这个独占队列居然名花有主了,燕子…
-
一直很安静的mq有点emo,想去确认一下心爱的队列过得好不好,发现对手有点眼熟,朝暮岁辰伴,凡年酒换柴,这不是自己的QQ签名嘛
-
好家伙,居然是一秒前的我,我已经强的可以留下残影了吗,(two minutes later)这个残影怎么还在,喵的,被自己锁死了,(one minute later)a’wsl
-
终于在mq切换网络三分钟后(实际会短一些,MQ服务发现断开一般在第三个心跳间隔内,因为不是刚好在最后一次心跳后断开的),MQ服务端释放了原来的连接
-
残影是客户端tcp连接断开后MQ服务端不能判断这个连接是不是真的嗝屁了,会给他三次机会,心动了就留下,没心动就放手,也就是在大概三次心跳时间后还没有收到客户端的消息才会释放对应的tcp连接,然后才能恢复消费者
解决方案
-
等服务端释放掉原来的连接我们就可以顺利恢复消费者了
-
三分钟也太久了吧
val connFactory = ConnectionFactory() // 修改心跳间隔,默认60s,按网上所说,一般可以设置在 5~20,太快容易导致误判,连接多的话应该会加大服务压力(猜的) connFactory.requestedHeartbeat = 10 //单位:秒 ... // 这样在恢复消费者channel前等个三倍心跳间隔就没啥问题了 ... consumerChannel = connection!!.createChannel() as AutorecoveringChannel ... consumerChannel!!.addShutdownListener { consumerBrokenTime = System.currentTimeMillis() Log.e(LOG_TAG, "rabbit mq consumer channel shutdown, err: ${it.stackTraceToString()}") } consumerChannel!!.addRecoveryListener(object : RecoveryListener { override fun handleRecovery(recoverable: Recoverable) { Log.e(LOG_TAG, "rabbit mq consumer listener is recovery") } override fun handleRecoveryStarted(recoverable: Recoverable) { val sleep = 35000 - (System.currentTimeMillis() - consumerBrokenTime) Log.e(LOG_TAG, "rabbit mq consumer listener recovery start, sleep ${max(sleep, 0)}ms to avoid exclusive use error") if (sleep > 0){ // 等一等 Thread.sleep(sleep) } } })
-
我一刻也等不了了,客户端是最了解情况的,只要给服务端打个电话说我已经死了。。。让她放下,然后我再以迅雷不及掩耳之势将她拿下,必拿下
-
mq web的connection界面,点开你想关闭的连接,最下面close this connection -> force close,解析它然后封装它,最后在客户端请求它
-
我也试过用old connection对象调用close,不更换网络有可能关闭,但是换过网络的话就不行,而且不管是操作切换还是断开重连,调用close都会报同样的错,timeout原因没有深究
-
插个图
参考