构建SignalR / Knockout仪表板,保证消息传递

我正在考虑使用实时消息替换我们公司的监控仪表板。

旧概念:

在我们公司,我们有一个仪表板,显示超过700台物理机器的(相当详细的)状态,以及添加的元信息。 它是由我的一位同事在ASP.NET Web Forms(我不喜欢)中大约1.5年前构建的,以使调度员能够协调我们的技术人员应该去解决问题的位置(这些机器位于不同的地理位置) )。

不幸的是,该应用程序使用30秒的完整页面自动刷新,后面有一个大查询。 它很慢,它完全重置你的视图(正如我所说,仪表板包含超过700多台机器)。 我个人想改变这个。 使用起来非常烦人。 我们的调度员已经学会了接受这一点,但我认为他们应该得到更好的待遇。

新概念:

我想在新仪表板上显示相同的内容,但需要实时更新和“消息”日志。 在我们公司,我们在MS堆栈上工作了大约90%,所以我计划使用ASP.NET MVC,SignalR,SQL Server和Knockout。

我现在有什么

看看这个简单的图表:

+----+ +----+ +----+ +----+ +----+ +----+ +----+ | PC | | PC | | PC | | PC | | PC | | PC | | PC | ... ... +--+-+ +--+-+ +-+--+ +--+-+ +--+-+ +--+-+ +--+-+ | | | | | | | | +--+ +--+ +----+ <-+ <-+  Data Enrichment App | | | | | +--------------------+ +---------+-------------+ | +------------------------------+ +---------+ | | | | +----------------+ | | +-----v-----+----------> DB Proxy +-----> SQL | | | PUB/SUB +----------------+ | | | Redis | | | | | +----------------+ +---------+ +-----------+ | TO BE... | +----------------+ 
  • 我创建了一个小的“数据丰富应用程序”,它通过TCP / IP从监控后端接收事件,并为事件添加额外的业务数据(例如,设备的位置,主机名旁边的描述性名称,可读的翻译)监控系统中未包含的警报等)
  • 丰富的事件从应用程序发送到Redis。 我这样做是因为其他应用程序可以作为订阅者挂钩到Redis,因为我输出的数据比监视后端发送的数据更优越,更易读。
  • 目前,对Redis唯一的PUB / SUB是一个数据库代理,它监听传入事件并将其发送到数据库(SQL Server),我已经将其用于历史报告目的,但目前只包含相当简单的数据。

这里的想法是在我的ASP.NET应用程序中将SignalR Hub订阅到Redis后端,以将事件发送到客户端。 (这是TO BE的一部分)

问题:

我们的想法是,当客户端导航到仪表板URL时,初始概述将由SQL后端中的状态数据填充。 之后,通过SignalR接收事件,并通过更改Knockout属性来更新视图。

但是,如果客户端断开连接(例如,从会议室走到会议室时睡觉他的笔记本电脑),他会错过来自SignalR集线器的消息,他的仪表板视图不再正确!

可能的解决方案是:

  1. 在每次事件更改时通过SignalR发送每个设备的完整状态:这是不可能的,因为我必须通过线路发送大量数据。 (我猜测至少有12,000条JSON数据记录)

  2. 在检测到超时连接后强制完全刷新 :我不知道如何使用SignalR实现这个:(

  3. ……?

处理实时,基于推送的数据并保证数据到达的推荐方法是什么? 或者我如何处理从超时连接中恢复的问题? 或者让这个实时疯狂的想法?

免责声明:我是系统工程师,不是专业程序员。 这是我的第一个实时网络应用程序。 关于SignalR的其他问题通常不会处理大量这样的数据。

Spender的答案很好,但我想在SignalR的背景下解决方案2; 您可以使用SignalR生命周期事件: OnConnectedOnReconnectedOnDisconnected 。 您可以在此处阅读有关此处事件以及如何在集线器中使用它们的更多信息 。

在客户端首次连接时(OnConnected被调用),您将完全初始化视图。 如果客户端暂时失去连接(默认情况下少于30秒,请参阅此处的相关设置 , OnReconnected ),您不需要执行任何其他操作; 只要标准排队机制中有足够的空间,就会传递排队的消息。

如果客户端PC进入hibernate状态,则最终会调用OnDisconnected ,并且客户端必须建立新连接。 此时,最简单的实现是简单地再次加载所有数据。 如果您想重用客户端已有的(过时的)数据,那么您需要

  • 一种检索数据/消息子集的方法(例如,基于序列号或时间戳;由于您已经将事件流存储到数据库中,听起来应该可以集成它)
  • 只要收到消息,就将此号码存储在客户端上
  • 在建立连接时将其发送到服务器(例如,通过查询字符串 ),以便服务器可以在OnConnected读取它并知道是初始化完整视图还是仅初始化变更集

使用SignalR消息进行实时更新应该没问题,但我建议使用常规的MVC / WebAPI控制器来提供初始化视图所需的完整数据集(来自OnConnected )。

也就是说,如果你想要保证交付,你将不得不确认你的消息,并可能还实现排队机制。 SignalR默认只缓存大约1000条消息,然后开始丢弃它们。 您可以增加该值,但根据您的要求构建一个可能更有意义。

所以,我认为这个问题有点过于广泛且没有重点,但我还有更多话要说,而不是适合评论。

为了保持简单,我不会使用websocket来自己传达消息,而只是作为通知渠道让客户知道有更多数据可用。

首先,我将专注于通过标准HTTP通过JSON请求有限的消息集的方法。 我假设所有消息都有某种序列号或时间戳,因此已经包含带有标记n消息的客户端需要能够请求所有带有标记>n消息。 我们称之为消息服务。

现在连接websocket并使用它将简单的“更多可用数据”事件传递给客户端。 每次客户端收到事件时,都会向消息服务发出一个新请求,要求所有邮件的邮票数>大于客户端最近的邮件数。

如果websocket断开连接,当它重新连接时,向消息服务单独发出请求以确保您同步,然后再次等待事件,如上所述。

SignalR不是为“可靠消息传递”而设计的。 它专为“实时通信”而设计。

问题在于可靠性实际上与实时不兼容。 可靠的消息传递意味着消息将至少传送一次。 但是,如果数据链接已关闭,则邮件将延迟传递。 然而,实时意味着消息“即时”发送,而不是半小时后。

如果可靠性是您所需要的,我会切换到“消息队列”。 您应该发现它们“足够快”用于您的目的(RTC通常意味着您需要毫秒范围内的延迟,而MQ应该在第二范围内提供典型延迟)。

您的问题是,如何在SignalR上实现Message Queue。

试试RabbitMQ,我听说它只是好事。 还有一个Javascript客户端。