33

If I have multiple pages that could use multiple hub classes, what is the best way to manage this?

For instance:

  • Is it bad to navigate to another page in the website and essentially "reopen" the connection to the same hub class that was open on the previous page?

  • Am I correct in thinking that opening multiple hub connections on a page is ok because they are all unified in one connection, even if they are different hub classes?

Jesse Hufstetler
  • 583
  • 7
  • 13
anthonypliu
  • 12,179
  • 28
  • 92
  • 154

3 Answers3

32

You can have multiple hubs sharing one connection on your site. SignalR 2.0 was updated to handle multiple hubs over one signlar connection with no lost in performance.

Official docs: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#multiplehubs

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.

Sam
  • 1,096
  • 11
  • 10
15

To start with Hubs read the WIKI entry for Hubs and Client Side of Hubs. There are couple of things according to the context of a multiple pages.

  1. When you start a hub it gives your client an ID which stays the same for that hub (someone can confirm with example) over multiple pages.
  2. Its not bad to reopen the connection to the same hub. You might have hub.start client side method running on all pages however if its one client opening multiple windows or going from one page to another you will have same connection ID on that hub so you can keep in contact. If it was multiple hubs then you have to manage hubs as well as connection IDs. So this question is like "Is it bad to have multiple ISPs serving my internet connection for different websites". You can have them but it is an overkill. A single ISP can server all pages to you as well.
  3. Multiple hubs on a single page is not ideal but it will work. Again the answer need a bit of context to the problem but in general you can differentiate between various requests on same connection ID via groups or using other parameter based approach. Having two hubs on same page may take more resources (need to test this) than using parameters or groups to separate different areas of messaging.

Example:

You have a page that has two parts, a graph which shows real time user activity and an area to see real time data changes done by users as a table. Will you create two hubs or two groups or what? There are other pages which use same graph and data table.

My Solution:

  1. I will create a single hub for the application to recieve real time data from the server.
  2. I will create different methods on server to send graph points and data tables.
  3. I will create client side method on all pages that use these graphs to communicate with server methods on the same hub.

When you switch between pages the client will connect with same hub and request getGraph or getDataTable or both and populate its client with relevant data. Similarly on server when data changes you can call client side method to update all clients or group of them (lets add this complexity)

Assume you have students and teachers looking at your application. They require different level of data access. You can use groups to keep them separate on the hub so you are not sending teachers info to students and students data to teachers.

  1. On your hub join you can add them to join a group associated with their role or any differentiating function.
  2. When you send to all clients , now you can send to group of clients that is teachers or students. Not creating another hub for teachers or students, they are all on same hub.

Coming back to your question of "is it bad" and "is ok" this is difficult to establish without context of actual application. I cant think of a scenario where you can justify multiple hubs apart from Performance.

James Skemp
  • 8,018
  • 9
  • 64
  • 107
Farrukh Subhani
  • 2,018
  • 1
  • 17
  • 25
  • The application has different hubs for different kinds of data. Our application is quite large, and it's a logical separation of concerns. Some front end clients only need a subset of live data, and it makes sense to have a hub they can connect to, rather than managing groups. – Carlos Rodriguez Oct 30 '15 at 18:32
  • As suggested by @sam in answer you can do that and it is explained here http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server#multiplehubs – Farrukh Subhani Nov 09 '15 at 16:56
  • I'm aware, and that's how I've set up my application. I was just commenting because you said you can't think of a scenario where you can justify multiple hubs – Carlos Rodriguez Nov 09 '15 at 21:06
  • 1
    No problem, my comment was back in 2012 when it wasnt available as a choice. Now it is. – Farrukh Subhani Nov 09 '15 at 21:07
  • 1
    Your 2nd point was exactly what I was looking for, it doesn't hurt to call `$.connection.hub.start()` multiple times – Alexander Derck Feb 14 '17 at 14:32
15

SignalR Core - NOT POSSIBLE

Unfortunately this is not possible anymore in the new 'Core' version of SignalR

https://github.com/aspnet/SignalR/issues/456

https://github.com/aspnet/SignalR/issues/955


Notes: Issues with iOS and self signed certificates

On iOS there's a limit of FOUR connections per server.

