How can I use SignalR as Azure Service Bus transport? - signalr

I'm trying to find an alternative to using REST to read Azure Service Bus Topic Subscriptions from the browser. Seems like SignalR would be a natural for this but I can't seem to find anyone that has done it. I'm not talking about scale-out, just a SignalR Hub that would relay a set of Service Bus functions back and forth to the browser. I'm thinking of functions like, addReceiver(string topic, string subscriptionID);defineSubscription(string name, string subscriptionRule);deleteSubscription(string name);postMessageToTopic(string topic, string message);addReceiver would initiate an async receive on the subscription. Each time a message came available from Service Bus, a function would be called on the JS client.

Here's some code to point people in the right direction.
namespace SBTester
{
public class SBHub : Hub
{
public void AddReceiver(string topic, string subscriptionName, string subscriptionFilter)
{
string messageData;
TopicConnector.Initialize( topic,
Context.ConnectionId + "." + subscriptionName,
subscriptionFilter);
// Initiate receive loop on Service Bus
TopicConnector.SBClient.OnMessage((receivedMessage) =>
{
try
{
// Process the message
messageData = receivedMessage.GetBody<string>();
Clients.Caller.onMessage(topic, messageData);
}
catch
{
// Handle any message processing specific exceptions here
}
});
}
public void DefineSubscription(string topic, string subscriptionRule)
{
// Call Service Bus to create Subscription on the Specified topic
}
public void PostMessageToTopic(string topic, string message)
{
// Call Service Bus to send a message
Clients.All.addNewMessageToPage(topic, message);
}
}
}

From your Hub code you could directly call the Service Bus APIs to send messages or directly use Service Bus APIs from JavaScript/Browsers: http://developers.de/blogs/damir_dobric/archive/2014/03/27/microsoft-azure-service-bus-receiving-of-messages-from-queue-and-topic-with-javascript.aspx

Related

Read message with a consumer connected to an existing bus

I'm trying to understand how to connect consumers to an existing bus like explained here, but I don't understand how this thing should work
my code:
public interface IMessage
{
string Text { get; set; }
}
public class Program
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri("rabbitmq://localhost"), h =>
{
h.Username("guest");
h.Password("guest");
});
});
bus.Start();
bus.ConnectConsumer<TestConsumer>();
bus.Publish<IMessage>(new { Text = "Hi" });
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
public class TestConsumer : IConsumer<IMessage>
{
public Task Consume(ConsumeContext<IMessage> context)
{
Console.Out.WriteLineAsync($"Received: {context.Message.Text}");
return Task.CompletedTask;
}
}
I don't understand where the message is sent since aren't there any queue specified, and therefore I don't know how the message can be consumed.
Is there a way to specify on what queue the message will be sent and written OUTSIDE the bus definition?
What am I missing?
What you are publishing here
bus.Publish(new { Text = "Hi" });
is an anonymous type, not IMessage to which you are subscribed, i think you need to create IMessage interface, then create some class implementing IMessage and send over the bus
The docs you linked state that published messages are not received by the temporary queue and it has a limited set of use cases.
As far as I understand there's a distinction between senders and consumers in Mass Transit.
Senders send messages to an exchange not to queues.
Consumers are responsible for creating a queue and binding this queue to an exchange. The queue-exchange binding is done auto-magically based on the type of the message.
For this reason it's important that your consumers are created first. If the a message is sent without a consumer having created a queue the message has nowhere to go and will be lost.
Instead of using:
bus.ConnectConsumer<TestConsumer>();
Consider using:
sbc.ReceiveEndpoint("queue-name", e => e.Consumer<TestConsumer>());
That will create a queue named "queue-name" and connect your consumer to it.
If you need to connect a receive endpoint to the bus after the fact, you can use:
host.ConnectReceiveEndpoint("queue-name", e => e.Consumer<TestConsumer>());
You'll need to save the host from the configuration, but it will allow you to add the receive endpoint after the bus is started.

