Connected to multiple hubs and disconnect from only one - signalr

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.

Related

SignalR backplane expected behavior

I'm creating a new SignalR (.net core) backplane and I'm a little confused about the backplane's expected behavior.
Send all: Is this restricted to all within a hub type or to all hubs of any type across the entire network?
Send to Group: Simalar question. Should I only be sending to groups within a hub type? Or should I send a group message to all subscribers of a group by name - regardless of the source or destination hub type?
Send to User: Also similar question. Should I send the message to all instances of the user, regardles of hub type? Or should I restrict sending to only same user instances of the source hub type?
The HubLifetimeManager class is generic with the hub type as the generic type. So it follows that the methods you implement only apply to the specific hub type.
I think I found the answer to my question in the source for the Microsoft.AspNetCore.SignalR.StackExchangeRedis backplane.
It seems all message channels are prefixed with the type.FullName of the hub. So send-all, send-to-group, and send-to-user messages are always scoped to the Hub type doing the sending.
So all mesages are always (and only) received by hubs of the same type. And no messages sent from other hub types should ever be received by said hub!
Please correct me if I'm wrong about this. Having the wrong paradigm will reak havoc on my backplane. Which I hope to make public as soon as it's done.

Performance impacts of running three simultaneous hub connections?

I am building a web application that current utilizes two SignalR hubs:
ChatHub - User communication
ControlHub - User manipulates controls and receives responses from server
I want to add a third hub: GuideHub that will be responsible for determining whether or not a user has completed a set of tasks that they are assigned on the website. Technically, this hub will be active whenever ChatHub is active (they share a page element) but they serve thematically different purposes. Generally, users will only be actively communicating across one hub at a time.
I know that premature optimization is usually no good, in this scenario, I need to plan ahead about how I am going to enable these features to scale well. Is this architecture scale-able or should I combine ControlHub and GuideHub to reduce the number of open connections users will have?
2.x support multiple hubs over one connection
http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#multiplehubs

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: Why choose Hub vs. Persistent Connection?

I've been searching and reading up on SignalR recently and, while I see a lot of explanation of what the difference is between Hubs and Persistent Connections I haven't been able to get my head around the next level, which is why would I choose one approach over the other?
From what I see in the Connection and Hubs section it seems that Hubs provide a topic system overlaying the lower-level persistent connections.
From the highly up-voted comment below:
Partially correct. You can get topics or groups in persistent connections as well. The big difference is dispatching different types of messages. For example you have different kinds of messages and you want to send different kinds of payloads. With persistent connections you have to embed the message type in the payload (see Raw sample) but hubs gives you the ability to do RPC over a connection (lets you call methods on on the client from the server and from the server to the client). Another big thing is model binding. Hubs allow you to pass strongly typed parameters to methods.
The example used in the documentation uses a chat room metaphor, where users can join a specific room and then only get messages from other users in the same room. More generically your code subscribes to a topic and then get just messages published to that topic. With the persistent connections you'd get all messages.
You could easily build your own topic system on top of the persistent connections, but in this case the SignalR team did the work for you already.
The main difference is that you can't do RPC with PersistentConnection, you can only send raw data. So instead of sending messages from the server like this
Clients.All.addNewMessageToPage(name, message);
you'd have to send an object with Connection.Broadcast() or Connection.Send() and then the client would have to decide what to do with that. You could, for example, send an object like this:
Connection.Broadcast(new {
method: "addNewMessageToPage",
name: "Albert",
message: "Hello"
});
And on the client, instead of simply defining
yourHub.client.addNewMessageToPage = function(name, message) {
// things and stuff
};
you'd have to add a callback to handle all incoming messages:
function addNewMessageToPage(name, message) {
// things and stuff
}
connection.received(function (data) {
var method = data.method;
window[method](data.name, data.message);
});
You'd have to do the same kind of dispatching on the server side in the OnReceived method. You'd also have to deserialize the data string there instead of receiving the strongly typed objects as you do with hub methods.
There aren't many reasons to choose PersistentConnection over Hubs. One reason I'm aware of is that it is possible to send preserialized JSON via PersistentConnection, which you can't do using hubs. In certain situations, this might be a relevant performance benefit.
Apart from that, see this quote from the documentation:
Choosing a communication model
Most applications should use the Hubs API. The Connections API could
be used in the following circumstances:
The format of the actual message sent needs to be specified.
The developer prefers to work with a messaging and dispatching model
rather than a remote invocation model.
An existing application that uses a messaging model is being ported to use SignalR.
Depending on your message structure, you might also get small perfomance benefits from using PersistentConnection.
You may want to take a look at the SignalR samples, specifically this here.
There are two ways to use SignalR: you can access it at a low level by overriding its PersistentConnection class, which gives you a lot of control over it; or you can let SignalR do all of the heavy lifting for you, by using the high level ‘Hubs’.
Persistent Connection is a lower level API, you can perform actions on more specific time when the connection is opened or closed, in most applications the Hub is the best choice
There are three major points to consider when comparing these two:
Message Format
Communication model
SignalR customization
With hubs message formatting is basically handled from you but with persistent connections the message is raw and has be tokenized and parsed back and forth. If the message size is important then also note that the payload of a persistent connection is much less that that of a hub.
When it comes to the communication model persistent connections basically have a function for sending and receiving messaging while hubs take a remote procedure call model with unique function per requirement.
When it comes to customization since persistent connections are more low level they may give you more control over customization.

SOAP Web Service on ASP.NET - Multiple Connection - Static Values - XMLRPC API

Have a SOAP Web Service that encapsulates calls to a 3rd party API... so our application can simply call my service and then my service handles all the various calls to the API. Works just fine.
However, we've hit a problem where the API we're connecting to allows a max of 10 connections at any given time for a given set of credentials.
Connections at most take a couple of seconds to process, but when we go live, we could in theory have users that max out this. So we've created multiple accounts (5) to the API giving us 50 connections across the 5 users.
How does ASP.NET handle connections to the Web service? I know it works asynchronously, but does it spawn multiple instances of my class or reuse the same class. Will variables persist across instances (i.e Will static variables work)?
What I need to do is if a call to the API fails on Client1, rollover to Client2 (or Clients[0], Clients[1]) etc... Sadly I have no way to detect if a given Client is out of connections at any given moment. I could poll it with a test call, but that would take time and be no guarantee the the client has a connection available when I make the call.
The API I'm calling is via XMLRPC Proxy class (CookComputing). Is the "connection" made when the Client is created or when the call is made, passing along the credentials?
public static IVoicestar GetClient(string userID, string password)
{
IVoicestar client = XmlRpcProxyGen.Create<IVoicestar>();
client.Credentials = new NetworkCredential(userID, password);
return client;
}
Seems from this that the credentials simply "ride along" until I make a call via Client.MethodCall() and then the connection is made.
If you are using ASP.NET Web Services (asmx) then it would spawn a new instance of your web service class for each request. In case WCF based web services, you can control the instancing /concurrency using attributes/configuartion (see this article) - you have three instancing modes possible - per call, per session and singleton.
Irrespective of what you are using, you can always implement your own pooling mechanism to pool your API connection. You already have a factory method to get the API client - just put call to pooling layer within method.
Normally Windows XP and Windows 7 have a limit of 10 concurrent TCP/IP connections. Maybe that's it. Be sure to work in a windows server version.

Resources