Now there isn't this limit for websockets (I think it may be 32 but not sure). However I am using a self signed certificate which has all kinds of issues in Safari - so it actually drops down to long polling (and it's not obvious it's done so).

So I ended up with these connections:

  • 1 - Angular / Webpack hot reload socket
  • 2 - Web API calls
  • 3 - Hub number one
  • 4 - Hub number two
  • 5 - #&#&#$&#$&

So if I had ONLY three hubs the whole Safari page would lock up with a blue bar. Even Web API calls got blocked.

Note: With HTTP/2 this limit is gone but you're probably better off limiting yourself to one hub especially if you're using hot reload. Plus setting up HTTP/2 in development isn't necessarily a trivial task.


So how to fix?

First (temporarily) set your hub to only accept websockets. This will give you an error in Safari (make sure errors are being caught and shown in an alert dialog).

routes.MapHub<SignalRHub>("/rt", options =>
{
     // when run in debug mode only WebSockets are allowed
     if (Debugger.IsAttached) {
        options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
     }
});

Now you'll be able to confirm the fix - run in debug mode, or remove the 'if'.

The problem with iOS is even if you accept a self signed certificate for https traffic - and get a nice little 'lock' symbol in the browser - it doesn't apply to the wss: protocol. So connections cannot be upgraded to wss, which is why they block at the max of 4.

Solution #1

If you can get everything down to one hub it's just easier :-)

I also realized that multiple hubs complicates reconnect logic if the connection is lost. One hub just makes this easier. If you're not careful you'll end up showing 3 dialog boxes saying 'Connection lost. Retry?' I'm switching to a single hub just because of this.

While I hate mixing everything, partial classes help and I personally don't have many SignalR methods anyway.

Solution #2

This is only relevant to debugging, and assumes you're using a https cert which you self-signed.

Use instead something like letsencrypt - or Cloudflare's argo tunnel to get a publically trusted cert. This will be fully trusted by Safari, so your connections will get upgraded to real web sockets.

Solution #3

Create a self signed ROOT certificate (CA) and then generate SSL certificates with the domain name from it.

This was trickier than I imagined. In the end it turned out I was missing Subject Type=CA in my root cert - which iOS requires. Without this 'extension' it will install your root the certificate as a profile, but won't allow you to select it for SSL.

Once you have the root cert installed Safari will work with websockets just fine.

Solution #4

Use http only. This wasn't an option for me because I use certain APIs like Facebook / Google / Payment and they require https.

Notes

  • Important: Now consider production. Realize that websockets may be unavailable for various reasons, so if you have 4 hubs that are connected on iOS this can still cause blocking. You're living dangerously.

Better to use one hub in the first place. BUT also best to get your cert installed properly so iOS will work with websockets.

How to create and install X.509 self signed certificates in Windows 10 without user interaction?

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 1
    This was giving me fits, thank you. By "partial classes", I assume you mean "public partial class MySingleHubForEverything : Microsoft.AspNet.SignalR.Hub"? – Eric Hirst Jul 27 '19 at 17:11
  • 1
    Exactly yes. Perhaps with a prefix for each method name eg. chat_sendMessage. Then multiple people can work on different files more easily and it feels less cluttered. – Simon_Weaver Jul 27 '19 at 18:29
  • This whole thing is a terrible design decision. Partial classes are a kludge, not a solution, around this design. And those are very limited, since you can't have a partial class across assemblies. Various architectural & code organization patterns & scenarios are right out the door thanks to this. Such as having feature-folders or feature-packages, and ensuring each feature defines it's own hub functions, nope. – Douglas Gaskell Oct 24 '21 at 00:50
  • @DouglasGaskell I'm not sure if you're referring primarily to MY design decision or the SignalR design decision. I started trying to create multiple hubs since it seemed to make more sense, but it backfired on me big time. For me partial classes were a perfect design decision since I don't use SignalR all that much and I needed to just move on. If you have a solution for a project that uses SignalR across many 'feature packages' would be interested to see what you came up with. – Simon_Weaver Oct 24 '21 at 20:58
  • 1
    Sorry, the signalr decision. It really puts you into a tight corner as far as how you can use it. There have been complaints for years about it, and the decision to move away from multiple hubs, 1 connection. Essentially being able to namespace and register functions. – Douglas Gaskell Oct 26 '21 at 04:50