Disable round-robin message consumption on MassTransit - c#

I have created a basic demo pub/sub application which works on localhost with MassTransit.
What I want to achieve is to publish a message and all the subscribers should receive the message.
At the moment, in my environment I start one publisher app and two subscriber apps. But when I publish a message the subscribers receive the message in turns.
My pub/sub code:
Publish:
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
config.Host(new Uri("rabbitmq://localhost/"), h => { });
config.ExchangeType = ExchangeType.Fanout;
});
var busHandle = bus.Start();
bus.Publish<SomethingHappened>(message);
Subscribers use this code:
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost/"), h => { });
config.ReceiveEndpoint(host, "MassTransitExample_Queue", e => e.Consumer<SomethingHappenedConsumer>());
});
var busHandle = bus.Start();
Console.ReadKey();
busHandle.Stop();

When reading the article below I found that the queue name must be unique
https://www.maldworth.com/2015/10/27/masstransit-send-vs-publish/
When building your bus and registering an endpoint like so:
sbc.ReceiveEndpoint(...), one has to be sure that the queueName
parameter is unique.
So my subscribers code looks like this now:
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost/"), h => { });
config.ReceiveEndpoint(host, "MTExQueue_" + Guid.NewGuid().ToString(), e => e.Consumer<SomethingHappenedConsumer>());
});
var busHandle = bus.Start();
Console.ReadKey();
busHandle.Stop();

Related

Azure Service Bus for messaging and saga persistence

I'm trying to persist the states of my saga into the Session of the AzureService Bus Messages. As long as the State of the Sage is just in memory it works fine. But if I activate RequiresSession and use a MessageSessionSagaRepository, my sage isn't doing anything anymore.
services.AddMassTransit(x =>
{
x.AddBus(provider => Bus.Factory.CreateUsingAzureServiceBus(cfg =>
{
cfg.Host(ConnectionString);
cfg.ReceiveEndpoint(EnrichQueue, e =>
{
// works
//e.StateMachineSaga(new EnrichmentStateMachine(), new InMemorySagaRepository<EnrichmentState>());
// doesn't work
e.RequiresSession = true;
e.StateMachineSaga(new EnrichmentStateMachine(), MessageSessionSagaRepository.Create<EnrichmentState>());
});
}));
});
Did I miss something that I should have configured?
Your configuration is a bit mixed, I'd suggest cleaning it up to bring it up to date and make it consistent:
services.AddMassTransit(x =>
{
x.AddSagaStateMachine<EnrichmentStateMachine, EnrichmentState>()
.MessageSessionRepository();
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(ConnectionString);
cfg.ReceiveEndpoint(EnrichQueue, e =>
{
e.RequiresSession = true;
e.ConfigureSaga<EnrichmentState>(context);
});
}));
});
Messages sent/published to that endpoint require a SessionId, which is covered in the documentation.

Kafka Producer with MassTransit - IBusInstance has not been registered

I'm trying to build a Kafka consumer using MassTransit
I have this piece of code
var services = new ServiceCollection();
services.AddMassTransit(x =>
{
x.AddRider(rider =>
{
rider.AddProducer<string, Request>("request", m => m.Message.RequestId);
rider.UsingKafka((context, k) =>
{
k.Host("localhost:9092");
});
});
});
var provider = services.BuildServiceProvider();
var producer = provider.GetRequiredService<ITopicProducer<Request>>();
await producer.Produce(new Request()
{
RequestId = "abc123",
RequestedAt = DateTime.UtcNow
});
This is the simplest example of a producer from here
but when I try to run it, I get this exception
Unhandled exception. System.InvalidOperationException: No service for type 'MassTransit.Registration.IBusInstance' has been registered.
Looking at the example from their website, I see that it could be related to the fact that I haven't registered a RabbitMQ
x.UsingRabbitMq((context, cfg) => cfg.ConfigureEndpoints(context));
But I don't have a RabbitMQ, I only use Kafka in this scenario.
Is it necessary to register a bus with some other message broker in order to produce to Kafka?
From the documentation:
Riders, introduced with MassTransit v7, provide a new way to deliver messages from any source to a bus. Riders are configured along with a bus, and board the bus when it is started.
To add riders, there must be a bus instance. If you don't need a bus with a durable transport such as RabbitMQ, you can use the in-memory transport.
var services = new ServiceCollection();
services.AddMassTransit(x =>
{
x.UsingInMemory((context,cfg) => cfg.ConfigureEndpoints(context));
x.AddRider(rider =>
{
rider.AddProducer<string, Request>("request", m => m.Message.RequestId);
rider.UsingKafka((context, k) =>
{
k.Host("localhost:9092");
});
});
});
The bus needs to be started and stopped, which will also start/stop any riders on the bus. You can do this via IBusControl:
var provider = services.BuildServiceProvider();
var busControl = provider.GetRequiredService<IBusControl>();
await busControl.StartAsync(cancellationToken);
Or by adding the MassTransit Hosted Service if you're using the ASP.NET Core Generic Host.
services.AddMassTransitHostedService(); // in MassTransit.AspNetCore

