Masstransit EndpointConvention Azure Service Bus
Asked Answered
H

1

15

I'm wondering if I'm doing something wrong, I expected MassTransit would automatically register ReceiveEndpoints in the EndpointConvention.

Sample code:

services.AddMassTransit(x =>
{
    x.AddServiceBusMessageScheduler();
    x.AddConsumersFromNamespaceContaining<MyNamespace.MyRequestConsumer>();
    x.UsingAzureServiceBus((context, cfg) =>
    {
        // Load the connection string from the configuration.
        cfg.Host(context.GetRequiredService<IConfiguration>().GetValue<string>("ServiceBus:ConnectionString"));
        cfg.UseServiceBusMessageScheduler();

        // Without this line I'm getting an error complaining about no endpoint convention for x could be found.
        EndpointConvention.Map<MyRequest>(new Uri("queue:queue-name"));

        cfg.ReceiveEndpoint("queue-name", e =>
        {
            e.MaxConcurrentCalls = 1;
            e.ConfigureConsumer<MyRequestConsumer>(context);
        });

        cfg.ConfigureEndpoints(context);
    });
});

I thought this line EndpointConvention.Map<MyRequest>(new Uri("queue:queue-name")); wouldn't be necessary to allow sending to the bus without specifing the queue name, or am I missing something?

await bus.Send<MyRequest>(new { ...});

Heifetz answered 3/7, 2020 at 10:45 Comment(0)
L
44

The EndpointConvention is a convenience method that allows the use of Send without specifying the endpoint address. There is nothing in MassTransit that will automatically configured this because, frankly, I don't use it. And I don't think anyone else should either. That stated, people do use it for whatever reason.

First, think about the ramifications - if every message type was registered as an endpoint convention, what about messages that are published and consumed on multiple endpoints? That wouldn't work.

So, if you want to route messages by message type, MassTransit has a feature for that. It's called Publish and it works great.

But wait, it's a command, and commands should be Sent.

That is true, however, if you are in control of the application and you know that there is only one consumer in your code base that consumes the KickTheTiresAndLightTheFires message contract, publish is as good as send and you don't need to know the address!

No, seriously dude, I want to use Send!

Okay, fine, here are the details. When using ConfigureEndpoints(), MassTransit uses the IEndpointNameFormatter to generate the receive endpoint queue names based upon the types registered via AddConsumer, AddSagaStateMachine, etc. and that same interface can be used to register your own endpoint conventions if you want to use Send without specifying a destination address.

You are, of course, coupling the knowledge of your consumer and message types, but that's your call. You're already dealing with magic (by using Send without an explicit destination) so why not right?

string queueName = formatter.Consumer<T>()

Use that string for the message types in that consumer as a $"queue:{queueName}" address and register it on the EndpointConvention.

Or, you know, just use Publish.

Leix answered 3/7, 2020 at 11:47 Comment(14)
So to understand correctly, even though it is a command, if we where to use Publish instead of Send the consumers can stay the same and will automatically handle the messages? But we don't have to specify the endpoint conventions on the publisher side (separate website)Heifetz
Correct, if you have a single consumer for a message type, and publish, it's a 1:1 conversation without having to know the address. The message will be routed through a topic to the receive endpoint, where it will be consumed.Leix
@ChrisPatterson Sorry for digging this up, but I get the feeling that some additional configuration is required with Publish. If I add the consumer, but don't register a receive endpoint, the message never gets consumed...Sudorific
As long as your message type matches, including namespace, publish works on every transport. If you have questions, open a new one here with code or ask on Discord.Leix
I think the semantics between Send and Publish are not the same if there is no subscriber: If I publish without subscribers, the message is discarded. Send would persist the message in a destination queue, even if there currently is no subscriber.Cognomen
I'm a bit confused, because your official documentation says "Do not use Publish for commands" >> masstransit-project.com/usage/messages.html#message-names <<Eventide
Well, the docs are wrong in this case.Leix
@ChrisPatterson why not using bus.GetPublishSendEndpoint<IMessage>() to get send endpoint without manual uri? do I think true?Fosterfosterage
What do you think Publish does? That gets you the endpoint by message type, which is exactly the same thing as publish.Leix
As stated above, careful when making this decision. 1 - If the consumer bus is not started before the producer, the "command" published will be lost. 2 - If the "command" is published, you might have the same being processed multiple time if you have multiple consumers for the same message. 3 - You will get one additional exchange/topic for each "command" you publish. Please correct me if wrong.Upside
@ChrisPatterson are there any cost and performance implications of using Publish instead of Send? I know it depends on the transport used, but lets say we are using Azure ServiceBus. Thanks.Charge
For anyone using RabbitMQ, I found this article helpful regarding Publish semantics. Basically, registering consumers without specifying a queue name will use the same queue and thus load balance between multiple hosted consumers. The RabbitMQ queue is by default durable so disconnected consumers will happily resume when they reconnect. medium.com/@kova98/pub-sub-in-net-with-masstransit-44cb27024497Cryoscope
Is my understanding correct in that Mass Transit encourages to publish everything (commands and events)?Elegancy
@DrSchizo that's correct.Leix

© 2022 - 2024 — McMap. All rights reserved.