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.
Related
I am trying to solve an issue whereby I need to combine third party events with the eventBus send and reply approach that Vertx provides for Standard and Worker Verticle setups. I am not sure if what I have laid out below is necessarily the correct approach.
Problem Statement:
I want to have standard verticle that sends a message to a worker verticle.
The worker verticle does some preprocessing and then uses a client method provided by a third party state management lib to publish an even (in an async manner). The result of which is only whether or not the event was successfully received or not (but does not contain any further info around processing etc).
Further processing takes place when the third party state management lib receives the event(this all happens on a separate thread) and a success or failure can occur at which point another event will be published to the cluster management tools output channel.
From the output channel listener I then want to be able to use the event to somehow use the message.reply() on the worker verticle to send back a response to the standard verticle that made the original request, thereby closing the loop of the entire request lifecycle but also using the async approach that vertx is built to use.
Now I conceptually know how to do 90% of what is described here but the missing piece for me is how to coordinate the event on the output channel listener and connect this to the worker verticle so that I can trigger the message.reply.
I have looked at possibly using SharedData and Clustering that Vertx has but was wondering if there is possibly another approach.
I have put a possible example implementation but would really appreciate if anyone has any insights/thoughts into how this can be accomplished and if I am on the right track.
class Order(val id: String)
class OrderCommand(val order: Order) : Serializable {
companion object {
const val name = "CreateOrderCommand"
}
}
class SuccessOrderEvent(val id: String) : Serializable {
companion object {
const val name = "OrderSuccessfulEvent"
}
}
interface StateManagementLib {
fun <T> send(
value: T,
success: Handler<AsyncResult<Int>>,
failure: Handler<AsyncResult<Exception>>
) {
val output = publish(value)
if (output == 1) {
success.handle(Future.succeededFuture())
} else {
failure.handle(Future.failedFuture("Failed"))
}
}
// non-blocking
fun <T> publish(value: T): Int // returns success/failure only
}
class WorkVerticle constructor(private val lib: StateManagementLib) : AbstractVerticle() {
override fun start(startPromise: Promise<Void>) {
workerHandler()
startPromise.complete()
}
private fun workerHandler() {
val consumer = vertx.eventBus().consumer<OrderCommand>(OrderCommand.name)
consumer.handler { message: Message<OrderCommand> ->
try {
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") { mapIt ->
if (mapIt.succeeded()) {
lib.send(message.body(), {
// The StateManagementLib successfully propagated the event so, we try and store in this map (id -> Message)
mapIt.result().put(message.body().order.id, message)
}, {
message.fail(400, it.result().message)
})
}
}
} catch (e: Exception) {
message.fail(
HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Failed to encode data."
)
}
}
// A consumer that will pick up an event that is received from the clusterTool output channel
vertx.eventBus().addInboundInterceptor { context: DeliveryContext<SuccessOrderEvent> ->
// Retrieve cluster map to get the previously stored message and try to respond with Message.reply
// This should go back to the Standard Verticle that sent the message
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") {
if (it.succeeded()) {
val id = context.message().body().id
val mapResult = it.result().get(id)
it.result().remove(id)
// Try and reply so the original eventloop thread can pickup and respond to calling client
mapResult.result().reply(id)
}
}
}
}
}
I'm quite new to the Microservice world and particularly vertX. I want my verticle to start anyway even there is no database connection available (e.g. database URL missing in configuration). I already managed to do this and my verticle is starting.
The issue now is that I want my verticle to notice when the database connection is available again and connect to it. How can I do this ?
I thought about creating another Verticle "DatabaseVerticle.java" which would send the current DB config on the event bus and my initial verticle would consume this message and check whether the config info is consistent (reply with success) or still missing some data (reply with fail and make the DatabaseVerticle check again).
This might work (and might not) but does not seem to be the optimal solution for me.
I'd be very glad if someone could suggest a better solution. Thank you !
For your use case, I'd recommend to use the vertx-config. In particular, have a look at the Listening to configuration changes section of the Vert.x Config documentation.
You could create a config retriever and set a handler for changes:
ConfigRetrieverOptions options = new ConfigRetrieverOptions()
.setScanPeriod(2000)
.addStore(myConfigStore);
ConfigRetriever retriever = ConfigRetriever.create(vertx, options);
retriever.getConfig(json -> {
// If DB config available, start the DB client
// Otherwise set a "dbStarted" variable to false
});
retriever.listen(change -> {
// If "dbStarted" is still set to false
// Check the config and start the DB client if possible
// Set "dbStarted" to true when done
});
The ideal way would be some other service telling your service about database connection. Either through event bus or HTTP, what you can do is when someone tries to access your database when connection is not made just try to make some DB call and handle the exception, return a boolean as false. Now when you get a message on event bus, consume it and save it in some config pojo. Now when someone tries to access your database, look for config and if available make a connection.
Your consumer:
public void start(){
EventBus eb = vertx.eventBus();
eb.consumer("database", message -> {
config.setConfig(message.body());
});
}
Your db client(Mongo for this eg):
public class MongoService{
private MongoClient client;
public boolean isAvailable = false;
MongoService(Vertx vertx){
if(config().getString("connection")){
client = MongoClient.createShared(vertx, config().getString("connection"));
isAvailable = true;
}
}
}
Not everything in Vertx should be solved by another verticle.
In this case, you can use .periodic()
http://vertx.io/docs/vertx-core/java/#_don_t_call_us_we_ll_call_you
I assume you have some function that checks the DB for the first time.
Let's call it checkDB()
class PeriodicVerticle extends AbstractVerticle {
private Long timerId;
#Override
public void start() {
System.out.println("Started");
// Should be called each time DB goes offline
final Long timerId = this.vertx.setPeriodic(1000, (l) -> {
final boolean result = checkDB();
// Set some variable telling verticle that DB is back online
if (result) {
cancelTimer();
}
});
setTimerId(timerId);
}
private void cancelTimer() {
System.out.println("Cancelling");
getVertx().cancelTimer(this.timerId);
}
private void setTimerId(final Long timerId) {
this.timerId = timerId;
}
}
Here I play a bit with timerId, since we cannot pass it to cancelTimer() right away. But otherwise, it's quite simple.
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
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
I want to make a service that notify the user in case there are some new messages sent to him. Thus I want to use some Comet framework that provide the server push ability. So I have looked into PokeIn.
Just wondering a thing. I have checked on the samples that they have on the website. None of them look into the database to retrieve new entries if there are some. But it is just a matter of modification to it I guess.
One of the sample implement this long polling by using a sleep on the server side. So if I use the same approach I can check the database, if there are any new entries, every 5 seconds. However this approach doesn't seem to be much different from when using polling on the client side with javascript.
This part is from a sample. As can be seen they put a sleep there for to update current time for everybody.
static void UpdateClients()
{
while (true)
{
//.. code to check database
if (CometWorker.ActiveClientCount > 0)
{
CometWorker.SendToAll(JSON.Method("UpdateTime", DateTime.Now));
}
Thread.Sleep(500);
}
}
So I wonder is this how I should implement the message notifier? It seems that the above approach is still going to push a huge load demand on the server side. The message notifier is intend to work same way as the one found Facebook.
You shouldn't implement this way, that sample is only implemented like that because the keep PokeIn related part is clear. You should implement SQL part as mentioned http://www.codeproject.com/Articles/12335/Using-SqlDependency-for-data-change-events
in order to track changes on database.
So, when you have something to send, call one of the PokeIn methods for the client side delivery. I don't know, how much your application is time critical because in addition to reverse ajax, PokeIn's internal websocket feature is very easy to activate and delivers messages to client quite fast.
You can do this with database as #Zuuum said, but I implemented it in a different way.
I'm using ASP.NET MVC with PokeIn and EF in a Windows Azure environment:
I have domain events similar to this approach: Strengthening your domain: Domain Events
When someone invokes an action, that's a Unit of Work
If that UOW succeeds then I raise a domain event (e.g. ChatMessageSent)
I have subscribers for these domain events so they can receive the event and forward the message to the PokeIn listeners
I use this pattern for all my real-time needs on my game site (making moves, actions etc in a game), I don't want to advertise it here, you can find it through me if you want.
I always use this pattern as a duplex communication solution so everybody gets their update via PokeIn, even the user who invoked the action so every client will behave the same. So when someone calls an action it won't return anything except the success signal.
The next examples are won't work because they are only snippets to demonstrate the flow
Here is an action snippet from my code:
[HttpPost]
[UnitOfWork]
[RestrictToAjax]
[ValidateAntiForgeryToken]
public JsonResult Post(SendMessageViewModel msg)
{
if (ModelState.IsValid)
{
var chatMessage = new ChatMessage
{
ContainerType = msg.ContainerType,
ContainerID = msg.ContainerID,
Message = _xssEncoder.Encode(msg.Message),
User = _profileService.CurrentUser
};
_chatRepository.AddMessage(chatMessage);
OnSuccessfulUoW = () => EventBroker.Current.Send(this, new ChatMessageSentPayload(chatMessage));
}
return Json(Constants.AjaxOk);
}
And the (simplified) EventBroker implementation:
public class UnityEventBroker : EventBroker
{
private readonly IUnityContainer _container;
public UnityEventBroker(IUnityContainer container)
{
_container = container;
}
public override void Send<TPayload>(object sender, TPayload payload)
{
var subscribers = _container.ResolveAll<IEventSubscriber<TPayload>>();
if (subscribers == null) return;
foreach (var subscriber in subscribers)
{
subscriber.Receive(sender, payload);
}
}
}
And the even more simplified subscriber:
public class ChatMessageSentSubscriber : IEventSubscriber<ChatMessageSentPayload>
{
public void Receive(object sender, ChatMessageSentPayload payload)
{
var message = payload.Message;
var content = SiteContent.Global;
var clients = Client.GetClients(c => c.ContentID == message.ContainerID && c.Content == content)
.Select(c => c.ClientID)
.ToArray();
var dto = ObjectMapper.Current.Map<ChatMessage, ChatMessageSentDto>(message);
var json = PokeIn.JSON.Method("pokein", dto);
CometWorker.SendToClients(clients, json);
}
}