构建具有保证消息传递的 SignalR / Knockout 仪表板
Building a SignalR / Knockout dashboard with guaranteed messaging
我正在研究使用 real-time 消息来替换我们公司的监控仪表板。
旧概念:
在我们公司,我们有一个仪表板,显示 700 多台物理机器的(相当详细的)状态,并添加了 meta-information。它是大约 1.5 年前由我的一位同事在 ASP.NET Web Forms(我不喜欢)中构建的,使调度员能够协调我们的技术人员应该去哪里解决问题(机器位于不同的位置)地理位置)。
不幸的是,该应用使用了一个 30 秒的完整页面 auto-refresh,其背后有一个大查询。它很慢,并且会完全重置您的视图(正如我所说,仪表板包含 700 多台机器)。我个人想改变这一点。使用起来非常烦人。我们的调度员已经学会了忍受这一点,但我认为他们应该得到更好的待遇。
新概念:
我想在新仪表板上显示相同的内容,但有 real-time 更新和 "message" 日志。在我们公司,我们大约 90% 的时间都在 MS 堆栈上工作,所以我计划使用 ASP.NET MVC、SignalR、SQL Server 和 Knockout。
我目前拥有的
看看这个简单的图表:
+----+ +----+ +----+ +----+ +----+ +----+ +----+
| PC | | PC | | PC | | PC | | PC | | PC | | PC | ... ...
+--+-+ +--+-+ +-+--+ +--+-+ +--+-+ +--+-+ +--+-+
| | | | | | |
| +--+ +--+ +----+ <-+ <-+ <-+
| | | |
+---v---v-----v-----v+ +-----------------------+
| | TCP/IP | |
| Monitoring Backend +---------> Data Enrichment App |
| | | |
+--------------------+ +---------+-------------+
|
+------------------------------+ +---------+
| | |
| +----------------+ | |
+-----v-----+----------> DB Proxy +-----> S Q L |
| | PUB/SUB +----------------+ | |
| Redis | | |
| | +----------------+ +---------+
+-----------+ | TO BE... |
+----------------+
- 我创建了一个小 "Data enrichment app",它通过 TCP/IP 从监控后端接收事件并向事件添加额外的业务数据(例如,设备的位置、描述性名称以及主机名、警报的 humn-readable 翻译等)未包含在监控系统中。
- 丰富的事件从应用程序发送到 Redis。我这样做是为了让其他应用程序可以作为订阅者连接到 Redis,因为我在此处输出的数据比监控后端发送的数据更优质且更具可读性。
- 目前唯一的 PUB/SUB-ing 到 Redis 是一个数据库代理,它监听传入的事件并将它们发送到数据库(SQL 服务器),我已经将其用于历史报告目的,但目前只包含相当简单的数据。
这里的想法是将 SignalR Hub 订阅到我的 ASP.NET 应用程序中的 Redis 后端,以将事件触发到客户端。 (这是 TO BE 部分)
问题:
想法是,当客户端导航到仪表板 URL 时,初始概览由 SQL 后端中的状态数据填充。之后,通过 SignalR 接收事件,并通过更改 Knockout 属性更新视图。
但是,如果客户断开连接(例如,在从一个会议室走到另一个会议室时他的笔记本电脑处于休眠状态),他会错过来自 SignalR 中心的消息,并且他的仪表板视图不再正确!
可能的解决方案是:
在每次事件更改时通过 SignalR 发送每个设备的完整状态: 这是不可能的,因为我必须发送大量数据电线。 (我猜至少有 12,000 条 JSON 数据记录)
在检测到 timed-out 连接后强制完全刷新:我不知道如何使用 SignalR 实现此功能:(
...?
处理 Real-time、push-based 数据并保证数据到达的推荐方法是什么?或者我将如何处理从 timed-out 连接中恢复?或者是让这个 real-time 变得疯狂的想法?
免责声明:我是系统工程师,不是职业程序员。这是我的第一个 real-time 网络应用程序。关于 SignalR 的其他问题通常不会像这样处理大量数据。
所以,我认为这个问题有点过于宽泛和没有重点,但我要说的比评论要多。
为了简单起见,我不会使用 websocket 本身来传达消息,而只是作为通知渠道让客户知道有更多数据可用。
首先,我将专注于通过 JSON 通过标准 HTTP 请求一组有限消息的方法。我假设所有消息都具有某种序列号或时间戳,因此已经具有带有标记 n
的消息的客户端将需要能够请求带有标记 >n
的所有消息。我们称其为消息服务。
现在连接 websocket 并使用它向客户端传送简单的 "more data available" 事件。每次客户端收到事件时,都会向消息服务发出一个新请求,请求所有带有时间戳 > 比客户端拥有的最新消息更高的消息。
如果 websocket 断开连接,当它重新连接时,向消息服务发出单独的请求以确保您同步,然后再次等待事件,如上所述。
spender 的回答很好,但我想在 SignalR 的上下文中解决解决方案 2;您可以为此使用 SignalR 生命周期事件:OnConnected
、OnReconnected
和 OnDisconnected
。您可以阅读更多 about the events here and how to use them in a hub here.
您将在客户端首次连接时完全初始化视图(调用 OnConnected)。如果客户端暂时失去连接(默认情况下少于 30 秒,请参阅 relevant settings here,OnReconnected
被调用),您无需执行任何其他操作;只要标准排队机制中有足够的 space,排队的消息就会被传送。
如果客户端 PC 进入睡眠状态,OnDisconnected
最终会被调用,客户端将不得不建立新的连接。到那时,最简单的实现方式就是简单地再次加载所有数据。如果你想重用客户端已有的(过时的)数据,那么你需要
- 一种检索数据/消息子集的方法(例如,基于序列号或时间戳;由于您已经将事件流存储到数据库中,听起来应该可以集成它)
- 每当收到消息时,将此号码存储在客户端上
- 在建立连接时将其发送到服务器(例如通过query string),以便服务器可以在
OnConnected
中读取它并知道是初始化完整视图还是仅初始化变更集
使用 SignalR 消息进行实时更新应该没问题,但我建议使用常规 MVC/WebAPI 控制器来提供初始化视图所需的完整数据集(来自 OnConnected
)。
就是说,如果您想要保证 传送,您将必须确认您的消息并且可能还实施排队机制。默认情况下,SignalR 仅缓冲大约 1000 条消息,然后开始丢弃它们。您可以增加该值,但构建一个根据您的要求量身定制的值可能更有意义。
SignalR 不适用于 "Reliable Messaging"。它专为 "Real Time Communication" 而设计。
问题是可靠性实际上与实时不兼容。可靠的消息传递意味着消息将至少传递一次。但是,如果数据 link 出现故障,则消息将延迟传递。但是,实时意味着消息已送达 "instantly",而不是半小时后。
如果您需要可靠性,我会换成 "Message Queue"。您应该发现它们 "quick enough" 适合您的目的(RTC 通常意味着您需要毫秒范围内的延迟,而 MQ 应该提供第二个范围内的典型延迟)。
您的问题是,如何通过 SignalR 实现消息队列。
试试 RabbitMQ,我只听说过它的好处。还有一个 Javascript 客户端。
我正在研究使用 real-time 消息来替换我们公司的监控仪表板。
旧概念:
在我们公司,我们有一个仪表板,显示 700 多台物理机器的(相当详细的)状态,并添加了 meta-information。它是大约 1.5 年前由我的一位同事在 ASP.NET Web Forms(我不喜欢)中构建的,使调度员能够协调我们的技术人员应该去哪里解决问题(机器位于不同的位置)地理位置)。
不幸的是,该应用使用了一个 30 秒的完整页面 auto-refresh,其背后有一个大查询。它很慢,并且会完全重置您的视图(正如我所说,仪表板包含 700 多台机器)。我个人想改变这一点。使用起来非常烦人。我们的调度员已经学会了忍受这一点,但我认为他们应该得到更好的待遇。
新概念:
我想在新仪表板上显示相同的内容,但有 real-time 更新和 "message" 日志。在我们公司,我们大约 90% 的时间都在 MS 堆栈上工作,所以我计划使用 ASP.NET MVC、SignalR、SQL Server 和 Knockout。
我目前拥有的
看看这个简单的图表:
+----+ +----+ +----+ +----+ +----+ +----+ +----+
| PC | | PC | | PC | | PC | | PC | | PC | | PC | ... ...
+--+-+ +--+-+ +-+--+ +--+-+ +--+-+ +--+-+ +--+-+
| | | | | | |
| +--+ +--+ +----+ <-+ <-+ <-+
| | | |
+---v---v-----v-----v+ +-----------------------+
| | TCP/IP | |
| Monitoring Backend +---------> Data Enrichment App |
| | | |
+--------------------+ +---------+-------------+
|
+------------------------------+ +---------+
| | |
| +----------------+ | |
+-----v-----+----------> DB Proxy +-----> S Q L |
| | PUB/SUB +----------------+ | |
| Redis | | |
| | +----------------+ +---------+
+-----------+ | TO BE... |
+----------------+
- 我创建了一个小 "Data enrichment app",它通过 TCP/IP 从监控后端接收事件并向事件添加额外的业务数据(例如,设备的位置、描述性名称以及主机名、警报的 humn-readable 翻译等)未包含在监控系统中。
- 丰富的事件从应用程序发送到 Redis。我这样做是为了让其他应用程序可以作为订阅者连接到 Redis,因为我在此处输出的数据比监控后端发送的数据更优质且更具可读性。
- 目前唯一的 PUB/SUB-ing 到 Redis 是一个数据库代理,它监听传入的事件并将它们发送到数据库(SQL 服务器),我已经将其用于历史报告目的,但目前只包含相当简单的数据。
这里的想法是将 SignalR Hub 订阅到我的 ASP.NET 应用程序中的 Redis 后端,以将事件触发到客户端。 (这是 TO BE 部分)
问题:
想法是,当客户端导航到仪表板 URL 时,初始概览由 SQL 后端中的状态数据填充。之后,通过 SignalR 接收事件,并通过更改 Knockout 属性更新视图。
但是,如果客户断开连接(例如,在从一个会议室走到另一个会议室时他的笔记本电脑处于休眠状态),他会错过来自 SignalR 中心的消息,并且他的仪表板视图不再正确!
可能的解决方案是:
在每次事件更改时通过 SignalR 发送每个设备的完整状态: 这是不可能的,因为我必须发送大量数据电线。 (我猜至少有 12,000 条 JSON 数据记录)
在检测到 timed-out 连接后强制完全刷新:我不知道如何使用 SignalR 实现此功能:(
...?
处理 Real-time、push-based 数据并保证数据到达的推荐方法是什么?或者我将如何处理从 timed-out 连接中恢复?或者是让这个 real-time 变得疯狂的想法?
免责声明:我是系统工程师,不是职业程序员。这是我的第一个 real-time 网络应用程序。关于 SignalR 的其他问题通常不会像这样处理大量数据。
所以,我认为这个问题有点过于宽泛和没有重点,但我要说的比评论要多。
为了简单起见,我不会使用 websocket 本身来传达消息,而只是作为通知渠道让客户知道有更多数据可用。
首先,我将专注于通过 JSON 通过标准 HTTP 请求一组有限消息的方法。我假设所有消息都具有某种序列号或时间戳,因此已经具有带有标记 n
的消息的客户端将需要能够请求带有标记 >n
的所有消息。我们称其为消息服务。
现在连接 websocket 并使用它向客户端传送简单的 "more data available" 事件。每次客户端收到事件时,都会向消息服务发出一个新请求,请求所有带有时间戳 > 比客户端拥有的最新消息更高的消息。
如果 websocket 断开连接,当它重新连接时,向消息服务发出单独的请求以确保您同步,然后再次等待事件,如上所述。
spender 的回答很好,但我想在 SignalR 的上下文中解决解决方案 2;您可以为此使用 SignalR 生命周期事件:OnConnected
、OnReconnected
和 OnDisconnected
。您可以阅读更多 about the events here and how to use them in a hub here.
您将在客户端首次连接时完全初始化视图(调用 OnConnected)。如果客户端暂时失去连接(默认情况下少于 30 秒,请参阅 relevant settings here,OnReconnected
被调用),您无需执行任何其他操作;只要标准排队机制中有足够的 space,排队的消息就会被传送。
如果客户端 PC 进入睡眠状态,OnDisconnected
最终会被调用,客户端将不得不建立新的连接。到那时,最简单的实现方式就是简单地再次加载所有数据。如果你想重用客户端已有的(过时的)数据,那么你需要
- 一种检索数据/消息子集的方法(例如,基于序列号或时间戳;由于您已经将事件流存储到数据库中,听起来应该可以集成它)
- 每当收到消息时,将此号码存储在客户端上
- 在建立连接时将其发送到服务器(例如通过query string),以便服务器可以在
OnConnected
中读取它并知道是初始化完整视图还是仅初始化变更集
使用 SignalR 消息进行实时更新应该没问题,但我建议使用常规 MVC/WebAPI 控制器来提供初始化视图所需的完整数据集(来自 OnConnected
)。
就是说,如果您想要保证 传送,您将必须确认您的消息并且可能还实施排队机制。默认情况下,SignalR 仅缓冲大约 1000 条消息,然后开始丢弃它们。您可以增加该值,但构建一个根据您的要求量身定制的值可能更有意义。
SignalR 不适用于 "Reliable Messaging"。它专为 "Real Time Communication" 而设计。
问题是可靠性实际上与实时不兼容。可靠的消息传递意味着消息将至少传递一次。但是,如果数据 link 出现故障,则消息将延迟传递。但是,实时意味着消息已送达 "instantly",而不是半小时后。
如果您需要可靠性,我会换成 "Message Queue"。您应该发现它们 "quick enough" 适合您的目的(RTC 通常意味着您需要毫秒范围内的延迟,而 MQ 应该提供第二个范围内的典型延迟)。
您的问题是,如何通过 SignalR 实现消息队列。
试试 RabbitMQ,我只听说过它的好处。还有一个 Javascript 客户端。