Pushing message to multiple clients through SignalR - signalr

I am having a Chat app in which i have to push a message to all other users in that conversation, signalr works fine when i push message to all clients over 100 but when i loop over those 100 connections and send message individually then message receives slowly on client side and when I start to push messages faster then my server IIS worker goes to 100% CPU usage and message receiving on client end become more slower,
So help me finding the best way to send messages to specific user from signalr more than 100 users at same time

If it's a chat application, you may want to look into groups in SignalR. Simply create a new group for each "conversation" and join the users to the group.
From the documentation:
public class ContosoChatHub : Hub
{
public Task JoinRoom(string roomName)
{
return Groups.Add(Context.ConnectionId, roomName);
}
public Task LeaveRoom(string roomName)
{
return Groups.Remove(Context.ConnectionId, roomName);
}
}
Then you can simply message the group:
Clients.Group(groupName).addChatMessage(name, message);

Related

OnDisconected not beein called, SignalR with slow Internet connections

I'm new using SignalR and I got stuck with a issue in OnDisconect event.
My project is kind of a cinema seats picker, that a user can select a seat and make a order.
When the user select the seat make it unavailable, in my front-end seat button send a post to my API that register de data of the seat and the ConnectionId of the user, and I use that connection Id when the user disconect (OnDisconectAsync in my API) without pay to make my seat available again.
The problem that I found was when a user has a Slow 3G connection, and spam checking and unchecking de reservation button, the SignalR connection get slow enought to throw this error:
"Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled."
And then "disconect" the user without calling OnDisconectAsyn() on my API and generating a new ConnectionId to my user.
So now if the user close de window the seats never comes back because the older ConnectionId got lost.
OnDisconect Method:
public override async Task OnDisconnectedAsync(Exception exception)
{
IOrderApplication order = scope.ServiceProvider.GetRequiredService<IOrderApplication>();
List<ViewOrderDto> orderList = await order.DeleteByConnectionId(Context.ConnectionId);
foreach (ViewOrderDto order in orderList)
{
await SendMessage(order);
}
await base.OnDisconnectedAsync(exception);
}
Controller Post:
[HttpPost]
public async Task<IActionResult> PostAsync([FromBody] PostOrderDto postOrderDto)
{
if (!ModelState.IsValid)
return CustomResponseFail(ModelState);
if (string.IsNullOrEmpty(postOrderDto.ConnectionId))
{
NotifyError("ConnectionId can not be null.");
return CustomResponseFail(ModelState);
}
ViewOrderDto order = await rderApplication.PostAsync(postOrderDto);
if (order is null)
{
NotifyError("Error when making a order.");
return CustomResponseFail(ModelState);
}
await messageHub.SendMessage(order);
// await pedidoBackgroundService.UpdateOrders();
return CustomResponseSuccess(order, "Succeded order!");
}
First Client Before the Error:
Second Client:
First Client after the error (after spam and get connection lost):
I don't know if there is a better way to make this, or a way to solve this problem but i'm really thankfull for all the help!

Azure SignalR Notifications from Service Bus Qeue

