SignalR hosting on separate appPool - signalr

My web application have a chat app module built on SignalR hub.
The app will have 1000+ concurrent users. I want to host the Chat Module on a separate app pool to separate it from my main application so that SignalR does not bottleneck my main application.
I'm not sure how to go about it. I've built a simple Chat system (much like Google Talk) tied to my Main Web project using Hub class and client side code resides in Site Master since it will be common across the application.
i also want to be able to call Hub method outside the Hub class. For example, an admin might assign certain task to an user from admin panel. So, from the Controller method after completing service operation (task assignment) successfully, I want to send a SignalR message to that particular user. Should I be using Hub or Persistence connection to achieve both the goal? Host SignalR on a different port? I'd appreciate some guidance on this. Thanks!

Not that I think you necessarily have to do this, but I can understand the desire to separate. To do this you would have to have your MVC application call hub methods as if it was a SignalR client itself. You can either do this by putting separate methods on the same hub or by adding a secondary hub which exists solely for this kind of inter-app communication.
I would probably use the latter approach of having a second hub because you can secure it differently. If you go this route, you would simply get the HubContext for the primary hub and make whatever calls you want/fire whatever signals you want to it. That might look something like this:
public MyInterAppCommunicationHub : Hub
{
public void SendSystemAlert(string message)
{
HubContext myPrimaryHubContext = GlobalHost.ConnectionManager.GetHubContext<MyPrimaryHub>();
myPrimaryHubContext.Clients.systemAlert(message);
}
}

Related

storing signalr hub connection details in local storage

I am using signalr client in my application. I want to establish the connection and use the same in all the pages. I could get the connection. To access the hub details i am trying to store it local storage.
Below is the hub details i am getting once i build it.
localStorage.setItem("thisConnectionHub",JSON.stringify(thisConnectionHub));
It is stored as below.
HubConnection is missing in this. Because of this i could not listen to any methods from server.
Any ideas how can i get this work.
Thanks,
If you want to use the same connection, you need to create a service that can be #injected in your components so you don't lose the context and can invoke the client side methods that instead will invoke the hub methods. This is called dependency injection. Your service will establish the connection on the application init and then other components would use the hubConnection methods that you will declare public or event implement an interface to do that.

How to Intercept ScaleoutMessage Broadcast: (Edited: How to send message directly to ServiceBus SignalR Backplane)

I have following scenario:
User request for certain resource on server, This request is long running task and very like 2~3 seconds to 10 seconds. We issue a JobTicket to user, As our user want to wait.
On receiving request we store that request in persistence storage and issue a token to user as JobTicket (GUID).
User make connection with Hub to get information about that GUID.
In Background:
We have WAS Hosted as well as Windows Service to perform some operation on that request.
On complete, WAS Hosted/Windows Service call our Web Application that job has been completed.
From there based on job Ticket we identify which user and on its connection we let user know its job has been completed.
Now we have farm of servers, we are using Windows Server On Prem ServiceBus 1.1 which is working fine, But challenge we have is that we are not able to intercept ServiceBus based backplane message broadcast and message is going to all the client. As we have farm, user intermediately may have drop connection and connected to other server based on load balancer so we need to have scale out using Service Bus as its kind of seamless to integrate and we are also using for our internal purpose in our application so we don't want to user any other mix in complex solution.
I have tried using IHubPipelineModule but still Scale out message broadcast not passing thru that, I tried to hookup SignalR code directly and debug thru it but its taking long. I don't want to mess-up something arbitrary in actual code. As I can see that in OnReceive I can see message are coming, but not able to follow further. I just need small mechanism that I can intercept broadcast message and make sure that it goes to client it intended and not all the client by wasting resources, and security concern as well.
Please help me on this issue, it's kind of stuck from last 4 days and not able to come to any solution and same time I want to go with establish pattern and don't want to fork any special build for this kind of small issues which I am sure one of you expert knows how I can do that seamlessly.
Thanks,
Shrenik
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:
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));
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,
};
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
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

SignalR in multiple instances in Azure

I'd like to use Azure to host my web application, for instance CloudService web role or Azure Websites, inside the application I use SignalR to connect client and server.
Since I scaled two instances for my web roles, it seems I came across a very common problem, the SignalR could not find the correct original instance. The client JavaScript said it was already started, but the server hub OnConnected event randomly not raised, so were the server methods which intended to be called by clients, all these strange issues happened randomly.
Once I changed the instance to be one, all the problems gone. So can anyone explain what happened when the client call server method, why sometimes the server seems not response properly?
I found the post, can Azure Service Bus solve this issue?
Yes, you need to use the azure service bus. Otherwise the connections are stored in memory on the given server and the other server will know nothing about them. Once you create the service bus, just reference it in the startup class.
public void Configuration(IAppBuilder app)
{
System.Diagnostics.Trace.TraceInformation("SignalR Startup > Configurtion start");
// Any connection or hub wire up and configuration should go here
string connectionString = "XXX";
GlobalHost.DependencyResolver.UseServiceBus(connectionString, "TopicName");
...
}
You will also need to get a reference to the context in each of your hub methods:
var context = GlobalHost.ConnectionManager.GetHubContext<HubName>();
It's easy peasy :)