How can I add publish message configuration on a running IBusControl

I'm using MassTransit with RabbitMqTransport.
Assume I have run IBusControl using:
var control = Bus.Factory.CreateUsingRabbitMq(c =>
{
var host = confgurator.Host(config.BuildHostUri(), h =>
{
...
});
...
});
await control.StartAsync();
Later I connected new endpoint to this running instance, using:
host.ConnectReceiveEndpoint(Configuration.QueueName, this.ConfigureEndpoint);
Is there a way to configure Publish/Send for new Message types at this moment also? By "configure Publish/Send" I mean using methods like existing on IRabbitMqBusFactoryConfigurator:
confgurator.Send<MessageContract>(_ =>
{
_.UseRoutingKeyFormatter(__ => Configuration.QueueName);
});
confgurator.Message<MessageContract>(x => x.SetEntityName(nameof(MessageContract)));
confgurator.Publish<MessageContract>(_ =>
{
...
}
As per Chris Patterson comment, configuring Publish/Send for message type can only be done during configuration, prior to starting the bus.

MassTransit: Adding Headers to Publish Pipeline

I'm using MassTransit 3.2.4 and I'm trying to add some header information for to my published messages but the code to set the header never seems to run. I'm not sure why this doesn't work.
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost/"), h {});
config.ReceiveEndpoint(host, "TestPublisher", e =>
{
e.ConfigurePublish(x => x.UseSendExecute(context =>
context.Headers.Set("HeaderKey", "HeaderValue")
));
});
});
On the consumer end I'm trying to read the header
public Task Consume(ConsumeContext<IActionHappened> context)
{
var headerValue = context.Headers.Get("HeaderKey", "Default Value");
}
Do I need to add an interceptor or something else in order to set header information?
Figured it out after much guessing. Just had the ConfigurePublish in the wrong place
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost/"), h => {});
config.ConfigurePublish(x => x.UseSendExecute(context =>
{
context.Headers.Set("HeaderKey", "HeaderValue");
}));
}

Integration testing microservices using MassTransit

I am trying to create black box integration tests around some services that I have created using MassTransit. The gist of the service is that it receives a message on a MassTransit channel, processes the message and sends the message on a different MassTransit channel (note that this is not a Request-Reply semantic but more of a component in a pipeline):
IBusControl bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://localhost"),
h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ReceiveEndpoint(host, "Queue1", ep =>
{
ep.Handler<ItemStarted>(context =>
{
ItemFinished item = FinishMessage(context.Message);
context.Publish(item);
});
});
});
bus.Start();
In order to test this I think what I need is a synchronous way to receive messages from the bus. Is there any way to do this:
IBusControl bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
var host = cfg.Host(new Uri("rabbitmq://localhost"),
h =>
{
h.Username("guest");
h.Password("guest");
}));
bus.Start();
bus.Publish(new ItemStarted());
// This is what does not seem to exist
ItemFinished finished = bus.Receive<ItemFinished>(timeout : Timespan.FromSeconds(5));
// Assertions about finished
Is there a way to receive messages directly from MassTransit without wiring up a consumer class or lambda?
There is a MultiTestConsumer that can be used to receive and track messages that are produced during testing. You can see the class here:
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit/Testing/MultiTestConsumer.cs
And you can see how it is used here:
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit.Tests/MultiTestConsumer_Specs.cs
It can also be used as a regular subscribed consumer, which sets up bindings in RabbitMQ to receive published messages.
I would recommend checking out the TestFramework, as there are a bunch of asynchronous testing helpers in there.

Categories