【问题标题】:How to Intercept ScaleoutMessage Broadcast: (Edited: How to send message directly to ServiceBus SignalR Backplane)如何拦截 ScaleoutMessage 广播:(编辑:如何将消息直接发送到 ServiceBus SignalR 背板)
【发布时间】:2014-07-10 03:13:44
【问题描述】:

我有以下场景:

  1. 用户对服务器上某些资源的请求,此请求是长时间运行的任务,非常像 2~3 秒到 10 秒。我们向用户发出 JobTicket,因为我们的用户想要等待。
  2. 收到请求后,我们将该请求存储在持久性存储中,并以 JobTicket (GUID) 的形式向用户发出令牌。
  3. 用户与 Hub 建立连接以获取有关该 GUID 的信息。

在后台:

  1. 我们已经托管了 WAS 以及 Windows 服务来对该请求执行一些操作。
  2. 完成后,WAS Hosted/Windows 服务调用我们的 Web 应用程序,该作业已完成。
  3. 根据工作票,我们确定哪个用户及其连接,我们让用户知道其工作已完成。

现在我们有服务器群,我们使用的是 Windows Server On Prem ServiceBus 1.1,它工作正常,但我们面临的挑战是我们无法拦截基于 ServiceBus 的背板消息广播,并且消息将发送到所有客户端。由于我们有农场,中间用户可能会断开连接并连接到基于负载均衡器的其他服务器,因此我们需要使用服务总线进行横向扩展,作为无缝集成的一种,我们也在应用程序中用于内部目的,所以我们不想在复杂的解决方案中使用任何其他组合。

我尝试过使用IHubPipelineModule,但仍然横向扩展消息广播没有通过它,我尝试直接连接 SignalR 代码并通过它进行调试,但它需要很长时间。我不想在实际代码中搞乱一些任意的东西。正如我在OnReceive 中看到的那样,我可以看到消息即将到来,但无法进一步跟进。我只需要一个小的机制,我可以拦截广播消息并确保它通过浪费资源和安全问题到达预期的客户端而不是所有客户端。

请在这个问题上帮助我,从过去 4 天开始就一直卡住了,无法找到任何解决方案,同时我想采用建立模式并且不想为这种类型分叉任何特殊构建小问题,我相信你们中的一位专家知道我可以如何无缝地做到这一点。

谢谢, 史瑞尼克

【问题讨论】:

标签: signalr signalr-backplane


【解决方案1】:

经过大量的努力并没有找到直截了当的方法,我找到了下面的方法,将来可能会对其他人有所帮助。

场景: 1. 网络农场:托管面向外部用户的网页 2. 后端进程:WebApi、SharePoint、Windows Service等的混合体。

来自网页的用户提交一些请求并获得一个唯一的 ID 作为返回。在内部接收请求时,我们使用 TopicClient 将该请求排队到服务总线进行处理。

有 Windows 服务池使用 SubscriptionClient 监视服务总线上的消息并处理该消息。完成过程可以运行 5 秒到 30 秒,在某些情况下甚至更长。如果它正在等待网页或等待完成通知,我们需要通知客户端它的工作已完成。

在这个故事中,我们使用 SignalR 向客户端推送作业完成通知。

现在我之前的问题是我如何从 Windows 服务告诉 Web 应用程序该工作已完成,因此向提交请求的客户端发送通知。

一种方法是我们在 Web 应用程序内部托管另一个集线器,Windows 服务充当客户端并调用 Web 应用程序托管集线器,在该集线器方法中,它将调用面向外部的集线器方法以将消息传播到提交请求的特定客户端,用于我们正在使用单用户组。

并且由于我们已将服务总线注册为背板,因此它将传播到其他服务器,然后适当的客户端将收到通知。所以这是理想的解决方案,在大多数情况下应该可以工作。

在上述方法中,我们有一个限制,即 Windows 服务如何连接到 Web 客户端,因为我们没有 Windows 身份验证,但我们有基于 openid 的 ADFS 身份验证。现在在这种情况下,Web 应用程序需要特殊代码,在其中为 Windows 服务提供单独的用户 ID 或密码以进​​行通信,或者也允许该中心为 Windows 服务的服务帐户提供 Windows 身份验证。

我一直在尝试如何消除服务器间通信和再次管理额外安全性之间的所有希望。

所以我很简单地在下面做了,尽管我花了一整夜才找到我们的 SignalR 内部。但它有效:

方法是直接向ServiceBus Backplane发送消息,并且所有Web Server已经连接到ServiceBus backplane然后他们将收到消息。

不幸的是,SignalR 没有提供这种机制来直接向背板发送消息。我认为它在发布/订阅模型上,所以他们不希望有人入侵他们的系统:)。或者它违反了他们的模式,但它是有道理的,就我而言,由于不同的角色和安全性,我简化了代码如下:

  1. 在我的代码中创建一个 ServiceBusMessageBus 实例,方法如下:虽然我创建了单独的实例并存储到 Windows 服务的生命周期,所以我不会每次都创建实例:

ServiceBusMessageBus serviceBusBackplane = new ServiceBusMessageBus(new DefaultDependencyResolver(), new ServiceBusScaleoutConfiguration(connectionString, appName));

  1. 创建一个 ClientHubInvocation 对象:这是在基于 Backplane 的消息广播时在 SignalR 基础架构中实际创建的消息:

                    ClientHubInvocation hubData = new ClientHubInvocation
                    {
                        Args = new object[] { msg },
                        Hub = "JobStatusHub",
                        Method = "onJobStatus",
                        State = null,
                    };
    
  2. 创建一个由 ServiceBusMessageBus.Publish 接受的 Message 对象,是的,所以这是一个在基类 ScaleoutMessageBus.Publish 上实际调用的方法。这个类实际上负责向其他服务器节点上的主题和其他订阅者发送消息。为什么不直接使用它。现在要创建消息对象,您需要以下代码:

消息背板Message = new Message( 源标识, “hg-JobStatusHub。” + 姓名, new ArraySegment(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(hubData))));

上面的第二个参数很有趣, 如果您想向所有客户端发布,则语法为“h-”,在我的情况下为特定组用户,因此语法为“hg-.. 您可以在此处查看代码:https://github.com/SignalR/SignalR/blob/bc9412bcab0f5ef097c7dc919e3ea1b37fc8718c/src/Microsoft.AspNet.SignalR.Core/Infrastructure/PrefixHelper.cs

  1. 将您的消息直接发布到背板,如下所示:

等待 serviceBusBackplane.Publish(backplaneMessage);

我希望这个 PrefixHelper 类已经公开。

记住:这是不推荐的方式,并且不会与 SignalR 的未来升级隔离,因为它的内部可能会发生变化,因此任何升级都可能伴随着小麻烦来更改此代码。但总而言之,这是可行的。希望 SignalR 团队提供一些开箱即用的机制,将消息直接发送到背板。

谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-23
    • 1970-01-01
    • 2015-09-27
    • 2018-02-27
    • 2017-01-20
    • 2018-09-08
    • 1970-01-01
    相关资源
    最近更新 更多