I'm trying to implement scheduling mechanism by the masstransit/rabbitmq.
I've added the configuration as stated in the docs:
Uri schedulerEndpoint = new (Constants.MassTransit.SchedulerEndpoint);
services.AddMassTransit(mtConfiguration =>
{
mtConfiguration.AddMessageScheduler(schedulerEndpoint);
mtConfiguration.AddSagaStateMachine<ArcStateMachine, ArcProcess>(typeof(ArcSagaDefinition))
.Endpoint(e => e.Name = massTransitConfiguration.SagaQueueName)
.MongoDbRepository(mongoDbConfiguration.ConnectionString, r =>
{
r.DatabaseName = mongoDbConfiguration.DbName;
r.CollectionName = mongoDbConfiguration.CollectionName;
});
mtConfiguration.UsingRabbitMq((context, cfg) =>
{
cfg.UseMessageScheduler(schedulerEndpoint);
cfg.Host(new Uri(rabbitMqConfiguration.Host), hst =>
{
hst.Username(rabbitMqConfiguration.Username);
hst.Password(rabbitMqConfiguration.Password);
});
cfg.ConfigureEndpoints(context);
});
});
Then I'm sending a scheduled message using the Bus:
DateTime messageScheduleTime = DateTime.UtcNow + TimeSpan.FromMinutes(1);
await _MessageScheduler.SchedulePublish<ScheduledMessage>(messageScheduleTime, new
{
ActivationId = context.Data.ActivationId
});
_MessageCheduler is the IMessageScheduler instance.
I do see the Scheduler queue receive the scheduled message and I see the correct scheduledTime property in it but the message does not reach the state machine whenever its schedule should fire. Seems like I'm missing something in the configuration or some MassTransit service that is not started.
Please, assist.
If you actually read the documentation you would see that UseDelayedMessageScheduler is the proper configuration to use RabbitMQ for scheduling. And AddDelayedMessageScheduler for the container-based IMessageScheduler registration.
Related
In StartUp of the project, I make the following settings for MassTransit.ActiveMQ. But when I run, it creates two queues for me, one is event-listener and the other is called Generation.
When I publish information, the information goes into the queues generated by the system.
But I want the information to be published inside queue event-listener that I set.
Please guide me
services.AddMassTransit(x =>
{
x.AddConsumer<EventConsumer>();
x.UsingActiveMq((context, cfg) =>
{
cfg.Host("localhost", h =>
{
h.Username("admin");
h.Password("admin");
});
cfg.ReceiveEndpoint("event-listener", e =>
{
e.ConfigureConsumer<EventConsumer>(context);
});
});
});
MassTransit will only create queues for configured consumers, or explicitly configured receive endpoints. In the code above, the only queue created would be called event-listener. For each message type consumed by the consumer, a topic is created and a virtual topic consumer is created so that the receive endpoint can consume messages of each type.
When messages are published, a topic is created for each published message type.
If you want to send a message directly to a queue, instead of publishing:
var provider = serviceProvider.GetRequiredService<ISendEndpointProvider>();
var endpoint = await provider.GetSendEndpoint(new Uri("queue:event-listener"));
await endpoint.Send(...);
I would like all the messaging for a particular saga to all take place on the same topic.
I set my saga up like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddMassTransit(x =>
{
var machine = new MyStateMachine();
var repository = new InMemorySagaRepository<MyState>();
x.UsingAzureServiceBus((ctx,cfg) => {
cfg.Host(config.ServiceBusConnectionString);
cfg.SubscriptionEndpoint("mySub", "myTopic", e =>
{
e.StateMachineSaga(machine, repository);
});
});
});
}
In the state machine I do this:
Initially(
When(FirstEvent)
.PublishAsync(context => context.Init<SecondMessage>(new { TestParam = "test"}))
.TransitionTo(FirstState));
I would like the SecondMessage to be published on the topic 'myTopic' but instead MassTransit creates a new topic for this message.
MassTransit creates a topic per message type, and publishes messages to their corresponding topics. Assigning the same topic name to multiple message types is not recommended.
If you want to use a subscription endpoint for a saga, you would need to configure the saga on the corresponding topic for each event. By default, MassTransit will forward the message topics for each saga event to the receive endpoint queue.
An example topology is shown below:
I am currently working with micro service architecture and .net core.
Rabbit MQ + MassTransit are being used to send and receive data between the micro services.
I have a host application in IIS and from 2 separate browsers I send the same request to micro service and that microservice calls other service using RabbitMQ.
I expect to get 2 separate requests hitting the consumer but instead get an internal server error.
Startup:
services.AddScoped<OrderCompletedEventConsumer>();
services.AddMassTransit(x =>
{
x.AddConsumer<Controllers.OrderController>();
});
services.AddSingleton(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("http://192.168.101.111:5672"),
"/", h =>
{
h.Username("Test");
h.Password("test");
});
cfg.ReceiveEndpoint(host, "TestQUE", e =>
{
e.Consumer<Controllers.OrderController>(provider);
});
}));
//Register Publish Endpoint of RabbitMQ bus service
services.AddSingleton<IPublishEndpoint>(provider => provider.GetRequiredService<IBusControl>());
//Register Send Endpoint of RabbitMQ bus service
services.AddSingleton<ISendEndpointProvider>(provider => provider.GetRequiredService<IBusControl>());
//Register Bus control for RabbitMQ
services.AddSingleton<IBus>(provider => provider.GetRequiredService<IBusControl>());
//Regster Bus Service hosting
services.AddSingleton<IHostedService, BusService>();
Request From One microservice:-
IRequestClient<IAddRequest<IOrder>, IAddResponse<IOrder>> orderClient =
new MessageRequestClient<IAddRequest<IOrder>, IAddResponse<IOrder>>(_bus,
EndpointAddress("orderQue"), TimeSpan.FromSeconds(Convert.ToDouble("150")));
var addResponse = orderClient.Request(new
{
entity = order
});
await Task.WhenAll(addResponse);
Consumer
public async Task Consume(ConsumeContext<IGetRequest<IOrder>> context)
{
// Operation and return result
await context.RespondAsync<IGetResposne<IOffice>>(new
{
// send result.
});
}
In the consumer the 2 separate requests from different browser arrive but both are unsuccessful. However if I do one request at a time then it will work, why is this?
Please give any idea, suggestion or hint.
Thank you,
I want to webapplication and in the backend I open a new thread and start a listener.What I want ,open connection one time and rabbitmq listener start and when new message is coming ,it is processed in background.
What is the best approach for this?
Why do you need to listen events in web application?
Instead of it, write a windows service using topshelf and masstransit as a rabbitmq client.
You can connect to rabbitmq and register listener consumers in Program.cs as below:
IBusControl busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
IRabbitMqHost host = cfg.Host(new Uri(RabbitMQConstants.RabbitMQUri),
hst =>
{
hst.Username(RabbitMQConstants.RabbitMQUserName);
hst.Password(RabbitMQConstants.RabbitMQPassword);
});
cfg.ReceiveEndpoint(host,
RabbitMQConstants.YourQueueName,
endPointConfigurator => { endPointConfigurator.Consumer<SomeConsumer>(); });
});
And start bus configurator :
busControl.Start();
Example consumer:
public class SomeConsumer :
IConsumer<YourMessageClass>
{
public async Task Consume(ConsumeContext<YourMessageClass> context)
{
await Console.Out.WriteLineAsync($"Message consumed: {context.Message.YourValue}");
}
}
For more information and example take a look at offical masstransit documentation : http://masstransit-project.com/MassTransit/usage/message-consumers.html
Greetings to MT experts.
In my app I have default retry policy that sends message each 3 minutes for 30 minutes in total. And if there are many failed messages that are affected with this policy (more than 16) other messages are not handled (even successfull ones). This is a huge problem, because if there are 16 broken messages, then whole queue is blocked for 30 minutes.
I'm sure there is a solution for this, but I haven't found any.
The solution is redelivery aka second-level retries.
Here is the documentation.
There are two ways to use it:
Explicit redelivery from a consumer, called on exception:
public async Task Consume(ConsumeContext<ScheduleNotification> context)
{
try
{
// try to update the database
}
catch (CustomerNotFoundException exception)
{
// schedule redelivery in one minute
context.Redeliver(TimeSpan.FromMinutes(1));
}
}
Or using configuration and policy (part of the endpoint configuration delegate):
ep.Consumer<CSomeConsumer>(c => c.Message<SomeMessage>(
x => x.UseDelayedRedelivery(
p =>
{
p.Handle<SqlException>(e => e.Message.Contains("Timeout"));
p.Exponential(40, TimeSpan.FromSeconds(10), TimeSpan.FromHours(1),
TimeSpan.FromSeconds(4));
})));
Remember that you must have scheduling configured to use this feature. It can be done using Quartz or RabbitMQ/AzureSB integrated scheduling features.
I think you're looking for the circuit breaker pattern, this can be applied to the masstransit with the following:
cfg.ReceiveEndpoint(host, "customer_update_queue", e =>
{
e.UseCircuitBreaker(cb =>
{
cb.TrackingPeriod = TimeSpan.FromMinutes(1);
cb.TripThreshold = 15;
cb.ActiveThreshold = 10;
cb.ResetInterval = TimeSpan.FromMinutes(5);
});
// other configuration
});
More information can be found in the docs:
http://masstransit-project.com/MassTransit/advanced/middleware/circuit-breaker.html