Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Samples

Multi-Instance Mode

Component: Messaging Bridge
NuGet Package: NServiceBus.MessagingBridge (2.x)
Target Version: NServiceBus 8.x

This is sample shows how to use the NServiceBus Messaging Bridge instead of the deprecated SQL Server transport multi-instance mode.

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.

The databases created by this sample are NsbSamplesSqlMultiInstanceReceiver and NsbSamplesSqlMultiInstanceSender.

Running the project

  1. Start the following projects:

    1. Sender
    2. Receiver
    3. Bridge
  2. Press enter in the Sender's console window to send a new message.

Verifying that the sample works correctly

  1. The Receiver displays information that an order was submitted.
  2. The Sender displays information that a response arrived for the same order.

Code walk-through

The sample contains the following projects:

  • Sender: A console application responsible for sending the initial ClientOrder message and processing the follow-up ClientOrderResponse message.
  • Receiver: A console application responsible for processing the order message.
  • Bridge: A console application responsible for routing messages across the two database instances.
  • Messages: A class library containing message definitions.
  • Helpers: A class library for creating the databases and schemas.

Sender project

The sender does not store any data. It mimics a front-end system where orders are submitted by the users and passed via the bus to the back-end. It is configured to use the SQL Server transport. Other than that, it is unaware the other endpoint is running on a different database instance.

var endpointConfiguration = new EndpointConfiguration("Samples.SqlServer.MultiInstanceSender");
var routing = endpointConfiguration.UseTransport(new SqlServerTransport(ConnectionString)
{
    TransportTransactionMode = TransportTransactionMode.ReceiveOnly
});

endpointConfiguration.UseSerialization<SystemJsonSerializer>();
endpointConfiguration.EnableInstallers();

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

The sender sends a message to the receiver:

var order = new ClientOrder
{
    OrderId = Guid.NewGuid()
};
await endpoint.Send(order);

Receiver project

The receiver mimics a back-end system. It is configured to use the SQL Server transport but has a different connection string from the sender.

var endpointConfiguration = new EndpointConfiguration("Samples.SqlServer.MultiInstanceReceiver");
endpointConfiguration.UseTransport(new SqlServerTransport(ConnectionString)
{
    TransportTransactionMode = TransportTransactionMode.ReceiveOnly
});

endpointConfiguration.UseSerialization<SystemJsonSerializer>();
endpointConfiguration.EnableInstallers();

Note that the endpoint configuration contains no routing information, as the response message is a regular reply and NServiceBus, together with the bridge, will take care of all the routing with reply messages. The receiver replies with ClientOrderResponse back to the sender.

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

Bridge

The bridge is configured with two transports. As both transports are of the same SqlServerTransport type, it is required to provide a name to each BridgeTransport to distinguish between the two in log files.

var receiverTransport = new BridgeTransport(new SqlServerTransport(ReceiverConnectionString))
{
    Name = "Receiver",
    AutoCreateQueues = true
};

receiverTransport.HasEndpoint("Samples.SqlServer.MultiInstanceReceiver");

var senderTransport = new BridgeTransport(new SqlServerTransport(SenderConnectionString))
{
    Name = "Sender",
    AutoCreateQueues = true
};

senderTransport.HasEndpoint("Samples.SqlServer.MultiInstanceSender");

bridgeConfiguration.AddTransport(receiverTransport);
bridgeConfiguration.AddTransport(senderTransport);

// .NET 6 does not support distributed transactions
bridgeConfiguration.RunInReceiveOnlyTransactionMode();

Both transports have the endpoints defined on their side and as a result, the bridge will mimic those endpoints on the other side. This way it becomes transparent to actual endpoints on either side that those endpoints are actually bridged.

Related Articles