We have a Service Bus queue that handles multiple message topics/subscriptions and what we'd like to be able to do is when certain messages have been handled is to notify connected users that a message has been handled.
The message handling takes place in a simple console app but we're not sure how to create a connection to our Azure SignalR service and send a message once it's been processed.
I believe the simplest most scalable approach would be to have a simple azure function to do this.
You would just have to use the Service Bus Trigger which runs your function when a message arrives and use the SignalR Service Output Binding to send the message to your users.
Your function could be as simple as the following
[FunctionName("ServiceBusQueueTriggerCSharp")]
public static void Run(
[ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBusConnection")]
string myQueueItem,
[SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages
ILogger log)
{
return signalRMessages.AddAsync(
new SignalRMessage
{
Target = "newMessage",
Arguments = new [] { myQueueItem }
});
}

Using SignalR to send message to client from Azure Worker Role

I'm working an ASP.net MVC cloud service project running two roles, a web role and a worker role. One of the pages in the web role initiate a request to build an APK file, building an APK file on the server can take anywhere from 1-5 minutes. So we came up with the following flow:
The user initiate the APK building process on the page.
The request is routed to our mvc action, creating a new message on an Azure Storage Queue.
The Worker role is always polling from the queue and starts the APK building process. Now that the APK is ready we want ideally to notify the user by:
(a) sending an email, which is working now. and (b) notifying the user on the page using SignalR.
Our problem is now in the SignalR part, how can we notify the user on the page that the APK is ready and he can download it.
EDIT - Copying contents of the first comment for the sake of completeness -
I've looked the question again and I understand that you are using a worker role to poll the queue. In this case, you can make your work role a .Net SignalR client that connects to the APK signalR hub on the web role. The signlaR hub on the web role can simple forward any message it receives from the .Net client to the javascript client (browser).
I would recommend going through the below links
Hubs API Guide - Server
Hubs API Guide - Javascript Client
before going through rest of the answer.
As can be understood from the above two links, SignalR enables the server to 'push' data to the client. In order for this to happen, you require two things -
A signalR hub - this is the 'hub' to which clients can subscribe to in order to receive messages.
A client connected to the hub
Your signalR hub on the server can look something like this -
public class APKHub : Hub
{
public async Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
Clients.Group(groupName).sendMessage(Context.User.Identity.Name + " joined.");
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
public void NotifyUser(string userId)
{
this.Clients.Group(userId).notify();
}
}
On the client, your code might look something like this -
var notificationHandler = function () {
var url;
var user;
var init = function (notificationUrl, userId) {
url = notificationUrl;
user = userId;
connectToAPKHub();
}
var connectToAPKHub = function () {
$.connection.hub.url = url;
var apk= $.connection.apkHub;
apk.client.notifyUser = function (user) {
console.log(user);
}
apk.client.addMessage = function (message) {
console.log(message);
}
$.connection.hub.start().done(function () {
console.log('connected to apkhub');
apk.server.joinGroup(user);
})
}
return {
init: init
}
}();
The notificationUrl is the URL that the signalR server is listening to.
This sets up your basic hub on the server and you should now be able to connect your client to the signalR hub. When the APK is built, you can use the following code (place it anywhere - for ex - in a controller action) to actually push a message to the concerned client -
var apkHub = GlobalHost.ConnectionManager.GetHubContext<APKHub>();
apkHub.Clients.Group(groupName).notifyUser(groupName);
The groupName can be an identifier that uniquely identifies a user.
Hope this helps.

SignalR Long Running Process

I have setup a SignalR hub which has the following method:
public void SomeFunction(int SomeID)
{
try
{
Thread.Sleep(600000);
Clients.Caller.sendComplete("Complete");
}
catch (Exception ex)
{
// Exception Handling
}
finally
{
// Some Actions
}
m_Logger.Trace("*****Trying To Exit*****");
}
The issue I am having is that SignalR initiates and defaults to Server Sent Events and then hangs. Even though the function/method exits minutes later (10 minutes) the method is initiated again ( > 3 minutes) even when the sendComplete and hub.stop() methods are initiated/called on the client prior. Should the user stay on the page the initial "/send?" request stays open indefinitely. Any assistance is greatly appreciated.
To avoid blocking the method for so long, you could use a Taskand call the client method asynchronously.
public void SomeFunction(Int32 id)
{
var connectionId = this.Context.ConnectionId;
Task.Delay(600000).ContinueWith(t =>
{
var message = String.Format("The operation has completed. The ID was: {0}.", id);
var context = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();
context.Clients.Client(connectionId).SendComplete(message);
});
}
Hubs are created when request arrives and destroyed after response is sent down the wire, so in the continuation task, you need to create a new context for yourself to be able to work with a client by their connection identifier, since the original hub instance will no longer be around to provide you with the Clients method.
Also note that you can leverage the nicer syntax that uses async and await keywords for describing asynchronous program flow. See examples at The ASP.NET Site's SignalR Hubs API Guide.

Use Signalr to have a facebook like notification system

I want to implement a facebook like notification system in ASP.NET MVC 3 : notifications are sent to a specific user to notify him for an action on one of his items.
Is signalr suited for such requirement?
How could i send a notification to a specific user (all opened sessions of this user) using SignalR?
Edit
Ok, Here what i did
In the client side
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
var username = '#Html.ViewContext.HttpContext.User.Identity.Name';
// Declare a function on the chat hub so the server can invoke it
chat.addMessage = function (message) {
$('#messages').append('<li>' + message + '</li>');
};
// Start the connection
$.connection.hub.start(function (){
chat.join(username);
});
});
In the server side
public class Chat : Hub
{
public void Join(string username)
{
AddToGroup(username);
}
}
And every time i need to notify a user in the controller i do the following:
IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<Chat>();
clients[username].addMessage("test");
Yes, SignalR is a good choice for that. Take a look at the documentation regarding Hubs (server and JS client).
You need to implement the server logic to associate your client's session with SignalR's session. You can use groups to notify all the open sessions of each user.
It is appropriate for this or you use polling, those are the two choices.
Heres a brand new video from today on this:
http://channel9.msdn.com/Shows/Web+Camps+TV/Damian-Edwards-and-David-Fowler-Demonstrate-SignalR?utm_source=dlvr.it&utm_medium=twitter

Resources