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");

Registering multiple publishers for the same event

The messaging bridge will not allow registering multiple publishers for the same event according to the events should be published by the logical owner convention.

To allow this, use the NServiceBus.MessagingBridge version 2.1 or above and disable best practices enforcement:

bridgeConfiguration.DoNotEnforceBestPractices();

If the best practices are not enforced the bridge will log the following warning:

The following subscriptions with multiple registered publishers are ignored as best practices are not enforced:

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");

Error queue

By default, when the bridge transfers a message to the ServiceControl error queue it will not attempt to translate the NServiceBus.ReplyToAddress message header. This means that endpoints only need to be registered with the bridge if they are directly involved with messages transferred between transports. The drawback is that if an endpoint is moved to a different transport, then any failed messages from that endpoint that perform a ReplyTo operation as part of the handler logic, cannot be retried since the value in the NServiceBus.ReplyToAddress header is unreachable.

This behavior can be changed by configuring the bridge to translate the NServiceBus.ReplyToAddress message header for failed messages.

bridgeConfiguration.TranslateReplyToAddressForFailedMessages();

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.

Monitoring

Heartbeats

Version 2.2 of the NServiceBus Messaging Bridge adds support for the heartbeats feature. This feature sends regular heartbeat messages from each messaging bridge transport to a ServiceControl instance. The ServiceControl instance keeps track of which endpoint instances are sending heartbeats and which ones are not. See the Hearbeats plugin documentation for more information.

Heartbeats can be configured on a per bridge transport basis. For each bridge transport that should send hearbeats, use the .SendHeartbeatTo as follows:

bridgeTransport.SendHeartbeatTo(
    serviceControlQueue: "ServiceControl_Queue",
    frequency: TimeSpan.FromSeconds(15),
    timeToLive: TimeSpan.FromSeconds(30));

Heartbeat interval

Heartbeat messages are sent at a default frequency of 10 seconds. As shown above, the frequency may be overridden for each endpoint.

Time-To-Live (TTL)

Heartbeat messages are sent with a default TTL of four times the frequency. As shown above, the TTL may be overridden for each endpoint. See the documentation for expired heartbeats for more information.

Identifying scaled-out endpoints

When heartbeats are being used in a scaled-out Messaging Bridge that is using competing consumers, each bridge transport must be configured with a unique name (see Configuring transports). The bridge transport name is used in the heartbeat messages so ServiceControl can track of all instances and identify which instance sent a given heartbeat message.

Custom checks

Version 2.2 of the Messaging Bridge adds support for custom checks. The custom checks feature enables Messaging Bridge health monitoring by running custom code and reporting status (success or failure) to a ServiceControl instance. See the custom checks plugin documentation for more information. For documentation on how to write custom checks, see the Writing Custom Checks document

The custom checks feature can be enabled per bridge transport by using ReportCustomChecksTo as follow:

bridgeTransport.ReportCustomChecksTo(
    serviceControlQueue: "ServiceControl_Queue",
    timeToLive: TimeSpan.FromSeconds(30));

Time-To-Live (TTL)

Custom check results are sent with a default TTL of four times the interval for periodic checks or infinite for one-time checks. As shown above, the TTL may be overridden for each bridge transport.