Skip to content

Commit 7e30712

Browse files
Merge pull request #7003 from Particular/asb-configuration
Azure Service Bus Configuration Documentation
2 parents c3a6a55 + 3abb282 commit 7e30712

File tree

10 files changed

+220
-14
lines changed

10 files changed

+220
-14
lines changed

Snippets/ASBS/ASBS_5/ASBS_5.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
</PropertyGroup>
55
<ItemGroup>
66
<PackageReference Include="Azure.Identity" Version="1.12.0" />
7+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
78
<PackageReference Include="NServiceBus.Transport.AzureServiceBus" Version="5.*" />
9+
<PackageReference Include="NUnit" Version="4.3.2" />
10+
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
811
</ItemGroup>
912
</Project>

Snippets/ASBS/ASBS_5/Options.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System.IO;
2+
using System.Text;
3+
using System.Text.Json;
4+
using MyNamespace;
5+
using NServiceBus.Transport.AzureServiceBus;
6+
using NUnit.Framework;
7+
8+
[TestFixture]
9+
public class Options
10+
{
11+
[Test, Explicit]
12+
public void Dump_Migration()
13+
{
14+
#pragma warning disable CS0618 // Type or member is obsolete
15+
var serializedOptions = JsonSerializer.Serialize(new MigrationTopologyOptions
16+
#pragma warning restore CS0618 // Type or member is obsolete
17+
{
18+
QueueNameToSubscriptionNameMap = { { "Publisher", "PublisherSubscriptionName" } },
19+
SubscribedEventToRuleNameMap = { { typeof(NotYetMigratedEvent).FullName, "EventRuleName" } },
20+
TopicToPublishTo = "TopicToPublishTo",
21+
TopicToSubscribeOn = "TopicToSubscribeOn",
22+
EventsToMigrateMap = [typeof(NotYetMigratedEvent).FullName],
23+
PublishedEventToTopicsMap = { { typeof(MigratedEvent).FullName, "MigratedEvent" } },
24+
SubscribedEventToTopicsMap = { { typeof(MigratedEvent).FullName, ["MigratedEvent"] } }
25+
}, TopologyOptionsSerializationContext.Default.TopologyOptions);
26+
27+
var builder = new StringBuilder();
28+
builder.AppendLine("# start" + "code migration-options");
29+
builder.AppendLine(serializedOptions);
30+
builder.AppendLine("# end" + "code migration-options");
31+
32+
File.WriteAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", "migration-options.json"), builder.ToString());
33+
}
34+
35+
[Test, Explicit]
36+
public void Dump_Options()
37+
{
38+
#pragma warning disable CS0618 // Type or member is obsolete
39+
var serializedOptions = JsonSerializer.Serialize(new TopologyOptions
40+
#pragma warning restore CS0618 // Type or member is obsolete
41+
{
42+
QueueNameToSubscriptionNameMap = { { "Publisher", "PublisherSubscriptionName" } },
43+
PublishedEventToTopicsMap = { { typeof(SomeEvent).FullName, "some-event" } },
44+
SubscribedEventToTopicsMap = { { typeof(SomeEvent).FullName, ["some-event"] } }
45+
}, TopologyOptionsSerializationContext.Default.TopologyOptions);
46+
47+
var builder = new StringBuilder();
48+
builder.AppendLine("# start" + "code topology-options");
49+
builder.AppendLine(serializedOptions);
50+
builder.AppendLine("# end" + "code topology-options");
51+
52+
File.WriteAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", "topology-options.json"), builder.ToString());
53+
}
54+
55+
[Test, Explicit]
56+
public void Dump_Options_Inheritance()
57+
{
58+
#pragma warning disable CS0618 // Type or member is obsolete
59+
var serializedOptions = JsonSerializer.Serialize(new TopologyOptions
60+
#pragma warning restore CS0618 // Type or member is obsolete
61+
{
62+
QueueNameToSubscriptionNameMap = { { "Publisher", "PublisherSubscriptionName" } },
63+
PublishedEventToTopicsMap = { { typeof(SomeEvent).FullName, "some-event" } },
64+
SubscribedEventToTopicsMap = { { typeof(SomeEvent).FullName, ["some-event", "some-other-event"] } }
65+
}, TopologyOptionsSerializationContext.Default.TopologyOptions);
66+
67+
var builder = new StringBuilder();
68+
builder.AppendLine("# start" + "code topology-options-inheritance");
69+
builder.AppendLine(serializedOptions);
70+
builder.AppendLine("# end" + "code topology-options-inheritance");
71+
72+
File.WriteAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", "topology-options-inheritance.json"), builder.ToString());
73+
}
74+
}
75+
76+
namespace MyNamespace
77+
{
78+
class NotYetMigratedEvent;
79+
class MigratedEvent;
80+
81+
class SomeEvent;
82+
}

