Updated background tasks to use common service logic

This commit is contained in:
David Fowler 2023-05-05 00:12:34 -07:00 committed by Reuben Bond
parent 0cb6b08300
commit c41cd3830c
14 changed files with 76 additions and 187 deletions

View File

@ -5,7 +5,7 @@ public static class CustomExtensionMethods
public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddHealthChecks() services.AddHealthChecks()
.AddRedis(_ => configuration.GetConnectionString("redis"), "redis", tags: new[] { "ready", "liveness" }); .AddRedis(_ => configuration.GetRequiredConnectionString("redis"), "redis", tags: new[] { "ready", "liveness" });
return services; return services;
} }
@ -14,7 +14,7 @@ public static class CustomExtensionMethods
{ {
return services.AddSingleton(sp => return services.AddSingleton(sp =>
{ {
var redisConfig = ConfigurationOptions.Parse(configuration.GetConnectionString("redis"), true); var redisConfig = ConfigurationOptions.Parse(configuration.GetRequiredConnectionString("redis"), true);
return ConnectionMultiplexer.Connect(redisConfig); return ConnectionMultiplexer.Connect(redisConfig);
}); });

View File

@ -7,7 +7,7 @@ public static class CustomExtensionMethods
var hcBuilder = services.AddHealthChecks(); var hcBuilder = services.AddHealthChecks();
hcBuilder hcBuilder
.AddSqlServer(_ => configuration.GetConnectionString("CatalogDB"), .AddSqlServer(_ => configuration.GetRequiredConnectionString("CatalogDB"),
name: "CatalogDB-check", name: "CatalogDB-check",
tags: new string[] { "live", "ready" }); tags: new string[] { "live", "ready" });
@ -89,7 +89,4 @@ public static class CustomExtensionMethods
return services; return services;
} }
private static string GetRequiredConnectionString(this IConfiguration configuration, string name) =>
configuration.GetConnectionString(name) ?? throw new InvalidOperationException($"Configuration missing value for: {(configuration is IConfigurationSection s ? s.Path + ":ConnectionStrings:" + name : "ConnectionStrings:" + name)}");
} }

View File

@ -73,7 +73,4 @@ static class CustomExtensionsMethods
return services; return services;
} }
private static string GetRequiredConnectionString(this IConfiguration configuration, string name) =>
configuration.GetConnectionString(name) ?? throw new InvalidOperationException($"Configuration missing value for: {(configuration is IConfigurationSection s ? s.Path + ":ConnectionStrings:" + name : "ConnectionStrings:" + name)}");
} }

View File

@ -52,6 +52,7 @@ global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options; global using Microsoft.Extensions.Options;
global using Polly; global using Polly;
global using Polly.Retry; global using Polly.Retry;
global using Services.Common;
global using Swashbuckle.AspNetCore.SwaggerGen; global using Swashbuckle.AspNetCore.SwaggerGen;
global using AppCommand = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; global using AppCommand = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
global using ApiModels = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models; global using ApiModels = Microsoft.eShopOnContainers.Services.Ordering.API.Application.Models;

View File

@ -1,6 +1,4 @@
using Services.Common; var builder = WebApplication.CreateBuilder(args);
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults(); builder.AddServiceDefaults();

View File

@ -1,119 +1,25 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; namespace Ordering.BackgroundTasks.Extensions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusServiceBus;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using RabbitMQ.Client;
namespace Ordering.BackgroundTasks.Extensions public static class CustomExtensionMethods
{ {
public static class CustomExtensionMethods public static IServiceCollection AddHealthChecks(this IServiceCollection services, IConfiguration configuration)
{ {
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services, IConfiguration configuration) var hcBuilder = services.AddHealthChecks();
hcBuilder.AddSqlServer(_ =>
configuration.GetRequiredConnectionString("OrderingDb"),
name: "OrderingTaskDB-check",
tags: new string[] { "live", "ready" });
return services;
}
public static IServiceCollection AddApplicationOptions(this IServiceCollection services, IConfiguration configuration)
{
return services.Configure<BackgroundTaskSettings>(configuration)
.Configure<BackgroundTaskSettings>(o =>
{ {
var hcBuilder = services.AddHealthChecks(); o.ConnectionString = configuration.GetRequiredConnectionString("OrderingDb");
});
hcBuilder.AddCheck("self", () => HealthCheckResult.Healthy());
hcBuilder.AddSqlServer(
configuration["ConnectionString"],
name: "OrderingTaskDB-check",
tags: new string[] { "orderingtaskdb" });
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
hcBuilder.AddAzureServiceBusTopic(
configuration["EventBusConnection"],
topicName: "eshop_event_bus",
name: "orderingtask-servicebus-check",
tags: new string[] { "servicebus" });
}
else
{
hcBuilder.AddRabbitMQ(
$"amqp://{configuration["EventBusConnection"]}",
name: "orderingtask-rabbitmqbus-check",
tags: new string[] { "rabbitmqbus" });
}
return services;
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
var subscriptionClientName = configuration["SubscriptionClientName"];
if (configuration.GetValue<bool>("AzureServiceBusEnabled"))
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var serviceBusConnectionString = configuration["EventBusConnection"];
return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
});
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger, eventBusSubcriptionsManager, sp, subscriptionName);
});
}
else
{
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, sp, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
});
}
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
return services;
}
} }
} }

