问题发现
设备突然出现多次断连的情况,查看event.log发现以下信息
Sent too many concurrent PUBLISH messages
然后就去源码搜索这个错误信息出现的地方,发现它在FlowControlHanlder类出现在
java
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
if (msg instanceof PUBLISH) {
//client already disconnected. No need to read more publishes.
if (serverSendQuota.get() < 0) {
return;
}
final PUBLISH publish = (PUBLISH) msg;
//decrement sendQuota for qos > 0 publish messages and disconnect client when quota gets negative
if (QoS.AT_MOST_ONCE != publish.getQoS() && serverSendQuota.getAndDecrement() == 0) {
serverDisconnector.disconnect(ctx.channel(),
"A client (IP: {}) sent too many concurrent PUBLISH messages. Disconnecting client.",
"Sent too many concurrent PUBLISH messages",
Mqtt5DisconnectReasonCode.RECEIVE_MAXIMUM_EXCEEDED,
ReasonStrings.DISCONNECT_RECEIVE_MAXIMUM_EXCEEDED);
return;
}
}
super.channelRead(ctx, msg);
}
这段代码的注释是这样说的:当发布的消息Qos>0时,发送配额减1;如果发送配额为负,就会把客户端断开连接
这个serverSendQuota就是发送配额,它是一个AtomicInteger类型的变量
问题来了,在这里减少,那在什么时候会增加呢?
同样在这个FlowControl中,我们找到了配额增加的代码
java
@Override
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise)
throws Exception {
//do not check msg instance and increment quota if its already at maximum
if (serverSendQuota.get() == serverReceiveMaximum) {
super.write(ctx, msg, promise);
return;
}
if (msg instanceof PUBACK) {
serverSendQuota.incrementAndGet();
}
if (msg instanceof PUBCOMP) {
serverSendQuota.incrementAndGet();
}
if (msg instanceof PUBREC) {
final PUBREC pubrec = (PUBREC) msg;
if (pubrec.getReasonCode().getCode() >= 0x80) {
serverSendQuota.incrementAndGet();
}
}
super.write(ctx, msg, promise);
}
在写方法,也就是消息回应的时候,配额会增加
- PUBACK (Publish Acknowledgement)
用途:QoS 1 级别的确认消息
发送方:服务端 → 客户端(或客户端 → 服务端)
触发时机:收到 QoS 1 的 PUBLISH 消息后
作用:确认已收到发布消息,完成单次握手 - PUBREC (Publish Received)
用途:QoS 2 级别的第一次确认
发送方:接收方 → 发送方
触发时机:收到 QoS 2 的 PUBLISH 消息后
作用:确认已收到发布消息,启动四次握手的第二步 - PUBCOMP (Publish Complete)
用途:QoS 2 级别的最终确认
发送方:接收方 → 发送方
触发时机:收到 PUBREL 消息后
作用:确认消息传递完成,结束四次握手
QoS 1 (At LEAST ONCE两次握手)
PUBLISH → PUBACK
QoS 2 (Extract ONCE四次握手):
PUBLISH → PUBREC → PUBREL → PUBCOMP
总结来说就是,服务器对客户端发送消息的一种限流策略