Topic Based Routing
While the Azure Service Bus Transport and RabbitMQ Transport options both allow you to explicitly specify message routing rules to a specific named topic (routing key in Rabbit MQ parlance) through the fluent interface, there's another option that amounts to "publish these messages to a topic derived from the message type." This usage is valuable to allow you top opt into more sophisticated publish/subscribe routing utilizing all the power of tools like Rabbit MQ or Azure Service Bus or whatever other transports Jasper supports later.
Here's an example of configuring this option with Rabbit MQ:
public class TopicSendingApp : JasperOptions
{
public TopicSendingApp()
{
Endpoints.ConfigureRabbitMq(rabbit =>
{
rabbit.ConnectionFactory.HostName = "localhost";
rabbit.DeclareExchange("numbers", e => e.ExchangeType = ExchangeType.Topic);
rabbit.AutoProvision = true;
});
// This directs Jasper to send all messages
// to the "numbers" exchange in Rabbit MQ with
// a routing key derived from the message type
Endpoints.PublishAllMessages()
.ToRabbitTopics("numbers")
.OutgoingTopicNameIs<NumberMessage>(x => x.Topic);
Extensions.UseMessageTrackingTestingSupport();
}
}
And another example doing the same configuration with Azure Service Bus:
public class TopicSendingApp : JasperOptions
{
public TopicSendingApp( )
{
Endpoints.ConfigureAzureServiceBus(asb =>
{
asb.ConnectionString = end_to_end.ConnectionString;
});
// This directs Jasper to send all messages to
// an Azure Service Bus topic name derived from the
// message type
Endpoints.PublishAllMessages()
.ToAzureServiceBusTopics()
.OutgoingTopicNameIs<NumberMessage>(x => x.Topic);
}
}
Using one of the two configured applications above, you could send this message type:
[Topic("items")]
public class ItemCreated
{
public string Name { get; set; }
}
like so:
public static async Task SendMessage(IMessagePublisher publisher)
{
await publisher.Send(new ItemCreated
{
Name = "NewItem"
});
}
In Jasper's internal routing, it would determine that the topic name for ItemCreated
is items and publish to either the configured Rabbit MQ topic exchange or Azure Service Bus connection using the topic name items.
Note!
Keep reading to the next section if you dislike using attributes, because the [Topic]
attribute is not required on your message classes.
How Topic Name is Derived
When you publish a message with Jasper that is routed by topic name, the topic name is derived from the message and/or message type with this order of precedence:
- A user-supplied topic name through the
IMessagePublisher.SendToTopic(message, topicName)
as shown in the section below titled Explicitly Send to a Named Topic - A
[Topic]
attribute directly on the message class - Any applicable Topic Naming Rules as shown in a section below
- The message identifier for the message type, which can in turn be overridden with the
[MessageIdentity]
attribute
Explicitly Send to a Named Topic
You can override the topic routing with explicit code like this sample shown below:
public static async Task SendToTopic(IMessagePublisher publisher)
{
var @event = new ItemCreated
{
Name = "New Thing"
};
// This call sends the ItemCreated message to the
// "NorthAmerica" topic
await publisher.SendToTopic(@event, "NorthAmerica");
}
Using the [Topic] Attribute
You can explicitly set the topic name for a message type by decorating it or its parent type with
the [Topic]
attribute like this:
[Topic("one")]
public class TopicMessage1
{
}
Topic Naming Rules
You can use a topic naming rule in your system that derives the topic name for a message using some combination of the message type and message instance.
Take for an example (stolen from Rabbit MQ documentation) where a custom logging message may be effectively routed by its Priority
:
public class LogMessage
{
public string Message { get; set; }
public string Priority { get; set; }
}
You can set up a topic naming rule to use the value of the LogMessage.Priority
property as the topic name like so:
public class PublishWithTopicRulesApp : JasperOptions
{
public PublishWithTopicRulesApp()
{
Endpoints.PublishAllMessages()
.ToAzureServiceBusTopics()
// This is setting up a topic name rule
// for any message of type that can be
// cast to LogMessage
.OutgoingTopicNameIs<LogMessage>(x => x.Priority);
}
public override void Configure(IHostEnvironment hosting, IConfiguration config)
{
var connectionString = config.GetConnectionString("azureservicebus");
Endpoints.ConfigureAzureServiceBus(connectionString);
}
}
Finally, in usage you just use IMessagePublisher.Send()
as you normally do:
public static async Task SendLogMessage(IMessagePublisher publisher)
{
var message = new LogMessage
{
Message = "Watch out!",
Priority = "High"
};
// In this sample, Jasper will route the LogMessage
// message to the "High" topic
await publisher.Send(message);
}