我有一个
Windows Service
在多个盒子上运行,publishing messages
到 HiveMq Mqtt Broker。
到目前为止还不错。
但是,我注意到当我手动停止其中一个 Win 服务时 - 我的浏览器 Mqtt 客户端立即 获取最后一条 Last Will 消息(当然我正在订阅)。
我不应该根据配置的秒数而不是立即接收 LWT 消息吗?它可能与
retained
标志有关吗?
感觉我的LWT配置有问题
这是我的 C# 代码片段(在我的 Win Svc 中)-
public ManagedMqttClientOptions WsSecureClientOptions(PublisherSubscriber pubSubType) {
// Build LWT message, then Client Options
MqttModel lastWill = new MqttModel();
lastWill.message = "BMAZZO box is OFFLINE";
lastWill.datestamp = DateTime.Now;
lastWill.status = ConnectionStatus.Offline;
LastWillMsgJson = JsonConvert.SerializeObject(lastWill, Formatting.Indented);
clientOptionsBldr = new MqttClientOptionsBuilder()
.WithClientId(".netWinSvc-BMAZZO-Pub")
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithWebSocketServer("<broker-url>:8884/mqtt")
.WithCredentials("myUser", "myPswd")
.WithCleanSession(true)
.WithWillQualityOfServiceLevel(2)
.WithWillTopic('myApp\myLAN\Box\BMAZZO\Status') // LAST WILL TOPIC
.WithWillRetain(true) // RETAIN
.WithWillPayload(LastWillMsgJson) // WILL PAYLOAD
.WithKeepAlivePeriod(TimeSpan.FromSeconds(30)) // KEEP ALIVE, 30 SECS
.WithTls(
new MqttClientOptionsBuilderTlsParameters()
{
UseTls = true,
SslProtocol = System.Security.Authentication.SslProtocols.Tls12,
Certificates = new List<X509Certificate2>() { x509Cert }
});
return managedClientOptions;
}
public async Task Publish(string messageIn, string topic, IManagedMqttClient pubClient = null, ConnectionStatus status = ConnectionStatus.Unknown)
{
MqttModel message = new MqttModel();
message.message = messageIn;
message.datestamp = DateTime.Now;
message.status = status;
var payload = JsonConvert.SerializeObject(message, Formatting.Indented);
var send = new MqttApplicationMessageBuilder()
.WithTopic("myApp\myLAN\Box\BMAZZO\Status")
.WithPayload(payload)
.WithMessageExpiryInterval(86400) // seconds per day
.WithQualityOfServiceLevel(2) // QOS
.WithRetainFlag(true) // retain = true
.Build();
applog.Debug($"Mqtt Publish() to broker - message = {messageIn} / {topic} ");
await this.managedMqttClientPublisher.EnqueueAsync(send);
await this.managedMqttClientPublisher.EnqueueAsync(send);
}
示例:我在
2023-04-25 17:30:00
停止了 Win Svc,并立即在我的浏览器中收到一条消息,该服务是 OFFLINE
分钟前的 2023-04-25T17:26:43
{message: 'Offline BMAZZO 10.2.23.62', status: 1, source: 'BMAZZO', datestamp: '2023-04-25T17:26:43.7074826-04:00'}
另一个例子:我刚刚在
17:58
停止了服务,LWT 消息似乎显示了之前的一条 LWT 消息(保留在代理中?)。
{message: 'Offline BMAZZO 10.2.23.62', status: 1, source: 'BMAZZO', datestamp: '2023-04-25T17:47:32.4730311-04:00'}
也许
WithWillRetain
标志应该设置为false?
任何想法或建议表示赞赏。
鲍勃
很高兴听到 HiveMQ Broker 的实施故事!
我想澄清的一个细节——当这个服务终止时,是否有任何当前功能在服务终止时发送 DISCONNECT 数据包?当前的功能似乎表明在连接关闭时正在触发正常断开连接。根据所使用的 HiveMQ 代理的版本,实现自定义侦听器以登录 onDisconnect 或 onServerInitiatedDisconnect 原因代码以进行更具体的诊断可能是值得的。有关实施的更多信息可在此处找到:https://docs.hivemq.com/hivemq/4.11/extensions/registries.html#on-server-disconnect.
此外,我鼓励任何对 HiveMQ 有疑问的人访问我们的社区论坛,网址为 https://community.hivemq.com/!
最好的,
HiveMQ 团队的 Aaron
首先注意 - 在评论中您引用了 v3 规范(“3.1.2.5 Will Flag specs at docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html”)。您正在使用 MQTT v5 (
.WithProtocolVersion(MqttProtocolVersion.V500)
),这是一个规格差异很大的领域。以下是基于我对规范的理解(因此可能包含错误!)。
您所看到的内容符合 MQTT v5 规范,它说:
服务器延迟发布客户的遗嘱消息,直到遗嘱延迟间隔过去或会话结束,以先发生者为准
所以这里的问题是“session什么时候结束”;我相信规范的相关部分是:
会话可以在一系列网络连接中继续。它的持续时间与最新的网络连接加上会话到期间隔一样长。
您的代码不使用
SessionExpiryInterval()
选项,这意味着会话将在断开连接后立即过期
如果会话到期间隔不存在,则使用值 0。
所以当您的客户断开连接时,会话结束并且遗嘱被发布。
参考保命;这些是使客户端能够检测半开连接的机制。 通常(特别是当一个服务被干净地关闭时)连接的另一端会在连接关闭时收到通知并且保持活动状态无关紧要。
请注意,另一个因素可能正在发挥作用(如@Aaron Franz 所述):
当我手动停止其中一个 Win 服务时
手动停止服务可能会导致完全断开连接(即发送 DISCONNECT 数据包)。干净地断开连接(“正常断开连接”) 删除遗嘱但您也可以选择发送它(这可能会改变会话到期间隔)。