Snippets/ASBS/ASBS_5/Usage.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using System;
2-
using System.Security.Cryptography;
3-
using System.Text;
4-
2+
using System.IO;
3+
using System.Text.Json;
54
using Azure.Identity;
65
using Azure.Messaging.ServiceBus;
76
using NServiceBus;
87
using NServiceBus.Transport;
8+
using NServiceBus.Transport.AzureServiceBus;
99
using Shipping;
1010

1111
class Usage
@@ -55,6 +55,20 @@ class Usage
5555
#endregion
5656
#pragma warning restore CS0618 // Type or member is obsolete
5757

58+
#region asb-options-validation-disable
59+
60+
transport.Topology.OptionsValidator = new TopologyOptionsDisableValidationValidator();
61+
62+
#endregion
63+
64+
#region asb-options-options-loading
65+
66+
using var stream = File.OpenRead("topology-options.json");
67+
var options = JsonSerializer.Deserialize<TopologyOptions>(stream, TopologyOptionsSerializationContext.Default.Options);
68+
var jsonTopology = TopicTopology.FromOptions(options);
69+
70+
#endregion
71+
5872
var topology = TopicTopology.Default;
5973
#region asb-interface-based-inheritance
6074
topology.SubscribeTo<IOrderStatusChanged>("Shipping.OrderAccepted");
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# startcode migration-options
2+
{
3+
"$type": "migration-topology-options",
4+
"TopicToPublishTo": "TopicToPublishTo",
5+
"TopicToSubscribeOn": "TopicToSubscribeOn",
6+
"EventsToMigrateMap": [
7+
"MyNamespace.NotYetMigratedEvent"
8+
],
9+
"SubscribedEventToRuleNameMap": {
10+
"MyNamespace.NotYetMigratedEvent": "EventRuleName"
11+
},
12+
"PublishedEventToTopicsMap": {
13+
"MyNamespace.MigratedEvent": "MigratedEvent"
14+
},
15+
"SubscribedEventToTopicsMap": {
16+
"MyNamespace.MigratedEvent": "MigratedEvent"
17+
},
18+
"QueueNameToSubscriptionNameMap": {
19+
"Publisher": "PublisherSubscriptionName"
20+
}
21+
}
22+
# endcode migration-options
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# startcode topology-options-inheritance
2+
{
3+
"$type": "topology-options",
4+
"PublishedEventToTopicsMap": {
5+
"MyNamespace.SomeEvent": "some-event"
6+
},
7+
"SubscribedEventToTopicsMap": {
8+
"MyNamespace.SomeEvent": [
9+
"some-event",
10+
"some-other-event"
11+
]
12+
},
13+
"QueueNameToSubscriptionNameMap": {
14+
"Publisher": "PublisherSubscriptionName"
15+
}
16+
}
17+
# endcode topology-options-inheritance
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# startcode topology-options
2+
{
3+
"$type": "topology-options",
4+
"PublishedEventToTopicsMap": {
5+
"MyNamespace.SomeEvent": "some-event"
6+
},
7+
"SubscribedEventToTopicsMap": {
8+
"MyNamespace.SomeEvent": "some-event"
9+
},
10+
"QueueNameToSubscriptionNameMap": {
11+
"Publisher": "PublisherSubscriptionName"
12+
}
13+
}
14+
# endcode topology-options

samples/azure-service-bus-netstandard/options/ASBS_5/Publisher/Program.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
2-
using System.Threading;
3-
using System.Threading.Tasks;
4-
using Microsoft.Extensions.Configuration;
1+
using Microsoft.Extensions.Configuration;
52
using Microsoft.Extensions.DependencyInjection;
63
using Microsoft.Extensions.Hosting;
74
using Microsoft.Extensions.Logging;
@@ -19,7 +16,15 @@
1916
var section = builder.Configuration.GetSection("AzureServiceBus");
2017
var topologyOptions = section.GetSection("Topology").Get<TopologyOptions>()!;
2118
var topology = TopicTopology.FromOptions(topologyOptions);
22-
endpointConfiguration.UseTransport(new AzureServiceBusTransport(section["ConnectionString"]!, topology));
19+
var transport = new AzureServiceBusTransport(section["ConnectionString"]!, topology)
20+
{
21+
Topology =
22+
{
23+
// Validation is already done by the generic host so we can disable in the transport
24+
OptionsValidator = new TopologyOptionsDisableValidationValidator()
25+
}
26+
};
27+
endpointConfiguration.UseTransport(transport);
2328
#endregion
2429

2530
endpointConfiguration.UseSerialization<SystemJsonSerializer>();

samples/azure-service-bus-netstandard/options/ASBS_5/Subscriber/Program.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
2-
using System.Threading;
3-
using System.Threading.Tasks;
4-
using Microsoft.Extensions.Configuration;
1+
using Microsoft.Extensions.Configuration;
52
using Microsoft.Extensions.Hosting;
63
using Microsoft.Extensions.Logging;
74
using NServiceBus;
@@ -18,7 +15,15 @@
1815
var topologyOptions = section.GetSection("Topology").Get<TopologyOptions>()!;
1916
var topology = TopicTopology.FromOptions(topologyOptions);
2017

