Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Samples

Bridge configuration options

Hosting

NServiceBus Messaging Bridge is hosted using the .NET Generic Host which takes care of life cycle management, configuration, logging, and other concerns.

await Host.CreateDefaultBuilder()
    .UseNServiceBusBridge(bridgeConfiguration =>
    {
        // Configure the bridge
    })
    .Build()
    .RunAsync();

The overload that accepts a HostBuilderContext provides access to the IConfiguration type and other host related details.

await Host.CreateDefaultBuilder()
    .UseNServiceBusBridge((hostBuilderContext, bridgeConfiguration) =>
    {
        var connectionString = hostBuilderContext.Configuration.GetValue<string>("MyBridge:AzureServiceBusConnectionString");
        var concurrency = hostBuilderContext.Configuration.GetValue<int>("MyBridge:Concurrency");

        var transport = new BridgeTransport(new AzureServiceBusTransport(connectionString))
        {
            Concurrency = concurrency
        };

        bridgeConfiguration.AddTransport(transport);

        // more configuration...
    })
    .Build()
    .RunAsync();

Registering endpoints

If a logical endpoint communicates with other endpoints that use a different transport, it must be registered with the bridge. Endpoints are registered with the bridge on the transport they run on. The bridge then creates a proxy endpoint on each transport that needs to be bridged.

await Host.CreateDefaultBuilder()
    .UseNServiceBusBridge((ctx, bridgeConfiguration) =>
    {
        var msmq = new BridgeTransport(new MsmqTransport());
        msmq.HasEndpoint("Sales");
        msmq.HasEndpoint("Shipping");

        var asb = new BridgeTransport(new AzureServiceBusTransport(connectionString));
        asb.HasEndpoint("Finance.Invoicing");
        asb.HasEndpoint("Finance.Billing");

        bridgeConfiguration.AddTransport(msmq);
        bridgeConfiguration.AddTransport(asb);
    })
    .Build()
    .RunAsync();

Registering publishers

When NServiceBus discovers a message handler in an endpoint for an event, it automatically subscribes to this event on the transport. The publisher itself is not aware of this, since it does not receive a notification when a subscriber subscribes to an event. This represents a challenge for the bridge.

If an endpoint subscribes to an event, the bridge must be made aware of this subscription since it must register the same subscription on the transports it's bridging. As the bridge is unaware of any subscriptions, the bridge must be configured to mimic the behavior of the endpoints.

The result is duplicate subscriptions for any endpoint that subscribes to an event. The endpoint that publishes the event must be configured as well.

var msmq = new BridgeTransport(new MsmqTransport());
msmq.HasEndpoint("Sales");
msmq.HasEndpoint("Finance.Billing");

var asb = new BridgeTransport(new AzureServiceBusTransport(connectionString));
asb.HasEndpoint("Shipping");

var invoicing = new BridgeEndpoint("Finance.Invoicing");
invoicing.RegisterPublisher(typeof(OrderBilled), "Finance.Billing");
invoicing.RegisterPublisher<OrderShipped>("Shipping");
invoicing.RegisterPublisher("Messages.OrderPlaced", "Sales");

asb.HasEndpoint(invoicing);

Legacy transport versions that use message-driven pub/sub require the fully qualified assembly type name value to be passed. Note that passing the culture and public key is not needed -- only the type name, assembly name, and assembly version are used in filtering subscribers by the message-driven pub/sub-feature.

// Type.AssemblyQualifiedName Property value
invoicing.RegisterPublisher("CreditApproved, CreditScoring.Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Sales");
// Type.AssemblyQualifiedName Property but trimmed without Culture and PublicKeyToken as these are ignored by the message driven pub/sub feature
invoicing.RegisterPublisher("CreditApproved, CreditScoring.Messages, Version=1.0.0.0", "Sales");

Referencing event types

When an assembly containing message types is referenced, either typeof() or a type argument may be used for type-safety when registering publishers. Sometimes it is not possible to reference an assembly containing message types. For example, the assembly may reference a different version of NServiceBus than the bridge. In these cases, the fully-qualified name of an event may be used instead. This may even be preferable to referencing message assemblies, to reduce the chance of compile-time conflicts.

Provisioning queues

By default, the bridge does not create queues for the endpoints that it proxies. This is done so that elevated privileges (which are often needed to create the queues) are not required at runtime.

