我有一个通过MassTransit发送到RabbitMq的.NET 4.5.2服务发布消息。
.p.net Core 2.1服务的和多个实例消耗这些消息。
目前,竞争的.NET核心消费者服务实例窃取其他消息。
即第一个使用该消息的消息将其从队列中移出,其余的服务实例将不使用它。
我希望所有实例使用同一条消息。
我该如何实现?
发布者服务的配置如下:
builder.Register(context =>
{
MessageCorrelation.UseCorrelationId<MyWrapper>(x => x.CorrelationId);
return Bus.Factory.CreateUsingRabbitMq(configurator =>
{
configurator.Host(new Uri("rabbitmq://localhost:5671"), host =>
{
host.Username(***);
host.Password(***);
});
configurator.Message<MyWrapper>(x => { x.SetEntityName("my.exchange"); });
configurator.Publish<MyWrapper>(x =>
{
x.AutoDelete = true;
x.Durable = true;
x.ExchangeType = true;
});
});
})
.As<IBusControl>()
.As<IBus>()
.SingleInstance();
.NET Core消费者服务的配置如下:
serviceCollection.AddScoped<MyWrapperConsumer>();
serviceCollection.AddMassTransit(serviceConfigurator =>
{
serviceConfigurator.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://localhost:5671"), hostConfigurator =>
{
hostConfigurator.Username(***);
hostConfigurator.Password(***);
});
cfg.ReceiveEndpoint(host, "my.exchange", exchangeConfigurator =>
{
exchangeConfigurator.AutoDelete = true;
exchangeConfigurator.Durable = true;
exchangeConfigurator.ExchangeType = "topic";
exchangeConfigurator.Consumer<MyWrapperConsumer>(provider);
});
}));
});
serviceCollection.AddSingleton<IHostedService, BusService>();
然后MyWrapperConsumer看起来像这样:
public class MyWrapperConsumer :
IConsumer<MyWrapper>
{
.
.
public MyWrapperConsumer(...) => (..) = (..);
public async Task Consume(ConsumeContext<MyWrapper> context)
{
//Do Stuff
}
}
答案 0 :(得分:2)
听起来您想发布消息并让多个消费者服务实例接收它们。在这种情况下,每个服务实例都需要有自己的队列。这样,每条已发布的消息都将导致副本被传递到每个队列。然后,每个接收端点将从其自己的队列中读取该消息并使用它。
您所做的所有过多配置都违背了您想要的配置。要使其工作,请删除所有交换类型配置,并为每个服务实例配置一个唯一的队列名称(您可以从主机,计算机等生成它),然后在消息生成器上调用Publish。
您可以看到RabbitMQ拓扑的配置方式:http://masstransit-project.com/MassTransit/understand/default-topology.html
答案 1 :(得分:1)
默认情况下,RabbitMQ按顺序将每个消息发送给所有使用者。这种调度称为“循环调度”,用于负载均衡(您可以使服务的多个实例消耗同一条消息)。 正如Chris所指出的,要确保您的服务始终收到消息的副本,您需要提供唯一的队列名称。
答案 2 :(得分:0)
感谢克里斯·帕特森(Chris Patterson)的回答和阿列克谢·齐马列夫(Alexey Zimarev)的评论,我现在相信我已经在工作。
伙计们指出(根据我的理解,如果我错了,请纠正我),我应该自己放弃指定Exchange和Queue等,并不再对我的配置太细腻。
根据我的类型MyWrapper
,让MassTransit知道要创建和发布到哪个交换,以及创建和绑定到那个队列的队列。还有我的IConsumer
实现类型MyWrapperConsumer
。
然后为每个消费者服务提供自己的唯一ReceiveEndpoint
名称,我们将最终以交换将MyWrapper类型的消息散发到由指定的唯一名称创建的每个唯一队列。
所以,就我而言。.
PUBLISHER SERVICE配置相关的代码行从:
configurator.Message<MyWrapper>(x => { x.SetEntityName("my.exchange"); });
configurator.Publish<MyWrapper>(x =>
{
x.AutoDelete = true;
x.Durable = true;
x.ExchangeType = true;
});
对此
configurator.Message<MyWrapper>(x => { });
configurator.AutoDelete = true;
“每个消费者服务”实例配置的相关代码行从以下位置更改:
cfg.ReceiveEndpoint(host, "my.exchange", exchangeConfigurator =>
{
exchangeConfigurator.AutoDelete = true;
exchangeConfigurator.Durable = true;
exchangeConfigurator.ExchangeType = "topic";
exchangeConfigurator.Consumer<MyWrapperConsumer>(provider);
});
对此:
cfg.ReceiveEndpoint(host, Environment.MachineName, queueConfigurator =>
{
queueConfigurator.AutoDelete = true;
queueConfigurator.Consumer<MyWrapperConsumer>(provider);
});
注意,Environment.MachineName
为每个实例提供唯一的队列名称
答案 3 :(得分:0)
我想分享一个稍有不同的代码示例。 instanceId:
指定唯一标识端点的标识符 实例,该实例将附加到端点名称的末尾。
services.AddMassTransit(x => {
x.SetKebabCaseEndpointNameFormatter();
Guid instanceId = Guid.NewGuid();
x.AddConsumer<MyConsumer>()
.Endpoint(c => c.InstanceId = instanceId.ToString());
x.UsingRabbitMq((context, cfg) => {
...
cfg.ConfigureEndpoints(context);
});
});
答案 4 :(得分:0)
我们可以通过为每个消费者服务设置单独的队列来实现它,并且每个队列都绑定一个单一的交换器。当我们发布消息以进行交换时,它会将消息副本发送到每个队列并最终由每个消费者服务接收。您可以在 https://github.com/prasantj409/Masstransit-PublishMultipleConsumer.git
上找到可行的解决方案