Getting Started
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Previews
Samples

Connecting multiple SQL Server instances with NServiceBus.Router

NuGet Package: NServiceBus.Router (3.x)

This is a community-maintained project
Target Version: NServiceBus 7.x
Particular Software's NServiceBus.Transport.Bridge package offers similar functionality to the NService.Router community package and should be considered for multi-transport operations.

The sample demonstrates how to use NServiceBus.Router to connect endpoints running SQL Server transport that use different instances of SQL Server. This is an alternative to the multi-instance mode of SQL Server transport which has been removed in Version 4.

Switch vs Backplane

Particular Software's NServiceBus.Transport.Bridge package offers similar functionality to the NService.Router community package and should be considered for multi-transport operations.

Both Switch and Backplane approaches can be used replace the deprecated multi-instance mode in connecting endpoints that use different SQL Server databases. The following table contains a side-by-side comparison of both approaches

SwitchBackplane
Single router for the entire solutionRouter-per-database
Requires distributed transaction support to ensure exactly-once processingExactly-once processing through de-duplication
All SQL Server instances must be in the same networkEach SQL Server instance can be in separate network or even data centre
Centralized forwarding configurationDistributed forwarding configuration
Distributed transactions require Microsoft Distributed Transaction Coordinator (MSDTC) or Azure SQL Elastic Transactions.

The Backplane approach, while more complex in terms of deployment, provides more flexibility e.g. some databases might be on-premise while others might be in the cloud.

Throughput

Both approaches can be used to increase the throughput of the entire system when performance of a single SQL Server instance becomes a bottle neck. The key to thing when using the Switch or Backplane for performance reasons is partitioning. When done wrong, it can have the opposite effect and decrease the overall throughput.

To correctly partition the system when using Switch or Backplane first cluster the endpoints based on the volume of messages exchanged. The more messages endpoint exchange, the closer they are. If all endpoints form a single cluster Switch or Backplane won't help. In a healthy system, however, there will be several clusters of endpoints of highly coupled endpoints. Assign each cluster its own instance of SQL Server. Use Switch or Backplane to connect the clusters.

This sample uses a single SQL Server instance with multiple catalogs to approximate the multi-instance scenario. If the production scenario does indeed uses a single SQL Server instance and multiple catalogs, use SQL Server transport multi-catalog instead of the Router.

Prerequisites

Ensure an instance of SQL Server (Version 2016 or above for custom saga finders sample, or Version 2012 or above for other samples) is installed and accessible on localhost and port 1433. A Docker image can be used to accomplish this by running docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=yourStrong(!)Password' -p 1433:1433 -d mcr.microsoft.com/mssql/server:latest in a terminal.

Alternatively, change the connection string to point to different SQL Server instance.

At startup each endpoint will create its required SQL assets including databases, tables, and schemas.

This sample automatically creates four databases: sqlswitch, sqlswitch_blue, sqlswitch_red and sqlswitch_green

Running the project

  1. Start the solution.
  2. The text Press <enter> to send a message should be displayed in the Client's console window.
  3. Hit enter several times to send some messages.

Verifying that the sample works correctly

  1. The Sales console display information about accepted orders in round-robin fashion.
  2. The Shipping endpoint displays information that orders were shipped.
  3. The Billing endpoint displays information that orders were billed.

Code walk-through

This sample contains four endpoints, Client, Sales, Shipping and Billing. The Client endpoint sends a PlaceOrder command to Sales. When PlaceOrder is processed, Sales publishes the OrderAccepted event which is subscribed by Shipping and Billing.

Client

The Client endpoint is configured to use its own, Blue, database to harden the security of the solution. This database does not contain sensitive data.

In order to route messages to Sales, Client needs to configure the router connection

var routing = transport.Routing();
var router = routing.ConnectToRouter("Switch");
router.RouteToEndpoint(typeof(PlaceOrder), "Red.Sales");

Sales and Shipping

The Sales and Shipping endpoints are configured to use the Red database for the transport. As Sales only publishes events, it does not need any routing configuration.

Shipping subscribes for events published by Sales and it uses the same transport database so the router is not involved.

Billing

The Billing endpoint requires even more enhanced security. It uses its own database, Green. In order to subscribe to Sales event it need to register the publisher in the router configuration

var routing = transport.Routing();
var router = routing.ConnectToRouter("Switch");
router.RegisterPublisher(typeof(OrderAccepted), "Red.Sales");

Switch

The Switch project hosts the NServiceBus.Router instance that has three interfaces, one for each SQL Server transport database.

var routerConfig = new RouterConfiguration("Switch");

routerConfig.AddInterface<SqlServerTransport>("Blue", t => { t.ConnectionString(ConnectionStrings.Blue); });
routerConfig.AddInterface<SqlServerTransport>("Red", t => { t.ConnectionString(ConnectionStrings.Red); });
routerConfig.AddInterface<SqlServerTransport>("Green", t => { t.ConnectionString(ConnectionStrings.Green); });

routerConfig.AutoCreateQueues();

The forwarding of messages between the databases is governed by an endpoint naming convention: the first part of the endpoint name is used as the destination interface name.

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("Red."),
    destinationFilterDescription: "To Red",
    gateway: null,
    iface: "Red");

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

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

Samples

Related Articles


Last modified