The queues can be created using one of the following methods:

  • Provisioning them manually using the tooling provided by the queuing system.
  • Using the queue creation tooling provided by Particular Software if one exists for the transports being used. See the individual transports documentation for more details.
  • Configuring the bridge to create queues of proxied endpoints automatically as described in the next section.

Automatic queue provisioning

Automatic queue creation for proxied endpoints is enabled by configuring the bridge as follows:

var msmq = new BridgeTransport(new MsmqTransport())
{
    AutoCreateQueues = true
};

var azureServiceBus = new BridgeTransport(new AzureServiceBusTransport(connectionString))
{
    AutoCreateQueues = true
};

The diagram below shows a simple MSMQ-to-AzureServiceBus configuration involving two endpoints.

flowchart LR Br(Bridge) Sales[Endpoint Sales] <---> Br Br <---> Billing[Endpoint Billing] subgraph MSMQ Sales end subgraph AzureServiceBus Billing end

and the following bridge configuration

msmq.HasEndpoint("Sales");
azureServiceBus.HasEndpoint("Billing");

When automatic queue creation is enabled a "Sales" proxy endpoint is created on the AzureServiceBus transport and a "Billing" proxy endpoint is created on the MSMQ transport. These proxy endpoints represent the endpoint on the other side of the bridge.

MSMQAutoCreatedAzureServiceBusAutoCreated
SalesFalseSalesTrue
BillingTrueBillingFalse

The "Sales" queue on the MSMQ transport and the "Billing" queue on the AzureServiceBus transport are assumed to be created by the endpoints connected on those transport and therefore are not owned by the bridge queue creation.

Custom queue address

The bridge provides the ability to change the address of the queue of incoming messages.

var transport = new BridgeTransport(new MsmqTransport());
transport.HasEndpoint("Finance", "finance@machinename");

var endpoint = new BridgeEndpoint("Sales", "sales@another-machine");
transport.HasEndpoint(endpoint);

Recoverability

If a message fails while it is being forwarded to the target transport, the following recoverability actions are taken:

  1. Three immediate retries are performed to make sure that the problem isn't transient.
  2. If the retries fail, the message is moved to the bridge error queue.

Error queue

The error queue used by the bridge is named bridge.error by default. Note that the default error queue used by other platform components can not be used to enable bridging of the system-wide error queue since a bridged queue may not be used as the error queue. See the documentation around bridging platform queues for more details.

A different error queue may be configured as follows:

var msmq = new BridgeTransport(new MsmqTransport())
{
    ErrorQueue = "my-msmq-bridge-error-queue"
};

var azureServiceBus = new BridgeTransport(new AzureServiceBusTransport(connectionString))
{
    ErrorQueue = "my-asb-bridge-error-queue"
};

Messages moved to the error queue have the NServiceBus.FailedQ header set to allow scripted retries. Refer to the documentation for the various transports for more details on how to perform retries.

Auditing

The bridge add a NServiceBus.Bridge.Transfer header to a message while that message is transferred between transports.

The value of the header is {source-transport-name}->{target-transport-name}. For example: msmq->sqlserver. This header provides traceability for a message as it moves through the bridge.

Configuring transports

By default, the bridge assigns a transport name based on the type of transport being used. This means that when bridging transports of the same type, each transport must be given a unique name. For example:

var azureServiceBus1 = new BridgeTransport(new AzureServiceBusTransport(connectionStringNamepace1))
{
    Name = "asb-namespace-1"
};

var azureServiceBus2 = new BridgeTransport(new AzureServiceBusTransport(connectionStringNamepace2))
{
    Name = "asb-namespace-2"
};

Bridging platform queues

The bridge can be configured to allow a single ServiceControl installation to manage and monitor endpoints on all bridged transports. For example:

var transportWhereServiceControlIsInstalled = new BridgeTransport(new MsmqTransport());

transportWhereServiceControlIsInstalled.HasEndpoint("Particular.ServiceControl");
transportWhereServiceControlIsInstalled.HasEndpoint("Particular.Monitoring");
transportWhereServiceControlIsInstalled.HasEndpoint("error");
transportWhereServiceControlIsInstalled.HasEndpoint("audit");

Audit queue

Special considerations are required for the audit queue due to potentially high message volume. For example, a dedicated ServiceControl audit instance could be created for each bridged transport, to make audit ingestion more efficient.