View File

@ -1,13 +1,10 @@
global using Microsoft.AspNetCore.Hosting; global using System;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Hosting;
global using Ordering.BackgroundTasks.Extensions;
global using System.IO;
global using HealthChecks.UI.Client;
global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Diagnostics.HealthChecks; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using Ordering.BackgroundTasks.Services;
global using System;
global using Ordering.BackgroundTasks; global using Ordering.BackgroundTasks;
global using Ordering.BackgroundTasks.Extensions;
global using Ordering.BackgroundTasks.Services;
global using Services.Common;

View File

@ -23,5 +23,6 @@
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusServiceBus\EventBusServiceBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\Services.Common\Services.Common.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,32 +1,18 @@
var builder = WebApplication.CreateBuilder(new WebApplicationOptions var builder = WebApplication.CreateBuilder(args);
{
Args = args, builder.AddServiceDefaults();
ApplicationName = typeof(Program).Assembly.FullName
}); builder.Services.AddHealthChecks(builder.Configuration);
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()); builder.Services.AddApplicationOptions(builder.Configuration);
builder.Configuration.AddJsonFile("appsettings.json", optional: true); builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Configuration.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true);
builder.Configuration.AddEnvironmentVariables();
builder.Services.AddCustomHealthCheck(builder.Configuration)
.Configure<BackgroundTaskSettings>(builder.Configuration)
.AddHostedService<GracePeriodManagerService>()
.AddEventBus(builder.Configuration);
var app = builder.Build(); var app = builder.Build();
if (!app.Environment.IsDevelopment())
if (!await app.CheckHealthAsync())
{ {
app.UseExceptionHandler("/Home/Error"); return;
} }
app.UseRouting(); app.UseServiceDefaults();
app.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/liveness", new HealthCheckOptions
{
Predicate = r => r.Name.Contains("self")
});
await app.RunAsync(); await app.RunAsync();

View File

@ -1,14 +1,17 @@
{ {
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", "Logging": {
"SubscriptionClientName": "BackgroundTasks", "LogLevel": {
"GracePeriodTime": "1", "Default": "Information",
"CheckUpdateTime": "1000", "Microsoft.AspNetCore": "Warning"
"ApplicationInsights": { }
"InstrumentationKey": ""
}, },
"AzureServiceBusEnabled": false, "ConnectionStrings": {
"EventBusRetryCount": 5, "EventBus": "localhost"
"EventBusConnection": "", },
"EventBusUserName": "", "EventBus": {
"EventBusPassword": "" "SubscriptionClientName": "BackgroundTasks",
"RetryCount": 5
},
"GracePeriodTime": "1",
"CheckUpdateTime": "1000"
} }

View File

@ -1,16 +1,17 @@
global using Microsoft.AspNetCore.Authentication.JwtBearer; global using System;
global using System.Collections.Generic;
global using System.Threading.Tasks;
global using Microsoft.AspNetCore.Authentication.JwtBearer;
global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.SignalR; global using Microsoft.AspNetCore.SignalR;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; global using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events; global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents; global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.Events;
global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Logging;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub.IntegrationEvents.EventHandling; global using Services.Common;
global using Microsoft.eShopOnContainers.Services.Ordering.SignalrHub;
global using System.Collections.Generic;
global using System.Threading.Tasks;
global using System;

View File

@ -1,6 +1,4 @@
using Services.Common; var builder = WebApplication.CreateBuilder(args);
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults(); builder.AddServiceDefaults();

View File

@ -419,10 +419,4 @@ public static class CommonExtensions
Predicate = r => r.Name.Contains("self") Predicate = r => r.Name.Contains("self")
}); });
} }
private static string GetRequiredValue(this IConfiguration configuration, string name) =>
configuration[name] ?? throw new InvalidOperationException($"Configuration missing value for: {(configuration is IConfigurationSection s ? s.Path + ":" + name : name)}");
private static string GetRequiredConnectionString(this IConfiguration configuration, string name) =>
configuration.GetConnectionString(name) ?? throw new InvalidOperationException($"Configuration missing value for: {(configuration is IConfigurationSection s ? s.Path + ":ConnectionStrings:" + name : "ConnectionStrings:" + name)}");
} }

View File

@ -0,0 +1,10 @@
namespace Microsoft.Extensions.Configuration;
public static class ConfigurationExtensions
{
public static string GetRequiredValue(this IConfiguration configuration, string name) =>
configuration[name] ?? throw new InvalidOperationException($"Configuration missing value for: {(configuration is IConfigurationSection s ? s.Path + ":" + name : name)}");
public static string GetRequiredConnectionString(this IConfiguration configuration, string name) =>
configuration.GetConnectionString(name) ?? throw new InvalidOperationException($"Configuration missing value for: {(configuration is IConfigurationSection s ? s.Path + ":ConnectionStrings:" + name : "ConnectionStrings:" + name)}");
}