在物联网(IoT)时代,各种设备如智能设备、服务器甚至Web会话的数量激增,实时监控这些设备的在线状态变得尤为重要。本文将介绍如何构建一个小型Web应用程序,用于跟踪登录用户的在线状态。为了简化演示,将在同一页面内创建3个不同的连接。值得注意的是,这种技术不仅限于Web应用,它底层使用的是MQTT协议,因此所有描述的代码都可以轻松地用C/C++编写,并在低内存集成板上运行。
在本教程中,将使用名为emitter.io
的服务来处理在线状态。该服务的在线状态功能贯穿整个服务,对用户来说是透明的。在emitter中,有两种方式可以检索在线状态信息,它们的性质是互补的:
emitter.io
是一个分布式的发布-订阅MQTT代理。在本文中,假设读者对MQTT有基本的了解,不会详细讨论协议的规范以及如何使用它。然而,这里有一些重要的点需要了解:
sensor/1/temperature/
)。
假设有多个MQTT客户端(连接)订阅了特定的通道。在演示中,通道是"presence-demo/" + Math.random().toString(16).substr(2, 8)
,这为每个人创建了一个隔离的演示。将检索:
在示例中,首先创建一个主连接,它将订阅通知。注意,在这里使用了secure: true
参数,因为希望设备通过TLS/SSL连接。
var client0 = emitter.connect({ secure: true });
一旦客户端连接上并想要监控在线状态,查询通道的在线状态。
client0.on('connect', function() {
// Query the presence state
client0.presence({
key: key,
channel: channel
});
});
当第一次调用presence()
函数时,收到的响应格式如下,告诉目前没有人订阅:
{event: "status", channel: "presence-demo/8758f6d3/", occupancy: 0, time: 1471850444, who: []}
由于没有特别指定,client0
也订阅了该通道的加入/离开推送通知,emitter代理将转发此类事件。
接下来,创建一些要测试的客户端。最初,这些连接是禁用的。
var client1 = null;
var client2 = null;
将切换这些连接的开关,以模拟设备连接和断开连接。在这里,name
参数实际上设置了MQTT的clientId
选项,允许识别各种设备。由于控制了clientId
,也可以将任何状态与它们关联。在示例中,简单地给用户一些名字,但理想情况下,会使用一个唯一的标识符,如GUID/UUID或强伪随机字符串。
当一个客户端加入时,收到一个subscribe
事件,其中包含当前占用率、时间戳(UNIX时间)和客户端ID。在这个演示中,将客户端ID设置为用户名,但理想情况下,会有一个GUID/UUID,如前所述。
{event: "subscribe", channel: "presence-demo/8758f6d3/", occupancy: 1, time: 1471850449, who: "Alan"}
另一个用户可能会订阅,会收到另一个事件。
{event: "subscribe", channel: "presence-demo/8758f6d3/", occupancy: 2, time: 1471850450, who: "Margaret"}
当用户取消订阅时,会收到一个unsubscribe
事件,以及当前的占用率。
{event: "unsubscribe", channel: "presence-demo/8758f6d3/", occupancy: 1, time: 1471850453, who: "Alan"}
现在,订阅了在线事件的客户端/设备将接收并处理各种通知。参见下面的代码片段,它相当直接。在这种情况下,它使用vuejs数据绑定更新UI。
// on every presence event, print it out
client0.on('presence', function(msg){
console.log(msg);
var users = vue.$data.users;
switch(msg.event){
case 'status':
for(var i = 0; i < msg.who.length; ++i){
users.push({
name: msg.who[i]
});
}
break;
case 'subscribe':
users.push({
name: msg.who
});
break;
case 'unsubscribe':
vue.$data.users = users.filter(function( obj ) {
return obj.name !== msg.who;
});
break;
}
// Also, set the occupancy
vue.$data.occupancy = msg.occupancy;
});