21-
endpointConfiguration.UseTransport(new AzureServiceBusTransport(section["ConnectionString"]!, topology));
18+
var transport = new AzureServiceBusTransport(section["ConnectionString"]!, topology)
19+
{
20+
Topology =
21+
{
22+
// Validation is already done by the generic host so we can disable in the transport
23+
OptionsValidator = new TopologyOptionsDisableValidationValidator()
24+
}
25+
};
26+
endpointConfiguration.UseTransport(transport);
2227
endpointConfiguration.UseSerialization<SystemJsonSerializer>();
2328
endpointConfiguration.EnableInstallers();
2429

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,41 @@
1-
### Topology
1+
### Topology
2+
3+
* `Topology`: The topology used to publish and subscribe to events between endpoints. The topology is shared by the endpoints that need to publish and subscribe to events from each other. The topology has to be explicitly passed into the constructor
4+
5+
Endpoints that do not require backward compatibility with the previous single-topic topology should be using `TopicTopology.Default` which represents the new default [topic-per-event topology](/transports/azure-service-bus/topology.md). For transports requiring compatibility during the migration towards the topic-per-event topology the [upgrade guide](/transports/upgrades/asbs-4to5.md) describes in more details the migration topology.
6+
7+
Topic names must adhere to the limits outlined in [the Microsoft documentation on topic creation](https://docs.microsoft.com/en-us/rest/api/servicebus/create-topic).
8+
9+
#### Mapping
10+
11+
#### Options
12+
13+
It is possible to configure a topology entirely from configuration by loading a serialized version of the options and using the `TopicTopology.FromOptions` to create the topology.
14+
15+
This allows loading topology configuration from Application configuration or any other sources. The options layer also provides support for source generated serializer options as part of `TopologyOptionsSerializationContext`.
16+
17+
The following snippet demonstrates raw deserialization of options and creating the topology from those options. Usage may vary depending on the usage cases. For more details how to load options in the generic host consolidate the [options sample](/samples/azure-service-bus-netstandard/options/).
18+
19+
snippet: asb-options-options-loading
20+
21+
The topology json document for the topic-per-event topology looks following:
22+
23+
snippet: topology-options
24+
25+
In order to support polymorphic events, one event (base type) can be mapped to multiple topics (where the derived events are published):
26+
27+
snippet: topology-options-inheritance
28+
29+
Loading from json is also supported for the migration topology:
30+
31+
snippet: migration-options
32+
33+
##### Validation
34+
35+
During the start of the transport the topology configuration is validated against some of the well known limitations like entity, subscription or rule name lengths and some consistency validation is executed.
36+
37+
The default validator uses data validations and source generated options validation. The default validator can be overriden or the validation can be entirely disabled.
38+
39+
snippet: asb-options-validation-disable
40+
41+
Disabling the validator might be desirable in generic hosting scenarios when the topology options are loaded from the Application configuration and the validator is registered to validate at startup to avoid double validating.

transports/upgrades/asbs-4to5.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Generally, it does not matter whether the publisher or the subscriber is upgrade
155155
Switching the event delivery path to the new topic-per-event-type approach is a two-step process.
156156

157157
First, ensure that the infrastructure for the event delivery (topic and all subscriptions) is created. This can be done in a number of ways:
158+
158159
- If endpoints have installers enabled, the subscribers can be restarted after the event is marked as "migrated" in the topology configuration. This means that during the startup process the necessary infrastructure for the event is created. The old infrastructure (subscription on the common topic) still exists and is being used to deliver the events
159160
- Using the [provided tool](/transports/azure-service-bus/operational-scripting.md)
160161
- Using infrastructure-as-code tools such as Bicep, Terraform, or Pulumi
@@ -173,6 +174,9 @@ Once all events have been migrated, the old single topic can be deleted.
173174

174175
### Migrating from non-default topics or hierarchies
175176

177+
> [!NOTE]
178+
> The methods are already obsoleted to give early notice when the migration topology will be phased out. Due to the obsoletion the methods have `[EditorBrowsable(EditorBrowsableState.Never)]` which may hide those members depending on the IDE settings. This can be solved by explicitly typing out the method signatures as shown below or configure the IDE to show members independent of their browsable state ([Visual Studio](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.editorbrowsableattribute#remarks) or Rider under Editor > General > Code Completion > Filter members by EditorBrowsable attribute).
179+
176180
Use either `TopicTopology.MigrateFromNamedSingleTopic(string topicName)` or `TopicTopology.MigrateFromTopicHierarchy(string topicToPublishTo, string topicToSubscribeOn)`.
177181

178182
The default topic name is `bundle-1`. In case that one is used create the migration topology with `TopicTopology.MigrateFromSingleDefaultTopic()`.

0 commit comments

Comments
 (0)