Topics
Wolverine supports publishing to Rabbit MQ topic exchanges with this usage:
using var host = await Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
opts.UseRabbitMq();
opts.Publish(x =>
{
x.MessagesFromNamespace("SomeNamespace");
x.ToRabbitTopics("topics-exchange", ex =>
{
// optionally configure the exchange
});
});
opts.ListenToRabbitQueue("");
}).StartAsync();While we're specifying the exchange name ("topics-exchange"), we did nothing to specify the topic name. With this set up, when you publish a message in this application like so:
var publisher = host.MessageBus();
await publisher.SendAsync(new Message1());You will be sending that message to the "topics-exchange" with a topic name derived from the message type. By default that topic name will be Wolverine's message type alias. Unless explicitly overridden, that alias is the full type name of the message type.
That topic name derivation can be overridden explicitly by placing the [Topic] attribute on a message type like so:
[Topic("one")]
public class TopicMessage1;[Topic("color.blue")]
public class FirstMessage
{
public Guid Id { get; set; } = Guid.NewGuid();
}Of course, you can always explicitly send a message to a specific topic with this syntax:
await publisher.BroadcastToTopicAsync("color.*", new Message1());Note two things about the code above:
- The
IMessageBus.BroadcastToTopicAsync()method will fail if there is not the declared topic exchange endpoint that we configured above - You can use Rabbit MQ topic matching patterns in addition to using the exact topic
Lastly, to set up listening to specific topic names or topic patterns, you just need to declare bindings between a topic name or pattern, the topics exchange, and the queues you're listening to in your application. Lot of words, here's some code from the Wolverine test suite:
theSender = await Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
opts.UseRabbitMq("host=localhost;port=5672").AutoProvision();
opts.PublishAllMessages().ToRabbitTopics("wolverine.topics", exchange =>
{
exchange.BindTopic("color.green").ToQueue("green");
exchange.BindTopic("color.blue").ToQueue("blue");
exchange.BindTopic("color.*").ToQueue("all");
// Need this to be able to go to ONLY the green receiver for a test
exchange.BindTopic("special").ToQueue("green");
});
opts.Discovery.DisableConventionalDiscovery()
.IncludeType<TriggerTopicMessageHandler>();
opts.ServiceName = "TheSender";
opts.PublishMessagesToRabbitMqExchange<RoutedMessage>("wolverine.topics", m => m.TopicName);
}).StartAsync();Publishing by Topic Rule
As of Wolverine 1.16, you can specify publishing rules for messages by supplying the logic to determine the topic name from the message itself. Let's say that we have an interface that several of our message types implement like so:
public interface ITenantMessage
{
string TenantId { get; }
}Let's say that any message that implements that interface, we want published to the topic for that messages TenantId. We can implement that rule like so:
var builder = Host.CreateApplicationBuilder();
builder.UseWolverine(opts =>
{
opts.UseRabbitMq();
// Publish any message that implements ITenantMessage to
// a Rabbit MQ "Topic" exchange named "tenant.messages"
opts.PublishMessagesToRabbitMqExchange<ITenantMessage>("tenant.messages",
m => $"{m.GetType().Name.ToLower()}/{m.TenantId}")
// Specify or configure sending through Wolverine for all
// messages through this Exchange
.BufferedInMemory();
});
using var host = builder.Build();
await host.StartAsync();Publishing to a Topic Exchange and to a Local Queue at the Same Time 6.49
Let's say you want every message of a certain type to go out to a Rabbit MQ topic exchange with a topic computed from the message, but you also want the same message handled right here in this application through a local queue. You might reach for both PublishMessagesToRabbitMqExchange<T>() and an explicit PublishMessage<T>().ToLocalQueue() rule, but with the original overload the exchange wins and the local queue rule is never even consulted. To make the exchange routing additive so that Wolverine's other routing rules still apply, pass additive: true:
opts.UseRabbitMq().AutoProvision();
// Every AdditiveRabbitCommand goes to the topic exchange with a topic computed from the message...
opts.PublishMessagesToRabbitMqExchange<AdditiveRabbitCommand>(exchangeName,
m => $"commands.{m.Region}", additive: true);
// ...and because that routing is additive, to the local "commands" queue at the same time
opts.PublishMessage<AdditiveRabbitCommand>().ToLocalQueue("commands");With that configuration, a single PublishAsync() sends the message to the topic exchange with the commands.{Region} topic and also drops it into the local commands queue. You don't even need the explicit local queue rule if all you want is for a handler in this application to get the message, because the conventional local routing to that handler applies as well -- just like it would if the exchange routing weren't there at all.
TIP
The original overload without the additive parameter is unchanged, and the exchange is the only route that explicit publishing rules or local routing will leave those messages. If you're ever unsure what Wolverine is going to do with a message type, IWolverineRuntime.ExplainRoutingFor(type) will walk you through every routing rule it consulted. The additive routing shows up there as [additive, pass-through]. See message routing diagnostics for more.

