Bus Configuration with Autofac: Issue with RabbitMQ vs Loopback? - rabbitmq

For some reason I can not post to the masstransit google group, even though I joined, I am told that I do not have permission to post to this group. So I am going to post here...
Now for my problem:
I am using MassTransit v2.7.2, with AutoFac v2.6.3. I am trying to configure Autofac to scan an assembly and register my consumers; all types that implement the IConsumer interface. This seems to work.
I am using the MassTransit.AutofacIntegration assembly and the LoadFrom(...) extension method to register the consumers from the container with MassTransit when I configure the bus. Here is the code:
var builder = new ContainerBuilder();
builder
.RegisterAssemblyTypes(typeof (CreateElectionCommandHandler).Assembly)
.Where(type => type.Implements<IConsumer>())
.AsSelf();
var container = builder.Build();
var localBus = ServiceBusFactory.New(configurator =>
{
//configurator.ReceiveFrom("loopback://localhost/testqueue");
configurator.ReceiveFrom("rabbitmq://localhost/commandqueue");
configurator.UseRabbitMq();
configurator.Subscribe(sbc => sbc.LoadFrom(container));
});
Assert.IsTrue(container.IsRegistered<CreateElectionCommandHandler>());
Assert.IsTrue(container.IsRegistered<TerminateElectionCommandHandler>());
Assert.AreEqual(1, localBus.HasSubscription<CreateElection>().Count());
Assert.AreEqual(1, localBus.HasSubscription<TerminateElection>().Count());
If I run the above code using the loopback
configurator.ReceiveFrom("loopback://localhost/testqueue");
configuration (comment out the rabbitmq conifig), the test will pass.
If I comment out the "loopback" config and comment in the
configurator.ReceiveFrom("rabbitmq://localhost/commandqueue");
configurator.UseRabbitMq();
config, the test will fail. (Note: The rabbitmq queue is already up and running - I have been using it as part of my POC). Specifically, it will fail on the assertion:
Assert.AreEqual(1, localBus.HasSubscription<CreateElection>().Count());
Assert.AreEqual(1, localBus.HasSubscription<TerminateElection>().Count());
Can anybody help me understand what is going on here? I am new to MT so fully anticipating that I am missing something, or not configuring something correctly.
Am I correct to assume that if there are no message subscriptions registered, then the bus will not be able to deliver to any of my consumers (even though the consumers are registered)?
Any help much appreciated!

With RabbitMQ, subscriptions are not added to the outbound bus until a message is published. This is due to how the classes are inspected and outbound endpoints to the appropriate exchanges are created and added to the pipeline.
So yes, this test will fail with RabbitMQ, but it will in fact work properly when the message is published.
The HasSubscription() calls are really meant for verifying that consumers and such are properly configured using the loopback transport, and really just for vetting out things that are not really integration issues but just making sure the internal MassTransit code is working.
So, if you were to add a Publish() call of one of those types, and then call the HasSubscription() extension method, it would pass.

I would check to see if MassTransit creates an exchange the message types in question. Messages are sent to the exchange and all consumer queues are bound to the exchange. You can look at the Rabbit config to see if that's happened or not as well. And with no consumers registered, no messages will be delivered. Chris has been working on adding options to error is there's consumers so you can handle it in your code.
I would join the mailing list https://groups.google.com/forum/?fromgroups=#!forum/masstransit-discuss to get help. There's a lot more people that can ask the right questions to get you where you need to be.

Related

RabbitMQ as both Producer and Consumer in a single application

I am currently learning RabbitMQ and AMQP in general. I started working with some tutorials I found online and all of them show more or less the same example - a Spring Boot web app that, upon a REST call, produces a message and puts in onto a RabbitMQ queue and then, another class from the same app, which is configured as the Consumer of that message consumes it and processes the handler method.
I can't wrap my head around why this is beneficial in any way. The upside I understand is that the handler is executed in a separate thread, while the controller method can return right after sending the message to the queue. However, why would this be in any way better than just using Spring's #Async annotation on that handler method and calling it explicitly? In that case I suppose we would achieve the same thing, while not having to host and manage a seperate instance of a message broker like RabbitMQ.
Can someone please explain? Thanks.
Very simply:
with RabbitMq you can have persistent messages and a much safer and consistent exception management. In case the machine crashes, already pushed messages are not lost.
A message can be pushed to an exchange and consumed by more parallel consumers, that helps scaling the application in case the consumer code is too slow.
and a lot of other reasons...

Creating queues dynamical on MassTransit

I have a particular scenario with RabbitMQ that needs to have dynamically created queues and binds to exchanges, that are also dynamically created (not by me). This creation and binding is triggered by a new SignalR subscription.
This issue: https://github.com/MassTransit/MassTransit/issues/398 is about it, but I still don't know the answer.
Seems that mass transit is not very flexible on creating things on the move.
How can I achieve this? What if I stop the bus and recreate all the queues and bindings plus the new one, and start the bus again?
Thanks in advance.
Receive endpoints can be connected via the bus, as shown in the documentation.
For example:
var handle = bus.ConnectReceiveEndpoint("queue-name", x =>
{
x.Consumer<SomeConsumer>();
})
// the code below waits for the receive endpoint to be ready
// and throws an exception if a fault occurs
var ready = await handle.Ready;

Testing with in-memory NServiceBus

