2

I want to send SignalR messages directly from my Azure WebJob to browser clients running on my web-server(s), so I've configured SignalR to use Azure Service Bus as a backplane on both the WebJob and my server. Messages sent from the WebJob are going to the Service Bus topic queues and are being picked up by the web-server but are not being sent on to connected SignalR web clients. I thought this would happen automatically, do I have to somehow subscribe to my WebJob SignalR messages from the web-server and manually propogate them to the browser clients?

I'd paste some code but I'm not sure what's relevant as I'm not receiving any errors. I can track messages from the WebJob through the service bus to my web-server but nothing reaches the SignalR browser clients running on each web-server.

Salient points;

  • Each Hub instance is registered using the same hub name
  • The WebJob and the web-server are both using the same Service Bus backplane connection string
  • I can see that messages are getting sent to the right topic, and the correct subscription is being read

Trace info from the WebJob:

SignalR.ServiceBusMessageBus Information: 0 : Subscribing to 5 topic(s) in the service bus... SignalR.ServiceBusMessageBus Information: 0 : Creation of a new topic client SIGNALR_TOPIC_SignalREvents_0 completed successfully. SignalR.ServiceBusMessageBus Information: 0 : Creation of a new subscription 2ea68ce5-58fe-4218-8768-e03675be8e74 for topic SIGNALR_TOPIC_SignalREvents_0 in the service bus completed successfully. SignalR.ServiceBusMessageBus Information: 0 : Creation of a message receive for subscription entity path SIGNALR_TOPIC_SignalREvents_0/Subscriptions/2ea68ce5-58fe-4218-8768-e03675be8e74 in the service bus completed successfully. SignalR.ScaleoutMessageBus Information: 0 : Stream(0) - Changed state from Initial to Open (x5) SignalR.ServiceBusMessageBus Information: 0 : Subscription to 5 topics in the service bus Topic service completed successfully. SignalR.ServiceBusMessageBus Verbose: 0 : Sending 68 bytes over Service Bus: {"H":"auctionHub","M":"Init","A":[{"Price":569.72,"Symbol":"GOOG"}]} SignalR.ServiceBusMessageBus Verbose: 0 : Sending 68 bytes over Service Bus: {"H":"auctionHub","M":"Init","A":[{"Price":577.38,"Symbol":"APPL"}]} SignalR.ServiceBusMessageBus Verbose: 0 : Receiving 68 bytes over Service Bus: {"H":"auctionHub","M":"Init","A":[{"Price":569.72,"Symbol":"GOOG"}]} SignalR.ScaleoutMessageBus Information: 0 : OnReceived(4, 65, 1)

Trace from the web-server

SignalR.ServiceBusMessageBus Information: 0 : Subscribing to 5 topic(s) in the service bus... SignalR.ServiceBusMessageBus Information: 0 : Creation of a new topic client SIGNALR_TOPIC_SignalREvents_0 completed successfully. SignalR.ServiceBusMessageBus Information: 0 : Creation of a new subscription e6afd744-7724-4ab5-8fd9-2717a62caf61 for topic SIGNALR_TOPIC_SignalREvents_0 in the service bus completed successfully. SignalR.ServiceBusMessageBus Information: 0 : Creation of a message receive for subscription entity path SIGNALR_TOPIC_SignalREvents_0/Subscriptions/e6afd744-7724-4ab5-8fd9-2717a62caf61 in the service bus completed successfully. SignalR.ScaleoutMessageBus Information: 0 : Stream(0) - Changed state from Initial to Open SignalR.ServiceBusMessageBus Information: 0 : Creation of a new topic client SIGNALR_TOPIC_SignalREvents_1 completed successfully. SignalR.ServiceBusMessageBus Information: 0 : Creation of a new subscription 2c155757-123f-44ba-ace7-96a6b390234e for topic SIGNALR_TOPIC_SignalREvents_1 in the service bus completed successfully. SignalR.ServiceBusMessageBus Information: 0 : Creation of a message receive for subscription entity path SIGNALR_TOPIC_SignalREvents_1/Subscriptions/2c155757-123f-44ba-ace7-96a6b390234e in the service bus completed successfully. SignalR.ScaleoutMessageBus Information: 0 : Stream(1) - Changed state from Initial to Open SignalR.ServiceBusMessageBus Information: 0 : Creation of a new topic client SIGNALR_TOPIC_SignalREvents_2 completed successfully. SignalR.Transports.TransportHeartBeat Information: 0 : Connection 869c61bb-5070-4c64-811a-9a3612eff981 is New.

SignalR.ServiceBusMessageBus Verbose: 0 : Receiving 37 bytes over Service Bus: {"H":"auctionHub","M":"Init","A":[1]} SignalR.ScaleoutMessageBus Information: 0 : OnReceived(4, 221, 1)

SignalR service-bus config code (same on WebJob and web-server)

var scaleOutConfig = new ServiceBusScaleoutConfiguration(ConfigurationManager.ConnectionStrings["AzureWebJobsServiceBus"].ConnectionString, "SignalREvents");
GlobalHost.DependencyResolver.UseServiceBus(scaleOutConfig);