SignalR cannot send a message to all connected clients

I am trying to using SignalR to send a message to all connected clients.
I have found several examples and added what I think to be all the required bits. I did successfully get my client to connect to my Hub. I cannot get my server to connect to my Hub and send a message to all the connected clients.
When I call DatabaseChangeListener::Notify() it never hits the code in the Hub.
Can anyone suggest what else I need to do?
I am using .NET Core 2.1 preview 2 in web application with React and Redux.
I am using SignalR 1.0.0-preview2-final
I am using SignalR.Client 1.0.0-preview2-final
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// remove all other code for this question
services.AddSignalR();
}
public void Configure(IApplicationBuilder app)
{
// remove all other code for this question
app.UseSignalR(routes =>
{
routes.MapHub<SignalRHub>("/hubs/update");
});
}
My Hub
[Authorize]
public class SignalRHub : Hub
{
public async Task Send(string message)
{
await Clients.All.SendAsync("SendMessage", Context.User.Identity.Name, message);
}
}
My class to notify clients
public class DatabaseChangeListener : IDatabaseChangeListener
{
private readonly IHubContext<SignalRHub> _hubContext;
public DatabaseChangeListener(IHubContext<SignalRHub> hubContext)
{
_hubContext = hubContext;
}
public void Notify()
{
_hubContext.Clients.All.SendAsync("SendMessage", "something changed, Yo");
}
}
You need to make the connection to the hub via client side and then by using your _hubContext, you should be able to send a message to the client based off of the connection made to the hub.
Connection to the hub from client side using JS.
const connection = new signalR.HubConnectionBuilder()
.withURL("/YourHub")
.build();
Then after the connection is made, you can make the method to send a message to the client from the server using JS as well.
connection.on("SendMessage", message => {
document.getElementById("IdOfElementToDisplayMessage").innerHTML = message;
});
Finally add:
connection.start().catch(err => console.error(err.toString()));
Now you have established the connection to the hub via client side and can now reference the connection to the hub from the IHubContext.
To send a message from the server to the client you can use _hubContext.
In your case you can call Notify() and then await _hubContext.Clients.All.SendAsync("SendMessage", "something changed, Yo"); which should send your message to the SendMessage method created in JS: connection.on("SendMessage", message => { ...
If your _hubContext variable is null during the execution then the injection of the IHubContext needs to be checked.

Send data from a Javafx-Client over Websockets to EventBus

I have a Socket handler in Vert.x and I know how to send data through the EventBus in a client-to-server (from Web Browser to Web Server) and server-component-to-server-component fashions.
Now I have a JavaFX-Client connected to the Vert.x Socket handler through websockets:
public void start() {
vertx.createHttpClient()
.setHost(Main.SOCKET_SERVER)
.setPort(8080)
.connectWebsocket("/chat/service", new Handler<WebSocket>() {
#Override
public void handle(WebSocket websocket) {
ws = websocket;
websocket.dataHandler(new Handler<Buffer>() {
#Override
public void handle(Buffer data) {
System.out.println("Received Data");
}
});
//...
// use ws for authentification
ws.writeTextFrame("doAuthentification");
//...
}
}
}
The Socket is connected to "/chat/service".
Now I want to use this Websocket to call different Services from Vert.x. I know that EventBus is not working from JavaFX-Client.
On the server:
ws.dataHandler(new Handler<Buffer>() {
#Override
public void handle(final Buffer data) {
String text = data.toString();
if(text.contentEquals("doAuthentification")){
logger.info("doAuthentification()");
doAuthentification();
}
// ...
}
}
I can now send "commands" like doAuthentification through the WebSocket, then, on server side and when that command is received, I can use the EventBus to process it further.
What would be the correct way using it from a client. Ideas?
Since you application is packaged as a standalone application is not deployed as in a Vert.x instance, you won't be able to call the event-bus since it is a Vert.x specific feature.
Your method to go would be, as you already tyried, to communicate to your Vert.x application in a standard way, through socket, or http for example (I would recommend HTTP and a RESTful application style), and send messages through an entry point that will be later on transferred to the appropriate verticles.
You may need to configure many path based handlers, maybe using a regex capture group inside, and let each handler choose the appropriate schema to delegate events, instead of having a single handler based on hardcoded messages.

Using SignalR to send Real Time Notification

I want to create a new system to send real time trade execution messages to users using SignalR. In the old system, each client connects to the trading server using Java Applet TCP connection.
I use the following tutorial as reference
http://www.asp.net/signalr/overview/getting-started/tutorial-server-broadcast-with-signalr
There is a line of code in the StockTicker constructor to update the stock prices:
_timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
However, I need to update trade execution in real time instead of updating stock prices per 250ms.
Is it okay to create TCP connection to my trading server per client in the constructor? It seems that in the sample code, the constructor of StockTicker (i.e. my TradingManager) will be called one time only. But in my design, I want to create a TCP connection per client. How should I change the code to do this?
Here is my code:
TradingHub.cs
public class TradingHub : Hub
{
private readonly TradingManager _tradingManager;
public TradingHub() : this(TradingManager.Instance) { }
public TradingHub(TradingManager tradingManager)
{
_tradingManager = tradingManager;
}
...
}
TradingManager.cs
public class TradingManager
{
// Singleton instance
private readonly static Lazy<TradingManager> _instance = new Lazy<TradingManager>(
() => new TradingManager());
...
public static TradingManager Instance{ get{ return _instance.Value; } }
public TradingManager()
{
...
this.apiConnector.MessageReceived += new CustomEventHandler(this.api_MessageReceived);
init();
}
private IHubConnectionContext<dynamic> Clients { get; set; }
private void init()
{
TradingSession tradingSession = getLoginSession(user);
// connect to trading server using TCP connection
this.apiConnector.ensureConnected(host, port, tradingSession);
// send keep alive message to trading server periodically
_timer = new Timer(sendKeepAlive, null, _updateInterval, _updateInterval);
}
private void api_MessageReceived(object sender, CustomEventArgs e)
{
// when web server receives trade execution from server, send out the message immediately
Clients.Caller.SendTradeExecutionMessage(......);
}
public static TradingSession getLoginSession(string user)
{
...
}
private void sendKeepAlive(object state)
{
...
}
}
If you were to make a new TradingManager in your Hub constructor instead of referencing a singleton, you would be creating more than one TradingManager per SignalR connection. Hubs are reinstantiated per method call. Every time you invoke a hub method or a hub event is called (e.g. OnConnected, OnReconnected, OnDisconnected), your constructor will be called.
However, OnConnected is only called once per SignalR connection. By the way, SignalR connections are completely orthogonal to TCP connections. With the long polling transport, for example, a new HTTP request is sent each time a message is received.
I think you want to create a new TradingManager instance each time OnConnected is called and potentially associate it with the client's Context.ConnectionId and store it (perhaps in a ConcurrentDictionary) so you can retrieve it using the connection id when your Hub methods are called. You can then dereference the stored TradingManager instance for a given connection id in OnDisconnected.
You can learn more about SignalR connections at:
http://www.asp.net/signalr/overview/guide-to-the-api/handling-connection-lifetime-events
You can learn more about the Hub API and the On* methods at:
http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server#connectionlifetime

Get names of Online users connected to a Server

I am new to asp.net. I have gone through this link which has shown how to count the online users connected to a server using asp.net. (which is working when I tried)
My question is: What should I change in that code (Global.asax) so that It shows all the names of the connected users instead of counting them.
I created a chat application which stores the name of the connected user in a variable chatUsername in js file as shown below:
js file
var chatUsername = window.prompt("Enter Username:", "");
//
chat.client.addMessage = //Function
//
chat.server.send(chatUsername);
.aspx.cs file
//Using SignalR (I think this doesnt matter)
public class Chat : Hub
{
public void Send(string from)
{
// Call the addMessage method on all clients
Clients.All.addMessage(from);
}
}
You can find my complete code here
EDIT: Please provide a simple example related only to asp.net or signalr (no other technologies like MVC)
Please help.
Edit: following code refers to SignalR v0.5, not the latest 1.0Alpha2, but I believe the reasoning is the same
To do this you need to add several steps to your SignalR connection process, both in the server and in the client:
on the server side:
on application start-up, for example, you can instantiate a static in-memory repository (can be a dictionary of ) that will serve as the user repository to store all currently connected users.
In the hub you need to handle the Disconnect event (when a user disconnects, needs to be removed from the user repository as well) and notify all other clients that this user disconnected
In the hub you need to add two new methods (the names can be whatever you want) that will help client connect to the system and get the list of currently connected users:
GetConnectedUsers() that just returns a collection of connected users
Joined() where the Hub will create a new User, using the info stored in the round-trip state (the username selected by the client) and the SignalR connection ID, and add the newly created user to the in-memory repository.
on the client side:
First you need to instantiate the javascript object that relates to your server-side hub
var chat = $.connection.chat;
chat.username = chatUsername;
Then implements all the functions that will be called by the hub and finally connect to the hub:
// Step 1: Start the connection
// Step 2: Get all currenlty connected users
// Step 3: Join to the chat and notify all the clients (me included) that there is a new user connected
$.connection.hub.start()
.done(function () {
chat.getConnectedUsers()
.done(/*display your contacts*/);
});
}).done(function () {
chat.joined();
});
});
});
If you are asking why we need to add a stage like "chat.joined()" is because in the method on the Hub that is handling the connection event, the round-trip state is not yet available, so the hub cannot retrieve the username chosen by the user.
Anyway I made a blog post to show more in detail how to create a basic SignalR chat web application using Asp.Net MVC, and it is available at:
http://thewayofcode.wordpress.com/2012/07/24/chatr-just-another-chat-application-using-signalr/
In the post you will also find a link to the github repository where the source is published.
I hope this helps.
Valerio
Apparently, you are using Signal-R - so try tracking state of online users (i.e. connected clients) in java-script itself. Use Connected/Disconnected/Reconnected server side events to broadcast to all clients - from documentation:
public class Chat : Hub
{
public override Task OnConnected()
{
return Clients.All.joined(Context.ConnectionId, DateTime.Now.ToString());
}
public override Task OnDisconnected()
{
return Clients.All.leave(Context.ConnectionId, DateTime.Now.ToString());
}
public override Task OnReconnected()
{
return Clients.All.rejoined(Context.ConnectionId, DateTime.Now.ToString());
}
}
A global server side store (for example - a static dictionary) can be used to store state against the connection id - that way, this dictionary can give you users for needed connection ids. For example,
// dis-claimer: untested code - just to give the idea/hint/outline
public class Chat : Hub
{
// change to use Concurrent Dictionary (or do thread-safe access)
static Dictionary<string, User> _users = new Dictionary<string, User>()
// call from client when it goes online
public void Join(string name)
{
var connId = this.Context.ConnectionId;
__users.Add(connId, new User(connId, name));
}
public override Task OnConnected()
{
return Clients.All.joined(_users[Context.ConnectionId], DateTime.Now.ToString());
}
public override Task OnDisconnected()
{
var user = _users[Context.ConnectionId];
_users.Remove(Context.ConnectionId);
return Clients.All.leave(user, DateTime.Now.ToString());
}
public List<User> GetUsers()
{
return _users.Values.ToList()
}
}
I think this should work for you :-
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["OnlineUsers"] = 0;
List<string> list = new List<string>();
}
//First check if it is Authenticated request:-
void Session_Start(object sender, EventArgs e)
{
if(Request.IsAuthenticated)
list.Add(User.Identity.Name);
//your rest of code .......
}
list will return you all the username who are online :-

Resources