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;
}