From 3fa15a2f4d40da69de90dc3da877345a7b6f9f22 Mon Sep 17 00:00:00 2001 From: Tim Bussmann Date: Wed, 11 Nov 2020 11:54:56 +0100 Subject: [PATCH 1/3] remove unnecessary base class --- .../Serverless/DeterministicGuid.cs | 21 ++++ .../ServerlessEndpointConfiguration.cs | 110 ------------------ ...erviceBusTriggeredEndpointConfiguration.cs | 81 ++++++++++++- 3 files changed, 99 insertions(+), 113 deletions(-) create mode 100644 src/NServiceBus.AzureFunctions.ServiceBus/Serverless/DeterministicGuid.cs delete mode 100644 src/NServiceBus.AzureFunctions.ServiceBus/Serverless/ServerlessEndpointConfiguration.cs diff --git a/src/NServiceBus.AzureFunctions.ServiceBus/Serverless/DeterministicGuid.cs b/src/NServiceBus.AzureFunctions.ServiceBus/Serverless/DeterministicGuid.cs new file mode 100644 index 00000000..c14d44cb --- /dev/null +++ b/src/NServiceBus.AzureFunctions.ServiceBus/Serverless/DeterministicGuid.cs @@ -0,0 +1,21 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace NServiceBus.AzureFunctions.ServiceBus +{ + static class DeterministicGuid + { + public static Guid Create(string data) + { + // use MD5 hash to get a 16-byte hash of the string + using (var provider = new MD5CryptoServiceProvider()) + { + var inputBytes = Encoding.Default.GetBytes(data); + var hashBytes = provider.ComputeHash(inputBytes); + // generate a guid from the hash: + return new Guid(hashBytes); + } + } + } +} \ No newline at end of file diff --git a/src/NServiceBus.AzureFunctions.ServiceBus/Serverless/ServerlessEndpointConfiguration.cs b/src/NServiceBus.AzureFunctions.ServiceBus/Serverless/ServerlessEndpointConfiguration.cs deleted file mode 100644 index 1927949a..00000000 --- a/src/NServiceBus.AzureFunctions.ServiceBus/Serverless/ServerlessEndpointConfiguration.cs +++ /dev/null @@ -1,110 +0,0 @@ -namespace NServiceBus.AzureFunctions.ServiceBus -{ - using System; - using System.Security.Cryptography; - using System.Text; - using System.Threading.Tasks; - using Logging; - using Serialization; - using Transport; - - /// - /// The configuration for an NServiceBus endpoint optimized for serverless environments. - /// - public abstract class ServerlessEndpointConfiguration - { - /// - /// Creates a new configuration. - /// - protected ServerlessEndpointConfiguration(string endpointName) - { - EndpointConfiguration = new EndpointConfiguration(endpointName); - - recoverabilityPolicy.SendFailedMessagesToErrorQueue = true; - EndpointConfiguration.Recoverability().CustomPolicy(recoverabilityPolicy.Invoke); - - // Disable diagnostics by default as it will fail to create the diagnostics file in the default path. - // Can be overriden by ServerlessEndpointConfiguration.LogDiagnostics(). - EndpointConfiguration.CustomDiagnosticsWriter(_ => Task.CompletedTask); - - // 'WEBSITE_SITE_NAME' represents an Azure Function App and the environment variable is set when hosting the function in Azure. - var functionAppName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") ?? Environment.MachineName; - EndpointConfiguration.UniquelyIdentifyRunningInstance() - .UsingCustomDisplayName(functionAppName) - .UsingCustomIdentifier(DeterministicGuid.Create(functionAppName)); - - // Look for license as an environment variable - var licenseText = Environment.GetEnvironmentVariable("NSERVICEBUS_LICENSE"); - if (!string.IsNullOrWhiteSpace(licenseText)) - { - EndpointConfiguration.License(licenseText); - } - } - - internal EndpointConfiguration EndpointConfiguration { get; } - - internal PipelineInvoker PipelineInvoker { get; private set; } - - /// - /// Gives access to the underlying endpoint configuration for advanced configuration options. - /// - public EndpointConfiguration AdvancedConfiguration => EndpointConfiguration; - - /// - /// Define a transport to be used when sending and publishing messages. - /// - protected TransportExtensions UseTransport() - where TTransport : TransportDefinition, new() - { - var serverlessTransport = EndpointConfiguration.UseTransport>(); - - PipelineInvoker = serverlessTransport.PipelineAccess(); - return serverlessTransport.BaseTransportConfiguration(); - } - - /// - /// Define the serializer to be used. - /// - public SerializationExtensions UseSerialization() where T : SerializationDefinition, new() - { - return EndpointConfiguration.UseSerialization(); - } - - /// - /// Disables moving messages to the error queue even if an error queue name is configured. - /// - public void DoNotSendMessagesToErrorQueue() - { - recoverabilityPolicy.SendFailedMessagesToErrorQueue = false; - } - - /// - /// Logs endpoint diagnostics information to the log. Diagnostics are logged on level . - /// - public void LogDiagnostics() - { - EndpointConfiguration.CustomDiagnosticsWriter(diagnostics => - { - LogManager.GetLogger("StartupDiagnostics").Info(diagnostics); - return Task.CompletedTask; - }); - } - - readonly ServerlessRecoverabilityPolicy recoverabilityPolicy = new ServerlessRecoverabilityPolicy(); - } - - static class DeterministicGuid - { - public static Guid Create(string data) - { - // use MD5 hash to get a 16-byte hash of the string - using (var provider = new MD5CryptoServiceProvider()) - { - var inputBytes = Encoding.Default.GetBytes(data); - var hashBytes = provider.ComputeHash(inputBytes); - // generate a guid from the hash: - return new Guid(hashBytes); - } - } - } -} diff --git a/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs b/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs index 4959f3c2..a8bb1caf 100644 --- a/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs +++ b/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs @@ -1,4 +1,8 @@ -namespace NServiceBus +using System.Threading.Tasks; +using NServiceBus.Serialization; +using NServiceBus.Transport; + +namespace NServiceBus { using Logging; using Microsoft.Azure.WebJobs; @@ -8,8 +12,9 @@ /// /// Represents a serverless NServiceBus endpoint running within an AzureServiceBus trigger. /// - public class ServiceBusTriggeredEndpointConfiguration : ServerlessEndpointConfiguration + public class ServiceBusTriggeredEndpointConfiguration { + private readonly ServerlessRecoverabilityPolicy recoverabilityPolicy = new ServerlessRecoverabilityPolicy(); internal const string DefaultServiceBusConnectionName = "AzureWebJobsServiceBus"; /// @@ -17,6 +22,14 @@ public class ServiceBusTriggeredEndpointConfiguration : ServerlessEndpointConfig /// public TransportExtensions Transport { get; } + internal EndpointConfiguration EndpointConfiguration { get; } + internal PipelineInvoker PipelineInvoker { get; private set; } + + /// + /// Gives access to the underlying endpoint configuration for advanced configuration options. + /// + public EndpointConfiguration AdvancedConfiguration => EndpointConfiguration; + static ServiceBusTriggeredEndpointConfiguration() { LogManager.UseFactory(FunctionsLoggerFactory.Instance); @@ -25,8 +38,30 @@ static ServiceBusTriggeredEndpointConfiguration() /// /// Creates a serverless NServiceBus endpoint running within an Azure Service Bus trigger. /// - public ServiceBusTriggeredEndpointConfiguration(string endpointName, string connectionStringName = null) : base(endpointName) + public ServiceBusTriggeredEndpointConfiguration(string endpointName, string connectionStringName = null) { + EndpointConfiguration = new EndpointConfiguration(endpointName); + + recoverabilityPolicy.SendFailedMessagesToErrorQueue = true; + EndpointConfiguration.Recoverability().CustomPolicy(recoverabilityPolicy.Invoke); + + // Disable diagnostics by default as it will fail to create the diagnostics file in the default path. + // Can be overriden by ServerlessEndpointConfiguration.LogDiagnostics(). + EndpointConfiguration.CustomDiagnosticsWriter(_ => Task.CompletedTask); + + // 'WEBSITE_SITE_NAME' represents an Azure Function App and the environment variable is set when hosting the function in Azure. + var functionAppName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME") ?? Environment.MachineName; + EndpointConfiguration.UniquelyIdentifyRunningInstance() + .UsingCustomDisplayName(functionAppName) + .UsingCustomIdentifier(DeterministicGuid.Create(functionAppName)); + + // Look for license as an environment variable + var licenseText = Environment.GetEnvironmentVariable("NSERVICEBUS_LICENSE"); + if (!string.IsNullOrWhiteSpace(licenseText)) + { + EndpointConfiguration.License(licenseText); + } + Transport = UseTransport(); var connectionString = Environment.GetEnvironmentVariable(connectionStringName ?? DefaultServiceBusConnectionName); @@ -52,5 +87,45 @@ public static ServiceBusTriggeredEndpointConfiguration FromAttributes() throw new Exception($"Unable to automatically derive the endpoint name from the ServiceBusTrigger attribute. Make sure the attribute exists or create the {nameof(ServiceBusTriggeredEndpointConfiguration)} with the required parameter manually."); } + + /// + /// Define a transport to be used when sending and publishing messages. + /// + protected TransportExtensions UseTransport() + where TTransport : TransportDefinition, new() + { + var serverlessTransport = EndpointConfiguration.UseTransport>(); + + PipelineInvoker = serverlessTransport.PipelineAccess(); + return serverlessTransport.BaseTransportConfiguration(); + } + + /// + /// Define the serializer to be used. + /// + public SerializationExtensions UseSerialization() where T : SerializationDefinition, new() + { + return EndpointConfiguration.UseSerialization(); + } + + /// + /// Disables moving messages to the error queue even if an error queue name is configured. + /// + public void DoNotSendMessagesToErrorQueue() + { + recoverabilityPolicy.SendFailedMessagesToErrorQueue = false; + } + + /// + /// Logs endpoint diagnostics information to the log. Diagnostics are logged on level . + /// + public void LogDiagnostics() + { + EndpointConfiguration.CustomDiagnosticsWriter(diagnostics => + { + LogManager.GetLogger("StartupDiagnostics").Info(diagnostics); + return Task.CompletedTask; + }); + } } } \ No newline at end of file From 11133b9987d1abeb88e2fb26f584b7040fa43269 Mon Sep 17 00:00:00 2001 From: Tim Bussmann Date: Wed, 11 Nov 2020 11:58:33 +0100 Subject: [PATCH 2/3] reformat --- .../FunctionEndpoint.cs | 14 ++--- ...erviceBusTriggeredEndpointConfiguration.cs | 56 ++++++++++--------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/NServiceBus.AzureFunctions.ServiceBus/FunctionEndpoint.cs b/src/NServiceBus.AzureFunctions.ServiceBus/FunctionEndpoint.cs index 31044a4a..ac1d374c 100644 --- a/src/NServiceBus.AzureFunctions.ServiceBus/FunctionEndpoint.cs +++ b/src/NServiceBus.AzureFunctions.ServiceBus/FunctionEndpoint.cs @@ -1,14 +1,14 @@ namespace NServiceBus { + using System; using System.IO; using System.Reflection; using System.Runtime.Loader; - using Logging; - using System; using System.Threading; using System.Threading.Tasks; using AzureFunctions.ServiceBus; using Extensibility; + using Logging; using Microsoft.Azure.ServiceBus; using Microsoft.Extensions.Logging; using Transport; @@ -20,11 +20,8 @@ /// public class FunctionEndpoint { - private readonly Func> endpointFactory; - private ServiceBusTriggeredEndpointConfiguration configuration; - /// - /// Creates a new instance of that can handle messages using the provided configuration. + /// Creates a new instance of that can handle messages using the provided configuration. /// public FunctionEndpoint(Func configurationFactory) { @@ -42,7 +39,7 @@ internal FunctionEndpoint(IStartableEndpointWithExternallyManagedContainer exter ServiceBusTriggeredEndpointConfiguration configuration, IServiceProvider serviceProvider) { this.configuration = configuration; - this.endpointFactory = _ => externallyManagedContainerEndpoint.Start(serviceProvider); + endpointFactory = _ => externallyManagedContainerEndpoint.Start(serviceProvider); } /// @@ -186,7 +183,10 @@ static bool IsRuntimeAssembly(byte[] publicKeyToken) protected Func AssemblyDirectoryResolver = functionExecutionContext => Path.Combine(functionExecutionContext.ExecutionContext.FunctionAppDirectory, "bin"); + private readonly Func> endpointFactory; + readonly SemaphoreSlim semaphoreLock = new SemaphoreSlim(initialCount: 1, maxCount: 1); + private ServiceBusTriggeredEndpointConfiguration configuration; PipelineInvoker pipeline; } diff --git a/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs b/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs index a8bb1caf..61cac467 100644 --- a/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs +++ b/src/NServiceBus.AzureFunctions.ServiceBus/ServiceBusTriggeredEndpointConfiguration.cs @@ -1,35 +1,18 @@ -using System.Threading.Tasks; -using NServiceBus.Serialization; -using NServiceBus.Transport; - -namespace NServiceBus +namespace NServiceBus { - using Logging; - using Microsoft.Azure.WebJobs; using System; + using System.Threading.Tasks; using AzureFunctions.ServiceBus; + using Logging; + using Microsoft.Azure.WebJobs; + using Serialization; + using Transport; /// /// Represents a serverless NServiceBus endpoint running within an AzureServiceBus trigger. /// public class ServiceBusTriggeredEndpointConfiguration { - private readonly ServerlessRecoverabilityPolicy recoverabilityPolicy = new ServerlessRecoverabilityPolicy(); - internal const string DefaultServiceBusConnectionName = "AzureWebJobsServiceBus"; - - /// - /// Azure Service Bus transport - /// - public TransportExtensions Transport { get; } - - internal EndpointConfiguration EndpointConfiguration { get; } - internal PipelineInvoker PipelineInvoker { get; private set; } - - /// - /// Gives access to the underlying endpoint configuration for advanced configuration options. - /// - public EndpointConfiguration AdvancedConfiguration => EndpointConfiguration; - static ServiceBusTriggeredEndpointConfiguration() { LogManager.UseFactory(FunctionsLoggerFactory.Instance); @@ -64,7 +47,8 @@ public ServiceBusTriggeredEndpointConfiguration(string endpointName, string conn Transport = UseTransport(); - var connectionString = Environment.GetEnvironmentVariable(connectionStringName ?? DefaultServiceBusConnectionName); + var connectionString = + Environment.GetEnvironmentVariable(connectionStringName ?? DefaultServiceBusConnectionName); Transport.ConnectionString(connectionString); var recoverability = AdvancedConfiguration.Recoverability(); @@ -75,7 +59,21 @@ public ServiceBusTriggeredEndpointConfiguration(string endpointName, string conn } /// - /// Attempts to derive the required configuration parameters automatically from the Azure Functions related attributes via reflection. + /// Azure Service Bus transport + /// + public TransportExtensions Transport { get; } + + internal EndpointConfiguration EndpointConfiguration { get; } + internal PipelineInvoker PipelineInvoker { get; private set; } + + /// + /// Gives access to the underlying endpoint configuration for advanced configuration options. + /// + public EndpointConfiguration AdvancedConfiguration => EndpointConfiguration; + + /// + /// Attempts to derive the required configuration parameters automatically from the Azure Functions related attributes via + /// reflection. /// public static ServiceBusTriggeredEndpointConfiguration FromAttributes() { @@ -85,7 +83,8 @@ public static ServiceBusTriggeredEndpointConfiguration FromAttributes() return new ServiceBusTriggeredEndpointConfiguration(configuration.QueueName, configuration.Connection); } - throw new Exception($"Unable to automatically derive the endpoint name from the ServiceBusTrigger attribute. Make sure the attribute exists or create the {nameof(ServiceBusTriggeredEndpointConfiguration)} with the required parameter manually."); + throw new Exception( + $"Unable to automatically derive the endpoint name from the ServiceBusTrigger attribute. Make sure the attribute exists or create the {nameof(ServiceBusTriggeredEndpointConfiguration)} with the required parameter manually."); } /// @@ -117,7 +116,7 @@ public void DoNotSendMessagesToErrorQueue() } /// - /// Logs endpoint diagnostics information to the log. Diagnostics are logged on level . + /// Logs endpoint diagnostics information to the log. Diagnostics are logged on level . /// public void LogDiagnostics() { @@ -127,5 +126,8 @@ public void LogDiagnostics() return Task.CompletedTask; }); } + + private readonly ServerlessRecoverabilityPolicy recoverabilityPolicy = new ServerlessRecoverabilityPolicy(); + internal const string DefaultServiceBusConnectionName = "AzureWebJobsServiceBus"; } } \ No newline at end of file From 89ca03225e3a95b9da057d4e19696ed2b088f1d1 Mon Sep 17 00:00:00 2001 From: Tim Bussmann Date: Wed, 11 Nov 2020 12:13:39 +0100 Subject: [PATCH 3/3] approve public API changes --- .../APIApprovals.Approve.approved.txt | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt b/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt index ce30b469..b1970ca5 100644 --- a/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt +++ b/src/ServiceBus.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt @@ -1,18 +1,4 @@ [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute(@"ServiceBus.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007f16e21368ff041183fab592d9e8ed37e7be355e93323147a1d29983d6e591b04282e4da0c9e18bd901e112c0033925eb7d7872c2f1706655891c5c9d57297994f707d16ee9a8f40d978f064ee1ffc73c0db3f4712691b23bf596f75130f4ec978cf78757ec034625a5f27e6bb50c618931ea49f6f628fd74271c32959efb1c5")] -namespace NServiceBus.AzureFunctions.ServiceBus -{ - public abstract class ServerlessEndpointConfiguration - { - protected ServerlessEndpointConfiguration(string endpointName) { } - public NServiceBus.EndpointConfiguration AdvancedConfiguration { get; } - public void DoNotSendMessagesToErrorQueue() { } - public void LogDiagnostics() { } - public NServiceBus.Serialization.SerializationExtensions UseSerialization() - where T : NServiceBus.Serialization.SerializationDefinition, new () { } - protected NServiceBus.TransportExtensions UseTransport() - where TTransport : NServiceBus.Transport.TransportDefinition, new () { } - } -} namespace NServiceBus { public class FunctionEndpoint @@ -31,10 +17,17 @@ namespace NServiceBus { public static void UseNServiceBus(this Microsoft.Azure.Functions.Extensions.DependencyInjection.IFunctionsHostBuilder functionsHostBuilder, System.Func configurationFactory) { } } - public class ServiceBusTriggeredEndpointConfiguration : NServiceBus.AzureFunctions.ServiceBus.ServerlessEndpointConfiguration + public class ServiceBusTriggeredEndpointConfiguration { public ServiceBusTriggeredEndpointConfiguration(string endpointName, string connectionStringName = null) { } + public NServiceBus.EndpointConfiguration AdvancedConfiguration { get; } public NServiceBus.TransportExtensions Transport { get; } + public void DoNotSendMessagesToErrorQueue() { } public static NServiceBus.ServiceBusTriggeredEndpointConfiguration FromAttributes() { } + public void LogDiagnostics() { } + public NServiceBus.Serialization.SerializationExtensions UseSerialization() + where T : NServiceBus.Serialization.SerializationDefinition, new () { } + protected NServiceBus.TransportExtensions UseTransport() + where TTransport : NServiceBus.Transport.TransportDefinition, new () { } } } \ No newline at end of file