I'm attempting to create a high level test in my solution, and I want to 'catch' messages sent to the bus.
Here's what I do:
nUnit [SetUp] spins up the WebAPI project in IISExpress
SetUp also creates the bus
Send a HTTP request to the API
Verify whatever I want to verify
The WebAPI part of the whole test works fine. The creation of the bus and kicking it off seems great too. It even finds my fake message handler. The problem is the handler never receives the command from the queue, they just stay in the RabbitMQ queue forever.
Here's how the bus is being configured:
var bus = Configure.With()
.DefineEndpointName("Local")
.Log4Net()
.UseTransport<global::NServiceBus.RabbitMQ>()
.UseInMemoryTimeoutPersister()
.RijndaelEncryptionService()
.UnicastBus();
.CreateBus();
In the log from NServiceBus starting up, I see that my fake handler is being associated with the command:
2014-09-24 15:29:59,007 [Runner thread] DEBUG NServiceBus.Unicast.MessageHandlerRegistry
[(null)] <(null)> - Associated 'Bloo.MyCommand' message with 'Blah.FakeMyCommandHandler' handler
So seeing as the message lands in the correct RabbitMQ queue, I'm assuming everything up until the handler point is working fine.
I've tried putting waits in my [TearDown] so that the bus lives a little longer - hoping to give the handler time to receive the message. I've also tried spinning off the in-memory bus for the consumer part of the interactoin into a new thread with no luck.
Has anyone else tried this?
This is only the first step, what I would love to do is create a fake bus that records messages being sent to it. The need for RabbitMQ is just to get myself going (the bounds of my solution are WebAPI on the front and the bus at the back).
Cheers
You forgot to call .Start() on the bus, that's why it didn't listen for messages.
See here for more info: http://docs.particular.net/nservicebus/hosting-nservicebus-in-your-own-process-v4.x
Also, consider using NServiceBus.Testing for unit testing your handlers and sagas:
https://www.nuget.org/packages/NServiceBus.Testing
I'm guessing your messages are just sitting in your queue forever because your end point is listening on "Local.MachineName" queue instead of "Local"
If you set the ScaleOut to be SingleBrokerQueue this should sort the issue.
Configure.ScaleOut(s => s.UseSingleBrokerQueue());
var bus = Configure.With()
.DefineEndpointName("Local")
...
If you are attempting to do full integration tests, using actual queues, then this answer won't help you.
If you are doing more focused tests, i.e. testing individual components that rely on the bus, I would recommend that you use a mocking framework (I like Moq) and mock out IBus. You can then verify that messages you expected to be sent to the bus were indeed sent.

SendOnly mode with no local queue

In NServiceBus you can create a SendOnly bus which means that all it will do is send messages to another service. It seems to me that this should not require a local queue for the sending service at all, however i have been unable to configure this.
var bus = Configure.With()
.DefiningCommandsAs(t => typeof(MyNamespace.Messaging.Markers.ICommand).IsAssignableFrom(t))
.DefaultBuilder()
.XmlSerializer()
.MsmqTransport()
.UnicastBus()
.SendOnly();
gives the following exception:
No endpoint name could be generated, please specify your own
convention using Configure.DefineEndpointName(...)
While i can solve this issue by adding a .DefineEndpointName it seems like poor form to be creating a queue for this service as it will never actually be used.
Is there a way of avoiding needing to create a local queue for a send only bus, and if not why is this local queue required in this mode?
All endpoints needs a name regardless if they are send only or not. This has nothing to do with the creation of queues(yes the queue name == endpoint name). Add a define endpoint name and you'll see that no queue will be created. Also note that the sent messages will not contain a reply to address since that wouldn't make sense.
I believe that this is provide where the message came from. The sending queue is an intrinsic part of MSMQ. It allows for ack messages, supports the NServiceBus bus.Reply feature.
In this case is it seems unnecessary, but if we consider that SendOnly is trying to guide the developer towards using a particular messaging style. Considering that MSMQ which supports many styles of messaging, this isn't such an issue in my view.

Is there an nServiceBus sample for Pub/Sub with WCF?

It's a really common pattern and I'm finding it a nightmare to implement!
--
The WcfIntegration sample is almost what I'm looking for in that it receives messages via a WCF endpoint. However, it receives messages back on itself. I want a separate subscriber.
So what I'm trying to do is merge the WcfIntegration and pub/sub samples.
The real need is for a website to call a class library, which then calls the WCF endpoint of the publisher.
A subscriber then picks receives a message that the publisher publishes, and does whatever with it.
--
Rob
You shouldn't need the interface IEventMessageService; NSB handles that for you.
In my local example, I mapped the messages to myself. For you this would be:
<UnicastBusConfig><MessageEndpointMappings>
<add Messages="MyMessages" Endpoint="MyPublisherInputQueue"/>
</MessageEndpointMappings></UnicastBusConfig>
You also need to move your Publish to the actual handler. Right now, it is only happening once (at startup):
public void Handle(EventMessage message)
{
bus.Publish(message);
bus.Return((int)ErrorCodes.None);
}
Make sure your infrastructure is primed, i.e. the queue is transactional, and MSDTC is running.
I'm willing to bet that since there is no mapping, once the service is called, it doesn't know where to put the messages (it calls Bus.Send() internally). I didn't look at the subscribers since that didn't seem to be the issue.
I've finally got it to work here:-
http://code.google.com/p/nservicebus-wcf-pubsub/downloads/list
Anyone is welcome to improve the code.