使用 Emitter 进行 IoT、移动或 Web 设备在线状态跟踪
演示如何使用 emitter.io 平台跟踪设备或用户的在线状态
引言
本文介绍了一种跟踪各种设备(如物联网设备、服务器甚至 Web 会话)存在状态的方法。通常有必要查询一组此类设备的状态,并查看当前有多少设备已连接并正在传输数据。
[查看实时演示]
在本例中,我们将构建一个小的 Web 应用程序,用于跟踪已登录用户的状态,为了简单起见,我们将在同一页面内创建 3 个不同的连接。值得注意的是,该技术不仅限于 Web,并且底层使用 MQTT 协议,因此此处描述的所有代码都可以轻松地用 C/C++ 编写并在低内存集成板上运行。
背景
在本教程中,我们将使用名为 emitter.io 的服务来处理在线状态。状态功能已集成到整个服务中,并且对用户是透明的。在 emitter 中,有两种获取状态信息的方法,它们是互补的
- 检索当前在线设备的列表,这是一个请求/响应样式的 API 调用,并返回一条状态消息,描述订阅者数量及其客户端标识符。
- 加入/离开更新通知,当设备订阅或取消订阅特定的 MQTT 频道或通配符时,由服务推送。
现在,emitter 是一个分布式的、发布-订阅、MQTT 代理。在本文中,我们假设您对 MQTT 有一些基本了解,并且我们不会详细介绍协议的规范以及如何使用它,但是,这里有几点重要说明
- 在 MQTT 中,客户端可以订阅主题(频道),这些主题由分层字符串表示(例如:sensor/1/temperature/)。
- 客户端可以向这些频道发布任意二进制数据。
- MQTT 数据包的标头只有 2 个字节,并且存在各种客户端库。
对状态信息感兴趣的设备
假设我们有许多订阅特定频道的 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()
函数时,我们会收到如下格式的响应,告诉我们当前没有人订阅 occupancy: 0
。由于我们没有另行指定,client0
也订阅了此频道的加入/离开推送通知,并且 emitter 代理将在这些事件发生时将此类事件转发给我们。
{event: "status", channel: "presence-demo/8758f6d3/", occupancy: 0, time: 1471850444, who: []}
要监视的设备
接下来,我们创建了几个我们将要测试的客户端。最初,这些连接被禁用。
var client1 = null;
var client2 = null;
我们将打开和关闭这些连接以模拟设备连接和断开连接。在这种情况下,name
参数实际上将设置 MQTT 的 clientId
选项,从而允许我们识别各种设备。由于我们控制了 clientId
,我们也可以将任何状态与它们关联起来。在示例中,我们仅给用户一些名称,但理想情况下,可以使用唯一的标识符,例如 GUID
/UUID
或强伪随机 string
。
/**
* Function that toggles the connection on a particular emitter client.
*/
function toggleConnection(client, name) {
if(client) {
// If client is already connected, disconnect it
client.disconnect();
return null;
} else {
// If client is not yet connected, connect and subscribe to the channel
client = emitter.connect({ secure: true, clientId: name });
client.on('connect', function(){
client.subscribe({
key: key,
channel: channel
});
});
return client;
}
}
当一个客户端加入时,我们会收到一个 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){
// Occurs when we've received a full response with a complete list of clients
// that are currently subscribed to this channel.
case 'status':
for(var i=0; i<msg.who.length;++i){
users.push({
name: msg.who[i]
});
}
break;
// Occurs when a user subscribes to a channel.
case 'subscribe':
users.push({
name: msg.who
});
break;
// Occurs when a user unsubscribes or disconnects from a channel.
case 'unsubscribe':
vue.$data.users = users.filter(function( obj ) {
return obj.name !== msg.who;
});
break;
}
// Also, set the occupancy
vue.$data.occupancy = msg.occupancy;
});
延伸阅读
这就是使用 emitter 的状态。最后,您可能需要查看更多资源,包括一些演示和源代码,以获取演示状态功能的完整应用程序。
历史
- 2016 年 8 月 22 日 - 文章的初始版本