Multi-Instance Mode to Bridge migration

Component: SQL Server Transport
NuGet Package NServiceBus.SqlServer (4.x)
Target NServiceBus Version: 7.x

The multi-instance mode has been deprecated in Version 4 of SQL Server transport. NServiceBus topologies with queues distributed between multiple catalogs and SQL Server instances can be migrated using a combination of Transport Bridge and multi-catalog addressing.

This samples shows how the Transport Bridge can be utilized when endpoints connect to catalogs hosted in different instances of SQL Server. For solutions where all catalogs are hosted within a single SQL Server instance, see multi-catalog addressing.

Prerequisites

An instance of SQL Server Express is installed and accessible as .\SqlExpress.

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

The databases created automatically by this sample are NsbSamplesSqlMultiInstanceReceiver3, NsbSamplesSqlMultiInstanceSender3, NsbSamplesSqlMultiInstanceReceiver4, NsbSamplesSqlMultiInstanceSender3 and NsbSamplesSqlMultiInstanceBridge.

Ensure Distributed Transaction Coordinator (DTC) is running. It can be started from the command line by running net start msdtc.

Running the project

  1. Start all projects.
  2. Hit enter in Sender V3 and V4 consoles window to send messages.

Verifying that the sample works correctly

  1. The Receiver displays information that an order was submitted.
  2. The Sender displays information that the order was accepted.

Code walk-through

This sample contains the following projects:

  • Sender.V3 - A console application using NServiceBus Version 6 and SQL Server transport Version 3 responsible for sending the initial ClientOrder message and processing the follow-up ClientOrderAccepted message.
  • Receiver.V3 - A console application using NServiceBus Version 6 and SQL Server transport Version 3 responsible for processing the order message.
  • Sender.V4 - A sender application upgraded to Version 7 of NServiceBus and Version 4 of SQL Server transport.
  • Receiver.V4 - A receiver application upgraded to Version 7 of NServiceBus and Version 4 of SQL Server transport.
  • Shared - A class library containing message definitions.

Sender project

The Sender does not store any data. It mimics the front-end system where orders are submitted by the users and passed via the bus to the back-end. In Version 3 it is configured to use SQL Server transport and run in the multi-instance mode. ConnectionProvider.GetConnection method is used for providing connections.

var endpointConfiguration = new EndpointConfiguration("Samples.SqlServer.MultiInstanceSender");
var transport = endpointConfiguration.UseTransport<SqlServerTransport>();
transport.EnableLegacyMultiInstanceMode(ConnectionProvider.GetConnection);
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();

transport.Routing().RouteToEndpoint(typeof(ClientOrder), "Samples.SqlServer.MultiInstanceReceiver");

The Sender sends a message to the Receiver:

4.x NServiceBus.SqlServer
var order = new ClientOrder
{
    OrderId = Guid.NewGuid()
};
await endpoint.Send(order).ConfigureAwait(false);
4.x NServiceBus.SqlServer
var order = new ClientOrder
{
    OrderId = Guid.NewGuid()
};
await endpoint.Send(order).ConfigureAwait(false);

In Version 4 the sender is configured to route the ClientOrder messages through a bridge:

var endpointConfiguration = new EndpointConfiguration("Samples.SqlServer.MultiInstanceSender");
var transport = endpointConfiguration.UseTransport<SqlServerTransport>();
transport.ConnectionString(ConnectionString);
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();

var bridgeConfig = transport.Routing().ConnectToBridge("Bridge-Sender");
bridgeConfig.RouteToEndpoint(typeof(ClientOrder), "Samples.SqlServer.MultiInstanceReceiver");

Receiver project

The Receiver mimics a back-end system. In Version 3 it is configured to use SQL Server transport in the multi-instance mode.

var endpointConfiguration = new EndpointConfiguration("Samples.SqlServer.MultiInstanceReceiver");
var transport = endpointConfiguration.UseTransport<SqlServerTransport>();
transport.EnableLegacyMultiInstanceMode(ConnectionProvider.GetConnection);
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();

It receives ClientOrder message sent by Sender and replies to them with ClientOrderAccepted.

4.x NServiceBus.SqlServer
public Task Handle(ClientOrder message, IMessageHandlerContext context)
{
    log.Info($"Handling ClientOrder with ID {message.OrderId}");
    var clientOrderAccepted = new ClientOrderAccepted
    {
        OrderId = message.OrderId
    };
    return context.Reply(clientOrderAccepted);
}
4.x NServiceBus.SqlServer
public Task Handle(ClientOrder message, IMessageHandlerContext context)
{
    log.Info($"Handling ClientOrder with ID {message.OrderId}");
    var clientOrderAccepted = new ClientOrderAccepted
    {
        OrderId = message.OrderId
    };
    return context.Reply(clientOrderAccepted);
}

In Version 4 the receiver does not require any special configuration. The reply is sent back to the bridge:

var endpointConfiguration = new EndpointConfiguration("Samples.SqlServer.MultiInstanceReceiver");
var transport = endpointConfiguration.UseTransport<SqlServerTransport>();
transport.ConnectionString(ConnectionString);
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.SendFailedMessagesTo("error");
endpointConfiguration.EnableInstallers();

Bridge

The bridge application runs the Transport Bridge component:

var bridgeConfig =
    Bridge.Between<SqlServerTransport>("Bridge-Sender",
            t => t.ConnectionString(SenderConnectionString))
        .And<SqlServerTransport>("Bridge-Receiver",
            t => t.ConnectionString(ReceiverConnectionString));

bridgeConfig.AutoCreateQueues();
bridgeConfig.UseSubscriptionPersistece<SqlPersistence>((e, p) =>
{
    p.ConnectionBuilder(() => new SqlConnection(BridgeConnectionString));
    p.SqlDialect<SqlDialect.MsSqlServer>();
    p.SubscriptionSettings().DisableCache();
});

The bridge is configured to use SQL Server transport on both sides and to use SQL Persistence-based subscription storage for Pub/Sub. It uses the default TransactionScope transport transaction mode to ensure that exactly-once semantics are preserved when messages traverse the bridge.

How it works

In Version 3 both endpoints have to know each other's connection strings. When sending a message a connection string is selected based on the destination queue address. Because the messages don't contain information about the origin's queue connection string, the receiver has to know the connection string to use when sending a reply. This creates unnecessary physical coupling back to the sender.

In Version 4 the Transport Bridge mediates between the sender and the receiver. Based on sender's bridge connector configuration the transport sends the message not to the designated endpoint but to the bridge. The bridge forwards it to the destination, replacing the reply-to address. Because of this the receiver does not have to know if sender is behind the bridge or not. The reply is routed back to the bridge and forwarded to the originator without requiring any specific receiver-side configuration.

Related Articles


Last modified