经过大量的努力并没有找到直截了当的方法,我找到了下面的方法,将来可能会对其他人有所帮助。
场景:
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 没有提供这种机制来直接向背板发送消息。我认为它在发布/订阅模型上,所以他们不希望有人入侵他们的系统:)。或者它违反了他们的模式,但它是有道理的,就我而言,由于不同的角色和安全性,我简化了代码如下:
- 在我的代码中创建一个 ServiceBusMessageBus 实例,方法如下:虽然我创建了单独的实例并存储到 Windows 服务的生命周期,所以我不会每次都创建实例:
ServiceBusMessageBus serviceBusBackplane = new ServiceBusMessageBus(new DefaultDependencyResolver(), new ServiceBusScaleoutConfiguration(connectionString, appName));
-
创建一个 ClientHubInvocation 对象:这是在基于 Backplane 的消息广播时在 SignalR 基础架构中实际创建的消息:
ClientHubInvocation hubData = new ClientHubInvocation
{
Args = new object[] { msg },
Hub = "JobStatusHub",
Method = "onJobStatus",
State = null,
};
创建一个由 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
- 将您的消息直接发布到背板,如下所示:
等待 serviceBusBackplane.Publish(backplaneMessage);
我希望这个 PrefixHelper 类已经公开。
记住:这是不推荐的方式,并且不会与 SignalR 的未来升级隔离,因为它的内部可能会发生变化,因此任何升级都可能伴随着小麻烦来更改此代码。但总而言之,这是可行的。希望 SignalR 团队提供一些开箱即用的机制,将消息直接发送到背板。
谢谢