Net çekirdek yapılandırmasını Azure Uygulama Yapılandırması'ndan dinamik olarak güncelleyin


9

Ne yapmaya çalışıyorum: Azure Uygulama Yapılandırması'nda bir sentinel anahtarı olan bir .net çekirdeği 2.1 mvc web uygulaması ile Azure Uygulama Yapılandırması'nı, Azure'daki anahtarları ve anahtarların hiçbirini değiştirememek amacıyla kurmaya çalışıyorum sentinel değeri değişene kadar uygulamalarımda güncellenecek. Teorik olarak, bu, hot swap yapılandırmalarını güvenli bir şekilde kullanmama izin vermelidir.

Benim sorunum: Bunu yaptığımda, IWebHostBuilder üzerinde sentinel izlemek için kullanılabilir bir WatchAndReloadAll () yöntemi yoktur ve alternatif Refresh () yöntemleri, durum olarak yapılandırmayı yenilemek gibi görünmüyor.

Arka Plan Bilgileri ve ne denedim: Geçen hafta VS Live - San Diego'ya katıldım ve Azure Uygulama Yapılandırması ile ilgili bir demo izledim. Ben implimenting zaman yapılandırma değerlerini yenilemek için uygulamayı almak için çalışırken bazı sorunlar vardı, bu yüzden de nasıl bunu açıklayan bu demo referans . İlgili bölüm yaklaşık 10 dakika uzaklıktadır. Ancak, bu yöntem IWebHostBuilder'da mevcut görünmemektedir.

Belgeleme atıfta bulunuyorum: Resmi belgelerde bu yönteme ilişkin bir referans yoktur bkz. Doc quickstart .net core ve doc dynamic configuration .net core

Benim Çevre: Net çekirdek 2.1 nokta Kullanılması 2.0.0-önizleme 010060003-1250 Microsoft.Azure.AppConfiguration.AspNetCore için en son önizleme Nuget paketi ile, Visual Studio Kurumsal 2019 çalıştırılıyor

Kodum : Demoda, CreateWebHostBuilder (string [] args) yöntemi ile bir IWebHostBuilder oluşturdular:

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
    return WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();
        config.AddAzureAppConfiguration(options =>
        {
            options.Connect(settings["ConnectionStrings:AzureConfiguration"])
            .Use(keyFilter: "TestApp:*")
            .WatchAndReloadAll(key: "TestApp:Sentinel", pollInterval: TimeSpan.FromSeconds(5));
        }); 
    })
    .UseStartup<Startup>();
}

Mevcut belgeleri kullanarak da bu şekilde denedim:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        var settings = config.Build();

        config.AddAzureAppConfiguration(options =>
        {
            // fetch connection string from local config. Could use KeyVault, or Secrets as well.
            options.Connect(settings["ConnectionStrings:AzureConfiguration"])
            // filter configs so we are only searching against configs that meet this pattern
            .Use(keyFilter: "WebApp:*")
            .ConfigureRefresh(refreshOptions =>
            { 
                // In theory, when this value changes, on the next refresh operation, the config will update all modified configs since it was last refreshed.
                refreshOptions.Register("WebApp:Sentinel", true);
                refreshOptions.Register("WebApp:Settings:BackgroundColor", false);
                refreshOptions.Register("WebApp:Settings:FontColor", false);
                refreshOptions.Register("WebApp:Settings:FontSize", false);
                refreshOptions.Register("WebApp:Settings:Message", false);
            });
        });
    })
    .UseStartup<Startup>();

Sonra, başlangıç ​​sınıfımda:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<Settings>(Configuration.GetSection("WebApp:Settings"));
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseAzureAppConfiguration();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

ve son olarak ayarlarım yapılandırma modelim:

public class Settings
{
    public string BackgroundColor { get; set; }
    public long FontSize { get; set; }
    public string FontColor { get; set; }
    public string Message { get; set; }
}

Şimdi, denetleyicimde, bu ayarları alıp görünümde görüntülenecek bir görünüm çantasına atıyorum.

public class HomeController : Controller
{
    private readonly Settings _Settings;

    public HomeController(IOptionsSnapshot<Settings> settings)
    {
        _Settings = settings.Value;
    }

    public IActionResult Index()
    {
        ViewData["BackgroundColor"] = _Settings.BackgroundColor;
        ViewData["FontSize"] = _Settings.FontSize;
        ViewData["FontColor"] = _Settings.FontColor;
        ViewData["Message"] = _Settings.Message;

        return View();
    }
}

Değişiklikleri görüntülemek için basit bir görünüm:

<!DOCTYPE html>
<html lang="en">
<style>
    body {
        background-color: @ViewData["BackgroundColor"]
    }
    h1 {
        color: @ViewData["FontColor"];
        font-size: @ViewData["FontSize"];
    }
</style>
<head>
    <title>Index View</title>
</head>
<body>
    <h1>@ViewData["Message"]</h1>
</body>
</html>

Yapılandırmayı ilk kez aşağı çekmeyi başarabilirim, ancak yenileme işlevselliği hiçbir şekilde çalışmıyor gibi görünüyor.

