Динамическое обновление конфигурации ASP.NET по запросу с помощью Azure App Configuration.

Что я пытаюсь сделать: я пытаюсь настроить обновление конфигурации ASP.Net по требованию, используя конфигурацию приложений Azure в качестве источника и подписку EventGrid на событие изменения ключа-значения с конечной точкой WebHook.

В чем моя проблема: когда событие достигает конечной точки, код выполняется без ошибок, но конфигурация в конце концов не обновляется.

Справочная информация и то, что я пробовал: я также пытался использовать метод опроса с дозорным ключом, который работает хорошо, но не кажется оптимальным решением, учитывая ограничение квоты конфигурации приложений Azure или необходимость ждать, когда истечет срок кэширования. .

Мой код: здесь я использую минимальный синтаксис API

      using System.Text.Json;
using AppConfigurationSpike;
using Azure.Identity;
using Azure.Messaging.EventGrid;
using Azure.Messaging.EventGrid.SystemEvents;
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Extensions;
using Microsoft.Extensions.Options;

var builder = WebApplication
    .CreateBuilder(args);

// Load configuration from Azure App Configuration
builder.Host.ConfigureAppConfiguration((context, config) =>
{
    var settings = config.Build();
    config.AddAzureAppConfiguration(options =>
    {
        options.Connect(new Uri(settings["MyApp:AppConfigurationEndpoint"]), new DefaultAzureCredential())
            // Load all keys that start with `MyApp:` and have no label
            .Select("MyApp:*")
            // Configure to reload configuration if the registered sentinel key is modified
            .ConfigureRefresh(refreshOptions =>
                refreshOptions.Register("MyApp:Settings:Sentinel", refreshAll: true)
                    .SetCacheExpiration(TimeSpan.FromDays(30)));
    });
});

// Add Azure App Configuration middleware to the container of services.
builder.Services.AddAzureAppConfiguration();
builder.Services.Configure<Settings>(builder.Configuration.GetSection("MyApp:Settings"));

var app = builder.Build();

// Use Azure App Configuration middleware for dynamic configuration refresh.
app.UseAzureAppConfiguration();

// I use this endpoint for checking whether the configuration is updated or not.
app.MapGet("/", (IOptionsSnapshot<Settings> settings) => settings.Value.Key);

// A webhook for immediate configuration update.
app.MapPost("/api/update_config", async (HttpContext context, IConfigurationRefresherProvider refresherProvider, ILogger<Program> logger) =>
{
    logger.LogInformation($"Entered update_config...");
    var refresher = refresherProvider.Refreshers.First();
    var data = await BinaryData.FromStreamAsync(context.Request.Body);
    var eventGridEvent = EventGridEvent.Parse(data);
    // Handle system events
    if (eventGridEvent.TryGetSystemEventData(out object eventData))
    {
        // Handle the subscription validation event. This is needed to register this webhook during event subsscription creation.
        if (eventData is SubscriptionValidationEventData subscriptionValidationEventData)
        {
            var responseData = new SubscriptionValidationResponse()
            {
                ValidationResponse = subscriptionValidationEventData.ValidationCode
            };
            await context.Response.WriteAsync(JsonSerializer.Serialize(responseData));
        }

        if (eventData is AppConfigurationKeyValueModifiedEventData)
        {
            logger.LogInformation($"Updating config data...");
            eventGridEvent.TryCreatePushNotification(out PushNotification pushNotification);
            // Invalidate cached config
            refresher.ProcessPushNotification(pushNotification);
            
            // Also tried this, but it doesn't update the config
            // refresher.SetDirty(TimeSpan.FromSeconds(1));
            // await Task.Delay(TimeSpan.FromSeconds(1));
            
            var result = await refresher.TryRefreshAsync();
            if (result)
            {
                logger.LogInformation("Config has been updated");
            }
        }
    }
});

app.Run();

А это класс настроек:

      public class Settings
{
    public string Key { get; set; } = null!;
    public string Sentinel { get; set; } = null!;
}

Когда я меняю ключ в конфигурации приложения Azure, я вижу, что обработчик событий выполнен, и я вижу следующие сообщения в журналах приложений:

      Entered update_config...
Trying to update config settings...
Result is True

Но когда я использую конечную точку GET, я вижу предыдущее значение.

Обновление 1. Я обнаружил, что если я добавлю параметры «Ключ» в метод refreshOptions.Register, то кеш конфигурации станет недействительным, и я получил новое значение ключа из Azure, поэтому я внес это изменение в раздел ConfigureRefresh:

      .ConfigureRefresh(refreshOptions =>
   refreshOptions.Register("MyApp:Settings:Key", refreshAll: true)
   .SetCacheExpiration(TimeSpan.FromDays(30)));

Но мне кажется не очевидным, зачем мне прописывать каждый конфигурационный ключ в RefreshOptions?

Обновление 2: я только что попытался полностью удалить обработчик и проверить, что будет, если я изменю значение ключа в конфигурации приложения. При опросе моей конечной точки GET я заметил, что значение изменилось через 20-30 минут, однако срок действия был установлен на 30 дней. Я проверяю, что за это время служба приложений не была перезапущена. Означает ли это, что этот срок действия ограничен внутренне?

Последующие вопросы:

  • Можно ли указать, что мне нужно следить за всеми ключами в конфигурации?
  • Могу ли я полностью отключить опрос конфигурации приложения и кэширование конфигурации, если у меня есть веб-перехватчик для обновления ключей и значений? В настоящее время я вижу, что это не сработает, если я удалю разделы ConfigureRefresh.
  • Почему мне нужно явно аннулировать кеш конфигурации перед вызовом await Refresh.TryRefreshAsync();? Я ожидал, что эта недействительность должна быть внутри метода TryRefreshAsync.

1 ответ

Модель push-уведомлений в конфигурации приложений предназначена для уведомления об изменениях конфигурации. Он НЕ предназначен для доставки фактической конфигурации. Только приложение знает, какой набор конфигураций загрузить и при каких условиях конфигурация должна быть перезагружена. Поэтому само по себе уведомление об изменении не должно инициировать обновление конфигурации. Это обеспечивает согласованность конфигурации с приложением.

Идея состоит в том, что сначала вы сообщаете своему приложению, какую конфигурацию загружать и какую конфигурацию отслеживать для перезагрузки. Это та же установка, что и для модели опроса. Разница лишь в том, что вы устанавливаете гораздо больший интервал опроса. Затем, когда приходит уведомление об изменении, ваше приложение сбрасывает время ожидания мониторинга и немедленно отправляет запрос в Конфигурацию приложения для ключа, который он настроен для мониторинга. Если ключ изменен, он перезагрузит конфигурацию; в противном случае он ничего не сделает.

Надеюсь, вышеизложенное объясняет, почему вы видите то, что видите. В вашем случае, если ваше приложение настроено на перезагрузку конфигурации только в случае изменения ключа дозорного, вы должны внести изменения в ключ дозорного для перезагрузки конфигурации. Когда вы вносите изменения в другие ключи, хотя они и запускают push-уведомления для вашего приложения, с точки зрения вашего приложения, оно не готово к перезагрузке конфигурации, поскольку ключ-сигнализатор не изменен. Таким образом, push-уведомления других клавиш будут эффективно игнорироваться.

Надеюсь, это поможет. Я настоятельно рекомендую вам прочитать обсуждение в разделе документа Регистрация обработчика событий для перезагрузки данных из конфигурации приложения , чтобы узнать некоторые подробности.

Другие вопросы по тегам