Connected to multiple hubs and disconnect from only one

I'm writing a Single Page Application with Durandal and I'm planning on using SignalR for some functionality. First of all, I have a top bar that listens for notifications that the server may send. The site start a connection to the "TopBarNotificationHub".
On one of the pages I want to connection to another hub as two users might edit the data on this page simultaneous and I want to notify if someone updated the data. No problem, this works fine.
But, when leaving that page I want to disconnect from ONLY the second hub, but I can't find a way to accomplish this. If I just say hub.connection.stop(); the connection to th eTopBarNotificationHub also stops (as it's shared).
Is there a way to just leave one hubproxy and let the other exist?
As this is a SPA the "shell" is never reloaded so it doesn't connect to the hub again... I might be able to force this to reconnect everytime another page disconnects from a hub, but there might be a cleaner solution...
Thanks in advance.
//J
If you use multiple hubs on a single page that's fine, but they share the same connection, so it isn't taking up more resources on the client other than receiving the updates.
Therefore to "connect and disconnect to/from a hub" you need to slightly rearchitect. If you use Groups instead of Clients on the server side you can "register" with a Hub by calling a (for example) Hub1.Register method and sticking the client in the relevant group in that method. To "deregister" you call a (for example) Hub1.DeRegister and remove the client's ConnectionId from the group in that method. Then, when you push updates to clients, you can just use the Group instead of Clients.All.
(C# assumed for server language as you didn't specify in your tag)
To add a client to the hub group: Groups.Add(Context.ConnectionId, groupNameForHub);
To remove a client from the hub group: Groups.Remove(Context.ConnectionId, id.ToString());
To broadcast to that Hub's clients: Clients.Group(groupNameForHub).clientMethodName(param1, param2);
Just to make it confusing, though, note that the group named "myGroup" in Hub1 is separate to the group named "myGroup" in Hub2.
This is the exact approach recommended in the documents (copied here in case they move/change in later versions):
Multiple Hubs
You can define multiple Hub classes in an application. When you do that, the connection is shared but groups are separate:
• All clients will use the same URL to establish a SignalR connection with your service ("/signalr" or your custom URL if you specified one), and that connection is used for all Hubs defined by the service.
There is no performance difference for multiple Hubs compared to defining all Hub functionality in a single class.
• All Hubs get the same HTTP request information.
Since all Hubs share the same connection, the only HTTP request information that the server gets is what comes in the original HTTP request that establishes the SignalR connection. If you use the connection request to pass information from the client to the server by specifying a query string, you can't provide different query strings to different Hubs. All Hubs will receive the same information.
• The generated JavaScript proxies file will contain proxies for all Hubs in one file.
For information about JavaScript proxies, see SignalR Hubs API Guide - JavaScript Client - The generated proxy and what it does for you.
• Groups are defined within Hubs.
In SignalR you can define named groups to broadcast to subsets of connected clients. Groups are maintained separately for each Hub. For example, a group named "Administrators" would include one set of clients for your ContosoChatHub class, and the same group name would refer to a different set of clients for your StockTickerHub class.

How can I Manually create a client proxy for a WF4 (xamlX) Service

I've created several services by wrapping the WorkflowServiceHost in a WCF service; using WorkflowHostingEndpoint. Doing this I was able to define my service contract and create proxy classes to connect to those services.
I'm now creating a service in which I want to use the WF4 messaging activities and again self host the service. I also REALLY want to manually create my proxy classes without using the ServiceReference in VS2010 or ServiceUtil...
I've seen some references that use the Send Activity in the Service client but I'd like to be able to use a more "WCF-like" proxy created directly against the service contract like I've done with the other services. How can I do that? Is there anywhere a xamlX (or xaml with messaging activities) stores the interface contract that I can use to generate a proxy manually?
NOTE: I don't want to use the Send activity as described in the WF_WCF_Samples.
UPDATE:
I tried creating an interface identical to the workflow receive activity before posting this question; but I keep getting the following error:
The message with Action 'http://tempuri.org/ISvrClientService/Create'
cannot be processed at the receiver, due to a ContractFilter mismatch
at the EndpointDispatcher. This may be because of either a contract
mismatch (mismatched Actions between sender and receiver) or a
binding/security mismatch between the sender and the receiver. Check
that sender and receiver have the same contract and the same binding
(including security requirements, e.g. Message, Transport, None).
Thanks!
There is no need to use the Send activity. When you host a workflow service you are hosting a SOAP endpoint, the only difference is the implementation but that is a private detail. You can create a proxy object using ChannelFactory just like with any other WCF service.
var factory = new ChannelFactory<IYourService>();
var proxy = factory.CreateChannel();
The IYourService interface is somethign you have to hand craft. There isn't one on the server, it is done in a workflow, so you have to code up the identical contract.
I've done this successfully with channel factory and manually created interfaces. The trick was in matching-up the reply/send in/out parameters names not just type signature.
This is particularly crucial if you have bookmarks with correlation in your workflow.

Resources