NServiceBus Router

Source
NuGet Package NServiceBus.Router (2.x) | License
This is a community maintained project. License and support are independent of Particular Software.
Target NServiceBus Version: 7.x

NServiceBus.Router is a universal component that connects parts of an NServiceBus-based solution that otherwise could not talk to each other (e.g. because they use different transports or transport settings).

Unlike the gateway or the wormhole, the router handles both sending and publishing. Unlike the bridge, the router can use site-based addressing to route messages between logically significant sites (just like the gateway does).

The router is transparent to the publishing and replying endpoint. That is:

  • The endpoint that replies to a message does not have to know if the initiating message came through a router. The reply will be routed automatically to the correct router and then forwarded to the initiating endpoint.
  • The endpoint that publishes events does not have to know if the subscribers are behind the router.

Connecting to the router

Regular endpoints connect to the router using connectors that allow them to configure the routing

var endpointConfiguration = new EndpointConfiguration("MyEndpoint");
var transport = endpointConfiguration.UseTransport<MsmqTransport>();
var routing = transport.Routing();
var bridge = routing.ConnectToRouter("MyRouter");

//routing.RouteToEndpoint(ty);
bridge.RouteToEndpoint(typeof(MyMessage), "Receiver");
bridge.RegisterPublisher(typeof(MyEvent), "Publisher");

The snippet above tells the endpoint that a designated router listens on queue MyRouter and that messages of type MyMessage should be sent to the endpoint Receiver via the router. It also tells the subscription infrastructure that the event MyEvent is published by the endpoint Publisher that is hosted behind the router.

Router configuration

NServiceBus.Router is packaged as a host-agnostic library. It can be hosted e.g. inside a console application or a Windows service. It can also be co-hosted with regular NServiceBus endpoints in the same process.

The following snippet shows a simple MSMQ-to-RabbitMQ router configuration

var routerConfig = new RouterConfiguration("MyRouter");

routerConfig.AddInterface<MsmqTransport>("Msmq", 
    customization: transportExtensions => { });

routerConfig.AddInterface<MsmqTransport>("Rabbit",
    customization: transportExtensions =>
    {
        transportExtensions.ConnectionString("host=localhost");
    });

The router has a simple life cycle:

var router = Router.Create(routerConfig);

await router.Start().ConfigureAwait(false);

await router.Stop().ConfigureAwait(false);

The router can be configured to create all required queues on startup:

routerConfig.AutoCreateQueues(identity: "Bob");

Error handling

The router has a built-in retry strategy for error handling. It retries forwarding each message a number of times (immediate retries) and then moves it to the back of the input queue incrementing a delayed retry counter. If that counter reaches the maximum configured value, the message is moved to the poison message queue. The following snippet shows how it can be configured:

routerConfig.CircuitBreakerThreshold = 20;
routerConfig.DelayedRetries = 10;
routerConfig.ImmediateRetries = 10;

In addition to immediate and delayed retries, the router has built-in outage detection through a circuit breaker. After a number of consecutive failures, the circuit breaker is triggered which causes the interface to enter the throttled mode. In this mode, the interface processes a single message at a time and pauses after each processing attempt. The interface goes back to the normal mode after the first successful processing attempt. When in the throttled mode, the router does not increment the delayed retries counter to prevent messages being sent to the poison message queue due to infrastructure outages.

Topologies

A router consists of multiple NServiceBus.Raw endpoints, called interfaces, and a routing protocol that controls how messages should be forwarded between them.

Bridge

Bridge

The arrows show the path of messages sent from Endpoint A to Endpoint C and from Endpoint D to Endpoint B. Each message is initially sent to the router queue and then forwarded to the destination queue. There is one additional hop compared to a direct communication between endpoints. The following snippet configures the built-in static routing protocol to forward messages between the router's interfaces.

var staticRouting = routerConfig.UseStaticRoutingProtocol();

//Forward all messages from MSMQ to RabbitMQ
staticRouting.AddForwardRoute(
    incomingInterface: "Msmq",
    outgoingInterface: "Rabbit");

//Forward all messages from RabbitMQ to MSMQ
staticRouting.AddForwardRoute(
    incomingInterface: "Rabbit",
    outgoingInterface: "Msmq");

Multi-way routing

Multi-way

The router is not limited to only two interfaces but in case there are more than two interfaces, the routing protocol rules will be more complex and specific. The following snippet configures the built-in static routing protocol to forward messages to interfaces based on the prefix of the destination endpoint's name.

var routerConfig = new RouterConfiguration("MyRouter");

routerConfig.AddInterface<SqlTransport>("A", transportExtensions => {});
routerConfig.AddInterface<SqlTransport>("B", transportExtensions => {});
routerConfig.AddInterface<SqlTransport>("C", transportExtensions => {});

var staticRouting = routerConfig.UseStaticRoutingProtocol();
//Send all messages to endpoints which name starts with Sales via interface A
staticRouting.AddRoute(
    destinationFilter: (iface, destination) => destination.Endpoint.StartsWith("Sales."),
    destinationFilterDescription: "To Sales", 
    gateway: null, 
    iface: "A");

staticRouting.AddRoute(
    (iface, destination) => destination.Endpoint.StartsWith("Shipping."), 
    "To Shipping", null, "B");

staticRouting.AddRoute(
    (iface, destination) => destination.Endpoint.StartsWith("Billing."),
    "To Billing", null, "C");
All three interfaces use the same transport type (SQL Server Transport) but may use different settings, e.g. different database instances. This way, each part of the system (Sales, Shipping and Billing) can be autonomous and own its database server yet they can still exchange messages in the same way as if they were connected to a single shared instance.

Backplane

Backplane

Two or more routers can be connected together to form a backplane topology. This setup often makes sense for geo-distributed systems. The following snippet configures the router hosted in the European part of the globally distributed system to route messages coming from outside via the Azure Storage Queues interface directly to the local endpoints and to route messages sent by local endpoints to either east or west United States through designated gateway routers.

The designated gateway concept is not related to the NServiceBus.Gateway package. When the designated gateway is specified in the route, the message is forwarded to it instead of the actual destination.
var routerConfig = new RouterConfiguration("Router.WestEurope");

routerConfig.AddInterface<MsmqTransport>("Endpoints", transportExtensions => { });
routerConfig.AddInterface<AzureStorageQueuesTransport>("Backplane", transportExtensions => { });

var staticRouting = routerConfig.UseStaticRoutingProtocol();

//Send all messages from the Backplane interface directly to the destination endpoints
staticRouting.AddRoute(
    (iface, destination) => iface == "Backplane",
    "From outside", null, "Endpoints");

//Send all messages to site WestUS through the Backplane interface via Router.WestUS
staticRouting.AddRoute(
    destinationFilter: (iface, destination) => destination.Site == "WestUS",
    destinationFilterDescription: "To West US",
    gateway: "Router.WestUS",
    iface: "Backplane");

//Send all messages to site EastUS through the Backplane interface via Router.EastUS
staticRouting.AddRoute(
    (iface, destination) => destination.Site == "EastUS",
    "To East US", "Router.EastUS", "Backplane");
As an example the routing rules here use the Site property that can be set through the SendOptions object when sending messages. The backplane topology does not require site-based routing and can be configured e.g. using endpoint-based convention like in the multi-way routing example.

Samples


Last modified