How can I add publish message configuration on a running IBusControl - c#

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.

Related

Consume method is not being called using MassTransit with Azure Service BusTopics in a .Net microservice application

I know that this is being asked a lot, but I already visited a lot of question and I'm in the dark at the moment.
I'm using 2 different apps, one is the producer and the other one is the consumer. The applications are using topic-subscription type.
Producer:
services.AddMassTransit(serviceCollectionConfigurator =>
{
serviceCollectionConfigurator.AddBus(registrationContext => Bus.Factory.CreateUsingAzureServiceBus(configurator =>
{
configurator.Host("connectionString");
configurator.Message<CreateEvent>(configTopology =>
{
configTopology.SetEntityName("topic1");
});
}
));
});
Consumer:
services.AddMassTransit(x =>
{
x.AddConsumer<CreateConsumer>(); //Consumer class
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host("connectionString");
cfg.Message<CreateEvent>(configTopology =>
{
configTopology.SetEntityName("topic1");
});
cfg.SubscriptionEndpoint<CreateEvent>("subscription1", e =>
{
e.PrefetchCount = 100;
e.MaxConcurrentCalls = 100;
e.LockDuration = TimeSpan.FromMinutes(5);
e.MaxAutoRenewDuration = TimeSpan.FromMinutes(30);
e.UseMessageRetry(r => r.Intervals(100, 200, 500, 800, 1000));
e.UseInMemoryOutbox();
e.ConfigureConsumer<CreateConsumer>(context);
});
});
});
}
public class CreateConsumer: IConsumer<CreateEvent>
{
public Task Consume(ConsumeContext<CreateEvent> context)
{
//breakpoint here
}
}
One of the errors I spotted was the namespace of CreateEvent needs to be the same. When the producer publish the message I can't see the message being sent to azure, neither being received in the consumer.
I assume the problem can be located in both configurations but I have no clue. Any thoughts?
Edit: Forgot to mention I'm using masstransit v8, it seems by default they inject the hosted service to run the bus automatically.

Masstransit not creating exchange

Good day
I'm solving the problem that exchange is not being automatically created
I have registered Masstransit in net6 net core application using such uri opions (have tried both):
rabbitmq://myurl
rabbitmq://myurl:5672
Registration looks like this:
services.AddMassTransit(mt =>
{
mt.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri(
RabbitMqOptions.RabbitMqUri),
RabbitMqOptions.VHost,
credentials =>
{
credentials.Username(RabbitMqOptions.UserName);
credentials.Password(RabbitMqOptions.Password);
});
cfg.AutoStart = true;
cfg.Publish<IServerNotificationMessage>(e => e.ExchangeType = RabbitMQ.Client.ExchangeType.Direct);
});
});
services.AddMassTransitHostedService();
Debugging publishing code shows that actual port used is 0 and bus control is null and not started
see the print screen
How can I make the bus start? (as I understand cfg.Host returns void, rather than buscontrol, so that it cannot be explicitly started, have specified autostart option, though its still down)
Thank you in advance
A URI is not required to configure MassTransit, you might just simplify your configuration as shown below.
services.AddMassTransit(mt =>
{
mt.UsingRabbitMq((context, cfg) =>
{
cfg.Host(RabbitMqOptions.Host,
RabbitMqOptions.Port,
RabbitMqOptions.VHost,
h =>
{
h.Username(RabbitMqOptions.UserName);
h.Password(RabbitMqOptions.Password);
});
cfg.AutoStart = true;
cfg.Publish<IServerNotificationMessage>(e => e.ExchangeType = RabbitMQ.Client.ExchangeType.Direct);
});
});
services.AddMassTransitHostedService();
The logs should show the bus starting, if they don't, then the hosted service is not being started. Is this an ASP.NET project, or a project using the .NET Generic Host?

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

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