MassTransit-多个消费者都能收到相同的消息吗?

时间:2019-07-25 20:36:36

标签: c# .net .net-core rabbitmq masstransit

我有一个通过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 
    }
}

5 个答案:

答案 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

上找到可行的解决方案
相关问题