If I had any hair left I'd be tearing it out. I've searched this extensively but wasn't able to find anything useful. Any pointers would be greatly appreciated.

Edit >>

[HubName("auctionHub")]
public class AuctionHub : Hub<IAuctionHubClientMethods>
{
    private readonly IAuctionHubService _auctionHubService;

    public AuctionHub(IAuctionHubService auctionHubService)
    {
        _auctionHubService = auctionHubService;
    }

    public override Task OnConnected()
    {
        var userId = Context.User.Identity.GetUserId();
        var connectionId = Context.ConnectionId;
        _auctionHubService.OnConnected(Groups, userId, connectionId);
        return base.OnConnected();
    }

public interface IAuctionHubClientMethods
{
    Task Heartbeat(string now);
    Task Init(AuctionViewModel eventId);
}

public class AuctionHubService : IAuctionHubService
{
    <snip>

    public AuctionHubService(IAuctionServices auctionServicesProxy, IQueryProcessor queryProcessor, ApplicationUserManager userManager)
    {
         <snip>
    }

    public void OnConnected(IGroupManager groups, string userId, string connectionId)
    {
        var user = _userManager.FindById(userId);
        var client = user.Client;
        var id = client?.Id;
        groups.Add(connectionId, user.Client.Id.ToString());
    }

    public void Init(int eventId, IHubCallerConnectionContext<IAuctionHubClientMethods> clients)
    {
        var query = new GetAuctionViewModelByEventIdQuery { EventId = eventId };
        var eventState = _queryProcessor.Process(query);
        eventState.Suppliers.ForEach(x => clients.Group(x.SupplierId.ToString()).Init(eventState));
    }
}

OnDisconnected and OnReconnected are untouched.

I am using SimpleInjector to supply the SignalR context as needed, would this implementation have any effect?

 var hubConfiguration = new HubConfiguration
        {
            EnableDetailedErrors = true,
            EnableJavaScriptProxies = false,
            Resolver = new SignalRDependencyResolver(MvcApplication.Container),
        };

app.MapSignalR<SignalRHubDispatcher>("/signalr", hubConfiguration);

public class SignalRHubDispatcher : HubDispatcher
{
    public SignalRHubDispatcher(Container container, HubConfiguration configuration) : base(configuration)
    {
        _container = container;
    }

    protected override Task OnConnected(IRequest request, string connectionId)
    {
        return Invoke(() => base.OnConnected(request, connectionId));
    }

    protected override Task OnReceived(IRequest request, string connectionId, string data)
    {
        return Invoke(() => base.OnReceived(request, connectionId, data));
    }

    protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
    {
        return Invoke(() => base.OnDisconnected(request, connectionId, stopCalled));
    }

    protected override Task OnReconnected(IRequest request, string connectionId)
    {
        return Invoke(() => base.OnReconnected(request, connectionId));
    }

    private async Task Invoke(Func<Task> method)
    {
        using (_container.BeginExecutionContextScope())
        {
            await method();
        }
    }

    private readonly Container _container;
}
keithl8041
  • 2,383
  • 20
  • 27
  • Can you post your notification hub, specifically how you then send the messages to the users, and how you are managing connections via `OnConnected`, `OnDisconnected` and `OnReconnected`. Also to confirm, you have load balanced webservers, correct? – Brendan Green Jul 10 '16 at 21:47
  • After some thought I'd just like to clarify that you're talking about the configuration concerning the web-server and the browser clients only, and nothing to do with the web-job? Let me know - I'll try figure out a concise method of posting the relevant code as an edit. So far I'm only running this in a dev environment, so no load-balanced production servers yet, just one WebJob and one web-server. – keithl8041 Jul 10 '16 at 22:03
  • The Hub that you have running in your **website**, on the **server side**. – Brendan Green Jul 10 '16 at 22:16
  • Added the relevant bits, let me know if I've missed something out. – keithl8041 Jul 11 '16 at 07:53
  • Any ideas, @BrendanGreen? Appreciate you taking a look. – keithl8041 Jul 12 '16 at 09:20
  • I take it you are using your `IGroupManager` to map authenticated users to their "signalR client connection" - firstly is this store the same store used by all instances of the website? Secondly, are you joining the user to the group in the browser? – Brendan Green Jul 12 '16 at 10:32
  • @BrendanGreen correct on both queries. None of the SignalR code gets fired when a message is received via ServiceBus (however it does fire on a request from the browser client and the client connects properly to the web-server), so there's a disconnect somewhere, even though the web-server is tracing the SignalR messages sent from the WebJob (which are addressed to Clients.All, so presumably that should be received by any connected Group user). – keithl8041 Jul 12 '16 at 11:05
  • Can we see the code that the WebJob is using to send the messages to signalR? In my case, I did not register anything for the scaleout (I'm using Redis) - I just send the message to the Hub (from an Azure Worker Role) and the hub then does the scale out to the other webserver instances via Redis. – Brendan Green Jul 12 '16 at 14:32

0 Answers0