2

我有以下情况:

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

在后台:

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

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

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

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

谢谢,史瑞尼克

4

1 回答 1

0

After lots of struggling and not finding straight forward way, I have found the way as below for someone else in future it might help.

Scenario: 1. Web Farm: Host External User facing Web Pages 2. Backend Process: Which is mix of WebApi, SharePoint, Windows Service etc.

User from Web Page submit some request and get a unique id as return back. Internally on receiving request, we queue that request to Service Bus using TopicClient for processing.

There are pool of Windows Service watching on Message on Service Bus using SubscriptionClient and process that message. On completion of process which can run from 5 seconds to 30 seconds and some cases even more. We need to inform client that its job done if its waiting on web page or waiting for completion notification.

In this story, We are using SignalR to push job completion notification to client.

Now my earlier problem is How I let know from windows service to web application that job is done so send notification to client who submitted request.

One way is we hosted another hub internally in web application, Windows service act as client and call web application hosted hub, and in that hub method it will call external facing hub method to propagate message to specific client who submitted request, for which we are using Single user Group.

And as we have register service bus as backplane it will propagate to other servers and then appropriate client will get notification. So this is ideal solution and should work in most cases.

In above approach we have one limitation that, how Windows Service connect to Web Client, as we donot have windows auth, but we have openid based auth with ADFS. Now in such case Web Application required special code in which provide separate userid or password for windows service to communicate or have windows authentication also allowed for that hub for service account of windows service.

I was trying and trying how to remove all this hopes between interserver communication and again management of extra security.

So I did below with simplicity, though it tooks me whole night to find our internal of SignalR. But it works:

Approach is to send message directly to ServiceBus Backplane, and as all Web Server already hooked-up with ServiceBus backplane then they will get message.

Unfortunately SignalR doesn't provide such mechanism to send message directly to Backplane. I think its on pub/sub model so they don't want somebody to hack in their system :). or its violation of their pattern, but its make sense, in my case because of different roles and security, I have simplify code as below:

  1. Create a ServiceBusMessageBus instance in my code, Same way as Below: Though I have created separate instance and store till lifetime of Windows Service, so I don't create instance every time:

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

  1. Create a ClientHubInvocation Object: This is a message which actually get created in SignalR infrastructure when Backplane based message broadcast:

                    ClientHubInvocation hubData = new ClientHubInvocation
                    {
                        Args = new object[] { msg },
                        Hub = "JobStatusHub",
                        Method = "onJobStatus",
                        State = null,
                    };
    
  2. Create a Message object which accept by ServiceBusMessageBus.Publish, Yes, so this is a method which actually get called on base class ScaleoutMessageBus.Publish. This class is actually responsible for sending message to topic and other subscribers on other server nodes. Why not use that directly. Now to create Message Object, You need following code:

Message backplaneMessage = new Message( sourceId, "hg-JobStatusHub." + name, new ArraySegment(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(hubData))));

In above second parameter is something interesting, In case if you want to publish to all the client then syntax is "h-", in my case specific group user, so syntax is "hg-.. You can check the code here: https://github.com/SignalR/SignalR/blob/bc9412bcab0f5ef097c7dc919e3ea1b37fc8718c/src/Microsoft.AspNet.SignalR.Core/Infrastructure/PrefixHelper.cs

  1. Publish your message to backplane directly as below:

await serviceBusBackplane.Publish(backplaneMessage);

I wish this PrefixHelper class have been public.

Remember: This is not recommended way and doent insulate from future upgrade for SignalR, as its internal they may change so any upgrade might come with small hazale to change this code. But in summary this works. Hope SignalR Team provide some mechanisam out of box to send message directly to backplane instead.

Thanks

于 2014-07-09T19:06:36.930 回答