Son örnekte, sentinel herhangi bir yeni değere ayarlandığında yapılandırmaların güncellenmesini veya en azından değiştirildikten 30 saniye sonra bir değeri güncellemesini bekledim. Bekleme süresi değerleri güncellemez ve yalnızca uygulamanın tamamen kapatılması ve yeniden başlatılması yeni yapılandırmayı yükler.

Güncelleme: App eklemeAzureAppConfiguration (); Başlangıçta yapılandırma yönteminde ve yapılandırma için önbellekte açık bir zaman aşımı ayarlandığında, yenileme yönteminin belirli bir süre sonra yenilenmesi düzeltildi, ancak sentinel işlevselliği hala çalışmıyor ve yenileme yöntemindeki updateAll bayrağı çalışmıyor.


Yapılandırmaya nasıl ve nerede eriştiğinizi gösterebilir misiniz? Durumumu kendi projelerimden birinde taklit ettim ve mükemmel çalışıyor
Peter Bons

ConfigureServicesStartuop.cs'taki yönteminizin bir yerinde bazı yapılandırma bağları bekledim, örneğin services.Configure<LogSettings>(configuration.GetSection("LogSettings"));
Peter Bons

@peterBons bağlantınız beni 404'e götürüyor.
Nick Gasia Robitsch

@PeterBons Yazımı, yapılandırma enjeksiyon / bağlama ile ilgili istenen bilgileri içerecek şekilde güncelledim. O zaman bunun uygun olduğunu düşünmedim çünkü bu işe yaradı.
Nick Gasia Robitsch

1
Öyleydi. Rica ederim.
Peter Bons

Yanıtlar:


6

Tamam, çok fazla test ve deneme yanılma işleminden sonra çalışıyorum.

Benim sorun yapılandırma yöntemi azure için eksik bir hizmet oldu. Burada bazı ilginç davranışlar var, çünkü ayarları hala aşağı çekecek, bu eksikse güncelleme yapmayacak. Bu bir kez yerleştirildikten ve dokümantasyon başına yapılandırılmış uygun bir sentinel ile updateAll bayrağıyla çalışır. Ancak bu henüz belgelenmemiştir.

İşte çözüm:

Program.cs dosyasında:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration.AzureAppConfiguration;

namespace ASPNetCoreApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }   // Main

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                var settings = config.Build();

                config.AddAzureAppConfiguration(options =>
                {
                    // fetch connection string from local config. Could use KeyVault, or Secrets as well.
                    options.Connect(settings["ConnectionStrings:AzureConfiguration"])
                    // filter configs so we are only searching against configs that meet this pattern
                    .Use(keyFilter: "WebApp:*")
                    .ConfigureRefresh(refreshOptions =>
                    { 
                        // When this value changes, on the next refresh operation, the config will update all modified configs since it was last refreshed.
                        refreshOptions.Register("WebApp:Sentinel", true);
                        // Set a timeout for the cache so that it will poll the azure config every X timespan.
                        refreshOptions.SetCacheExpiration(cacheExpirationTime: new System.TimeSpan(0, 0, 0, 15, 0));
                    });
                });
            })
            .UseStartup<Startup>();
    }
}

Sonra Startup.cs dosyasında:

using ASPNetCoreApp.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace ASPNetCoreApp
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // bind the config to our DI container for the settings we are pulling down from azure.
            services.Configure<Settings>(Configuration.GetSection("WebApp:Settings"));
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }
            // Set the Azure middleware to handle configuration
            // It will pull the config down without this, but will not refresh.
            app.UseAzureAppConfiguration();
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Azure geri alınan verilerimi şuraya bağlıyorum: Ayarlar modeli:

namespace ASPNetCoreApp.Models
{
    public class Settings
    {
        public string BackgroundColor { get; set; }
        public long FontSize { get; set; }
        public string FontColor { get; set; }
        public string Message { get; set; }
    }
}

Bizim görüşümüze geçmek için yapılandırmanın ViewBag'e ayarlandığı genel bir ev denetleyicisi:

using ASPNetCoreApp.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Diagnostics;

namespace ASPNetCoreApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly Settings _Settings;

        public HomeController(IOptionsSnapshot<Settings> settings)
        {
            _Settings = settings.Value;
        }
        public IActionResult Index()
        {
            ViewData["BackgroundColor"] = _Settings.BackgroundColor;
            ViewData["FontSize"] = _Settings.FontSize;
            ViewData["FontColor"] = _Settings.FontColor;
            ViewData["Message"] = _Settings.Message;

            return View();
        }

        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";

            return View();
        }

        public IActionResult Contact()
        {
            ViewData["Message"] = "Your contact page.";

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

Bizim görüşümüz:

<!DOCTYPE html>
<html lang="en">
<style>
    body {
        background-color: @ViewData["BackgroundColor"]
    }
    h1 {
        color: @ViewData["FontColor"];
        font-size: @ViewData["FontSize"];
    }
</style>
<head>
    <title>Index View</title>
</head>
<body>
    <h1>@ViewData["Message"]</h1>
</body>
</html>

Umarım bu başka birine yardımcı olur!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.