Wykrywanie zmian za pomocą tokenów zmian w programie ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Token zmiany jest blokiem konstrukcyjnym ogólnego przeznaczenia niskiego poziomu używanym do śledzenia zmian stanu.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
IChangeToken, interfejs
IChangeToken propaguje powiadomienia o wystąpieniu zmiany. IChangeToken
znajduje się w Microsoft.Extensions.Primitives przestrzeni nazw. Pakiet NuGet Microsoft.Extensions.Primitives jest niejawnie udostępniany aplikacjom ASP.NET Core.
IChangeToken
ma dwie właściwości:
- ActiveChangeCallbacks wskaż, czy token aktywnie wywołuje wywołania zwrotne. Jeśli
ActiveChangedCallbacks
ustawiono wartośćfalse
, wywołanie zwrotne nigdy nie jest wywoływane, a aplikacja musi sondowaćHasChanged
pod kątem zmian. Istnieje również możliwość, że token nigdy nie zostanie anulowany, jeśli nie wystąpią żadne zmiany lub podstawowy odbiornik zmian zostanie usunięty lub wyłączony. - HasChanged otrzymuje wartość wskazującą, czy nastąpiła zmiana.
Interfejs IChangeToken
zawiera metodę RegisterChangeCallback(Action<Object>, Object), która rejestruje wywołanie zwrotne wywoływane po zmianie tokenu. HasChanged
należy ustawić przed wywołaniem wywołania zwrotnego.
ChangeToken, klasa
ChangeToken jest klasą statyczną używaną do propagowania powiadomień o wystąpieniu zmiany. ChangeToken
znajduje się w Microsoft.Extensions.Primitives przestrzeni nazw. Pakiet NuGet Microsoft.Extensions.Primitives jest niejawnie udostępniany aplikacjom ASP.NET Core.
Metoda ChangeToken.OnChange(Func<IChangeToken>, Action) rejestruje metodę Action
do wywołania przy każdym zmianie tokenu:
Func<IChangeToken>
tworzy token.Action
jest wywoływany po zmianie tokenu.
Przeciążenie ChangeToken.OnChange TState>(Func<IChangeToken<>, Action<TState, TState>) przyjmuje dodatkowy TState
parametr przekazany do użytkownika Action
tokenu .
OnChange
zwraca wartość IDisposable. Wywołanie Dispose uniemożliwia nasłuchiwanie tokenu w celu uzyskania dalszych zmian i zwalnia zasoby tokenu.
Przykładowe zastosowania tokenów zmian w programie ASP.NET Core
Tokeny zmian są używane w widocznych obszarach ASP.NET Core do monitorowania zmian w obiektach:
- W celu monitorowania zmian w plikach IFileProvidermetoda tworzy Watch
IChangeToken
element dla określonych plików lub folderu do obejrzenia. IChangeToken
tokeny można dodawać do wpisów pamięci podręcznej w celu wyzwalania eksmisji pamięci podręcznej po zmianie.- W przypadku
TOptions
zmian domyślna OptionsMonitor<TOptions> implementacja IOptionsMonitor<TOptions> ma przeciążenie, które akceptuje co najmniej jedno IOptionsChangeTokenSource<TOptions> wystąpienie. Każde wystąpienie zwraca element ,IChangeToken
aby zarejestrować wywołanie zwrotne powiadomienia o zmianie w celu śledzenia zmian opcji.
Monitorowanie zmian konfiguracji
Domyślnie szablony ASP.NET Core używają plików konfiguracji JSON (appsettings.json
, appsettings.Development.json
i appsettings.Production.json
) do ładowania ustawień konfiguracji aplikacji.
Te pliki są konfigurowane przy użyciu metody rozszerzenia AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean), która ConfigurationBuilder akceptuje reloadOnChange
parametr. reloadOnChange
wskazuje, czy konfiguracja powinna zostać ponownie załadowana na zmiany w pliku. To ustawienie jest wyświetlane w metodzie Host CreateDefaultBuilderwygody :
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
Konfiguracja oparta na plikach jest reprezentowana przez FileConfigurationSource. FileConfigurationSource
program używa IFileProvider do monitorowania plików.
Domyślnie element IFileMonitor
jest dostarczany przez PhysicalFileProviderelement , który służy FileSystemWatcher do monitorowania zmian plików konfiguracji.
Przykładowa aplikacja demonstruje dwie implementacje monitorowania zmian konfiguracji. Jeśli którykolwiek z appsettings
plików ulegnie zmianie, obie implementacje monitorowania plików wykonują kod niestandardowy — przykładowa aplikacja zapisuje komunikat w konsoli.
Plik FileSystemWatcher
konfiguracji może wyzwalać wiele wywołań zwrotnych tokenów dla jednej zmiany pliku konfiguracji. Aby upewnić się, że kod niestandardowy jest uruchamiany tylko raz po wyzwoleniu wielu wywołań zwrotnych tokenów, implementacja przykładu sprawdza skróty plików. W przykładzie użyto skrótu pliku SHA1. Ponawianie jest implementowane przy użyciu wycofywania wykładniczego.
Utilities/Utilities.cs
:
public static byte[] ComputeHash(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fs = File.OpenRead(filePath))
{
return System.Security.Cryptography.SHA1
.Create().ComputeHash(fs);
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3)
{
throw;
}
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return new byte[20];
}
Prosty token zmiany uruchamiania
Zarejestruj wywołanie zwrotne użytkownika Action
tokenu, aby otrzymywać powiadomienia o zmianie w tokenie ponownego ładowania konfiguracji.
W pliku Startup.Configure
:
ChangeToken.OnChange(
() => config.GetReloadToken(),
(state) => InvokeChanged(state),
env);
config.GetReloadToken()
udostępnia token. Wywołanie zwrotne jest InvokeChanged
metodą:
private void InvokeChanged(IWebHostEnvironment env)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (Simple Startup Change Token)");
}
}
Wywołanie state
zwrotne służy do przekazywania IWebHostEnvironment
elementu , który jest przydatny do określenia poprawnego appsettings
pliku konfiguracji do monitorowania (na przykład appsettings.Development.json
w środowisku deweloperskiego). Skróty plików są używane do zapobiegania wielokrotnemu uruchamianiu WriteConsole
instrukcji z powodu wielu wywołań zwrotnych tokenów, gdy plik konfiguracji zmienił się tylko raz.
Ten system działa tak długo, jak aplikacja jest uruchomiona i nie może być wyłączona przez użytkownika.
Monitorowanie zmian konfiguracji jako usługi
Przykład implementuje:
- Podstawowe monitorowanie tokenu uruchamiania.
- Monitorowanie jako usługa.
- Mechanizm włączania i wyłączania monitorowania.
Przykład ustanawia IConfigurationMonitor
interfejs.
Extensions/ConfigurationMonitor.cs
:
public interface IConfigurationMonitor
{
bool MonitoringEnabled { get; set; }
string CurrentState { get; set; }
}
Konstruktor zaimplementowanej klasy , ConfigurationMonitor
rejestruje wywołanie zwrotne dla powiadomień o zmianie:
public ConfigurationMonitor(IConfiguration config, IWebHostEnvironment env)
{
_env = env;
ChangeToken.OnChange<IConfigurationMonitor>(
() => config.GetReloadToken(),
InvokeChanged,
this);
}
public bool MonitoringEnabled { get; set; } = false;
public string CurrentState { get; set; } = "Not monitoring";
config.GetReloadToken()
dostarcza token. InvokeChanged
to metoda wywołania zwrotnego. W state
tym wystąpieniu jest odwołaniem do IConfigurationMonitor
wystąpienia używanego do uzyskiwania dostępu do stanu monitorowania. Używane są dwie właściwości:
MonitoringEnabled
: wskazuje, czy wywołanie zwrotne powinno uruchamiać kod niestandardowy.CurrentState
: opisuje bieżący stan monitorowania do użycia w interfejsie użytkownika.
Metoda jest podobna InvokeChanged
do wcześniejszego podejścia, z tą różnicą, że:
- Nie uruchamia kodu, chyba że
MonitoringEnabled
ma wartośćtrue
. - Zwraca bieżącą wartość
state
w danych wyjściowychWriteConsole
.
private void InvokeChanged(IConfigurationMonitor state)
{
if (MonitoringEnabled)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{_env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
string message = $"State updated at {DateTime.Now}";
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (ConfigurationMonitor Class) " +
$"{message}, state:{state.CurrentState}");
}
}
}
Wystąpienie ConfigurationMonitor
jest zarejestrowane jako usługa w programie Startup.ConfigureServices
:
services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();
Strona Indeks oferuje użytkownikowi kontrolę nad monitorowaniem konfiguracji. Wystąpienie IConfigurationMonitor
klasy jest wstrzykiwane do obiektu IndexModel
.
Pages/Index.cshtml.cs
:
public IndexModel(
IConfiguration config,
IConfigurationMonitor monitor,
FileService fileService)
{
_config = config;
_monitor = monitor;
_fileService = fileService;
}
Monitor konfiguracji (_monitor
) służy do włączania lub wyłączania monitorowania i ustawiania bieżącego stanu opinii interfejsu użytkownika:
public IActionResult OnPostStartMonitoring()
{
_monitor.MonitoringEnabled = true;
_monitor.CurrentState = "Monitoring!";
return RedirectToPage();
}
public IActionResult OnPostStopMonitoring()
{
_monitor.MonitoringEnabled = false;
_monitor.CurrentState = "Not monitoring";
return RedirectToPage();
}
Po OnPostStartMonitoring
wyzwoleniu monitorowanie jest włączone, a bieżący stan zostanie wyczyszczone. Po OnPostStopMonitoring
wyzwoleniu monitorowanie jest wyłączone, a stan jest ustawiony tak, aby odzwierciedlał, że monitorowanie nie występuje.
Przyciski w interfejsie użytkownika włączają i wyłączają monitorowanie.
Pages/Index.cshtml
:
<button class="btn btn-success" asp-page-handler="StartMonitoring">
Start Monitoring
</button>
<button class="btn btn-danger" asp-page-handler="StopMonitoring">
Stop Monitoring
</button>
Monitorowanie buforowanych zmian plików
Zawartość pliku może być buforowana w pamięci przy użyciu polecenia IMemoryCache. Buforowanie w pamięci zostało opisane w temacie Pamięć podręczna w pamięci . Bez wykonywania dodatkowych kroków, takich jak implementacja opisana poniżej, nieaktualne (nieaktualne) dane są zwracane z pamięci podręcznej, jeśli dane źródłowe się zmienią.
Na przykład nie biorąc pod uwagę stanu buforowanego pliku źródłowego podczas odnawiania przesuwanego okresu wygaśnięcia prowadzi do nieaktualnych danych plików w pamięci podręcznej. Każde żądanie dotyczące danych odnawia okres wygaśnięcia, ale plik nigdy nie jest ponownie ładowany do pamięci podręcznej. Wszystkie funkcje aplikacji korzystające z buforowanej zawartości pliku mogą otrzymywać nieaktualną zawartość.
Używanie tokenów zmian w scenariuszu buforowania plików zapobiega obecności nieaktualnej zawartości pliku w pamięci podręcznej. Przykładowa aplikacja demonstruje implementację podejścia.
W przykładzie użyto metody GetFileContent
do:
- Zwracanie zawartości pliku.
- Zaimplementuj algorytm ponawiania z wykładniczym wycofywaniem, aby uwzględnić przypadki, w których problem z dostępem do pliku tymczasowo opóźnia odczytywanie zawartości pliku.
Utilities/Utilities.cs
:
public async static Task<string> GetFileContent(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fileStreamReader = File.OpenText(filePath))
{
return await fileStreamReader.ReadToEndAsync();
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3)
{
throw;
}
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return null;
}
Zostanie FileService
utworzony element do obsługi buforowanych odnośników plików. Wywołanie GetFileContent
metody usługi próbuje uzyskać zawartość pliku z pamięci podręcznej w pamięci i zwrócić ją do obiektu wywołującego (Services/FileService.cs
).
Jeśli zawartość buforowana nie zostanie znaleziona przy użyciu klucza pamięci podręcznej, zostaną wykonane następujące akcje:
- Zawartość pliku jest uzyskiwana przy użyciu polecenia
GetFileContent
. - Token zmiany jest uzyskiwany od dostawcy plików za pomocą elementu IFileProviders.Watch. Wywołanie zwrotne tokenu jest wyzwalane po zmodyfikowaniu pliku.
- Zawartość pliku jest buforowana z przesuwanym okresem wygaśnięcia . Token zmiany jest dołączany z elementem MemoryCacheEntryExtensions.AddExpirationToken , aby wykluczyć wpis pamięci podręcznej, jeśli plik ulegnie zmianie w pamięci podręcznej.
W poniższym przykładzie pliki są przechowywane w katalogu głównym zawartości aplikacji. IWebHostEnvironment.ContentRootFileProvider
służy do uzyskiwania wskaźnika IFileProvider w aplikacji IWebHostEnvironment.ContentRootPath
. Element filePath
jest uzyskiwany za pomocą elementu IFileInfo.PhysicalPath.
public class FileService
{
private readonly IMemoryCache _cache;
private readonly IFileProvider _fileProvider;
private List<string> _tokens = new List<string>();
public FileService(IMemoryCache cache, IWebHostEnvironment env)
{
_cache = cache;
_fileProvider = env.ContentRootFileProvider;
}
public async Task<string> GetFileContents(string fileName)
{
var filePath = _fileProvider.GetFileInfo(fileName).PhysicalPath;
string fileContent;
// Try to obtain the file contents from the cache.
if (_cache.TryGetValue(filePath, out fileContent))
{
return fileContent;
}
// The cache doesn't have the entry, so obtain the file
// contents from the file itself.
fileContent = await GetFileContent(filePath);
if (fileContent != null)
{
// Obtain a change token from the file provider whose
// callback is triggered when the file is modified.
var changeToken = _fileProvider.Watch(fileName);
// Configure the cache entry options for a five minute
// sliding expiration and use the change token to
// expire the file in the cache if the file is
// modified.
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5))
.AddExpirationToken(changeToken);
// Put the file contents into the cache.
_cache.Set(filePath, fileContent, cacheEntryOptions);
return fileContent;
}
return string.Empty;
}
}
Element FileService
jest zarejestrowany w kontenerze usługi wraz z usługą buforowania pamięci.
W pliku Startup.ConfigureServices
:
services.AddMemoryCache();
services.AddSingleton<FileService>();
Model strony ładuje zawartość pliku przy użyciu usługi.
W metodzie strony OnGet
Indeks (Pages/Index.cshtml.cs
):
var fileContent = await _fileService.GetFileContents("poem.txt");
CompositeChangeToken, klasa
Aby reprezentować co najmniej jedno IChangeToken
wystąpienie w jednym obiekcie, użyj CompositeChangeToken klasy .
var firstCancellationTokenSource = new CancellationTokenSource();
var secondCancellationTokenSource = new CancellationTokenSource();
var firstCancellationToken = firstCancellationTokenSource.Token;
var secondCancellationToken = secondCancellationTokenSource.Token;
var firstCancellationChangeToken = new CancellationChangeToken(firstCancellationToken);
var secondCancellationChangeToken = new CancellationChangeToken(secondCancellationToken);
var compositeChangeToken =
new CompositeChangeToken(
new List<IChangeToken>
{
firstCancellationChangeToken,
secondCancellationChangeToken
});
HasChanged
w raportach true
tokenu złożonego, jeśli jakikolwiek reprezentowany token HasChanged
to true
. ActiveChangeCallbacks
w raportach true
tokenu złożonego, jeśli jakikolwiek reprezentowany token ActiveChangeCallbacks
to true
. Jeśli wystąpi wiele współbieżnych zdarzeń zmiany, wywołanie zwrotne zmiany złożonej jest wywoływane jednorazowo.
Token zmiany jest blokiem konstrukcyjnym ogólnego przeznaczenia niskiego poziomu używanym do śledzenia zmian stanu.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
IChangeToken, interfejs
IChangeToken propaguje powiadomienia o wystąpieniu zmiany. IChangeToken
znajduje się w Microsoft.Extensions.Primitives przestrzeni nazw. W przypadku aplikacji, które nie korzystają z Microsoft.AspNetCore.App metapakiet, utwórz odwołanie do pakietu Microsoft.Extensions.Primitives NuGet.
IChangeToken
ma dwie właściwości:
- ActiveChangeCallbacks wskaż, czy token aktywnie wywołuje wywołania zwrotne. Jeśli
ActiveChangedCallbacks
ustawiono wartośćfalse
, wywołanie zwrotne nigdy nie jest wywoływane, a aplikacja musi sondowaćHasChanged
pod kątem zmian. Istnieje również możliwość, że token nigdy nie zostanie anulowany, jeśli nie wystąpią żadne zmiany lub podstawowy odbiornik zmian zostanie usunięty lub wyłączony. - HasChanged otrzymuje wartość wskazującą, czy nastąpiła zmiana.
Interfejs IChangeToken
zawiera metodę RegisterChangeCallback(Action<Object>, Object), która rejestruje wywołanie zwrotne wywoływane po zmianie tokenu. HasChanged
należy ustawić przed wywołaniem wywołania zwrotnego.
ChangeToken, klasa
ChangeToken jest klasą statyczną używaną do propagowania powiadomień o wystąpieniu zmiany. ChangeToken
znajduje się w Microsoft.Extensions.Primitives przestrzeni nazw. W przypadku aplikacji, które nie korzystają z Microsoft.AspNetCore.App metapakiet, utwórz odwołanie do pakietu Microsoft.Extensions.Primitives NuGet.
Metoda ChangeToken.OnChange(Func<IChangeToken>, Action) rejestruje metodę Action
do wywołania przy każdym zmianie tokenu:
Func<IChangeToken>
tworzy token.Action
jest wywoływany po zmianie tokenu.
Przeciążenie ChangeToken.OnChange TState>(Func<IChangeToken<>, Action<TState, TState>) przyjmuje dodatkowy TState
parametr przekazany do użytkownika Action
tokenu .
OnChange
zwraca wartość IDisposable. Wywołanie Dispose uniemożliwia nasłuchiwanie tokenu w celu uzyskania dalszych zmian i zwalnia zasoby tokenu.
Przykładowe zastosowania tokenów zmian w programie ASP.NET Core
Tokeny zmian są używane w widocznych obszarach ASP.NET Core do monitorowania zmian w obiektach:
- W celu monitorowania zmian w plikach IFileProvidermetoda tworzy Watch
IChangeToken
element dla określonych plików lub folderu do obejrzenia. IChangeToken
tokeny można dodawać do wpisów pamięci podręcznej w celu wyzwalania eksmisji pamięci podręcznej po zmianie.- W przypadku
TOptions
zmian domyślna OptionsMonitor<TOptions> implementacja IOptionsMonitor<TOptions> ma przeciążenie, które akceptuje co najmniej jedno IOptionsChangeTokenSource<TOptions> wystąpienie. Każde wystąpienie zwraca element ,IChangeToken
aby zarejestrować wywołanie zwrotne powiadomienia o zmianie w celu śledzenia zmian opcji.
Monitorowanie zmian konfiguracji
Domyślnie szablony ASP.NET Core używają plików konfiguracji JSON (appsettings.json
, appsettings.Development.json
i appsettings.Production.json
) do ładowania ustawień konfiguracji aplikacji.
Te pliki są konfigurowane przy użyciu metody rozszerzenia AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean), która ConfigurationBuilder akceptuje reloadOnChange
parametr. reloadOnChange
wskazuje, czy konfiguracja powinna zostać ponownie załadowana na zmiany w pliku. To ustawienie jest wyświetlane w metodzie WebHost CreateDefaultBuilderwygody :
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
Konfiguracja oparta na plikach jest reprezentowana przez FileConfigurationSource. FileConfigurationSource
program używa IFileProvider do monitorowania plików.
Domyślnie element IFileMonitor
jest dostarczany przez PhysicalFileProviderelement , który służy FileSystemWatcher do monitorowania zmian plików konfiguracji.
Przykładowa aplikacja demonstruje dwie implementacje monitorowania zmian konfiguracji. Jeśli którykolwiek z appsettings
plików ulegnie zmianie, obie implementacje monitorowania plików wykonują kod niestandardowy — przykładowa aplikacja zapisuje komunikat w konsoli.
Plik FileSystemWatcher
konfiguracji może wyzwalać wiele wywołań zwrotnych tokenów dla jednej zmiany pliku konfiguracji. Aby upewnić się, że kod niestandardowy jest uruchamiany tylko raz po wyzwoleniu wielu wywołań zwrotnych tokenów, implementacja przykładu sprawdza skróty plików. W przykładzie użyto skrótu pliku SHA1. Ponawianie jest implementowane przy użyciu wycofywania wykładniczego.
Utilities/Utilities.cs
:
public static byte[] ComputeHash(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fs = File.OpenRead(filePath))
{
return System.Security.Cryptography.SHA1
.Create().ComputeHash(fs);
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3)
{
throw;
}
Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
return new byte[20];
}
Prosty token zmiany uruchamiania
Zarejestruj wywołanie zwrotne użytkownika Action
tokenu, aby otrzymywać powiadomienia o zmianie w tokenie ponownego ładowania konfiguracji.
W pliku Startup.Configure
:
ChangeToken.OnChange(
() => config.GetReloadToken(),
(state) => InvokeChanged(state),
env);
config.GetReloadToken()
udostępnia token. Wywołanie zwrotne jest InvokeChanged
metodą:
private void InvokeChanged(IHostingEnvironment env)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (Simple Startup Change Token)");
}
}
Wywołanie state
zwrotne służy do przekazywania IHostingEnvironment
elementu , który jest przydatny do określenia poprawnego appsettings
pliku konfiguracji do monitorowania (na przykład appsettings.Development.json
w środowisku deweloperskiego). Skróty plików są używane do zapobiegania wielokrotnemu uruchamianiu WriteConsole
instrukcji z powodu wielu wywołań zwrotnych tokenów, gdy plik konfiguracji zmienił się tylko raz.
Ten system działa tak długo, jak aplikacja jest uruchomiona i nie może być wyłączona przez użytkownika.
Monitorowanie zmian konfiguracji jako usługi
Przykład implementuje:
- Podstawowe monitorowanie tokenu uruchamiania.
- Monitorowanie jako usługa.
- Mechanizm włączania i wyłączania monitorowania.
Przykład ustanawia IConfigurationMonitor
interfejs.
Extensions/ConfigurationMonitor.cs
:
public interface IConfigurationMonitor
{
bool MonitoringEnabled { get; set; }
string CurrentState { get; set; }
}
Konstruktor zaimplementowanej klasy , ConfigurationMonitor
rejestruje wywołanie zwrotne dla powiadomień o zmianie:
public ConfigurationMonitor(IConfiguration config, IHostingEnvironment env)
{
_env = env;
ChangeToken.OnChange<IConfigurationMonitor>(
() => config.GetReloadToken(),
InvokeChanged,
this);
}
public bool MonitoringEnabled { get; set; } = false;
public string CurrentState { get; set; } = "Not monitoring";
config.GetReloadToken()
dostarcza token. InvokeChanged
to metoda wywołania zwrotnego. W state
tym wystąpieniu jest odwołaniem do IConfigurationMonitor
wystąpienia używanego do uzyskiwania dostępu do stanu monitorowania. Używane są dwie właściwości:
MonitoringEnabled
: wskazuje, czy wywołanie zwrotne powinno uruchamiać kod niestandardowy.CurrentState
: opisuje bieżący stan monitorowania do użycia w interfejsie użytkownika.
Metoda jest podobna InvokeChanged
do wcześniejszego podejścia, z tą różnicą, że:
- Nie uruchamia kodu, chyba że
MonitoringEnabled
ma wartośćtrue
. - Zwraca bieżącą wartość
state
w danych wyjściowychWriteConsole
.
private void InvokeChanged(IConfigurationMonitor state)
{
if (MonitoringEnabled)
{
byte[] appsettingsHash = ComputeHash("appSettings.json");
byte[] appsettingsEnvHash =
ComputeHash($"appSettings.{_env.EnvironmentName}.json");
if (!_appsettingsHash.SequenceEqual(appsettingsHash) ||
!_appsettingsEnvHash.SequenceEqual(appsettingsEnvHash))
{
string message = $"State updated at {DateTime.Now}";
_appsettingsHash = appsettingsHash;
_appsettingsEnvHash = appsettingsEnvHash;
WriteConsole("Configuration changed (ConfigurationMonitor Class) " +
$"{message}, state:{state.CurrentState}");
}
}
}
Wystąpienie ConfigurationMonitor
jest zarejestrowane jako usługa w programie Startup.ConfigureServices
:
services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();
Strona Indeks oferuje użytkownikowi kontrolę nad monitorowaniem konfiguracji. Wystąpienie IConfigurationMonitor
klasy jest wstrzykiwane do obiektu IndexModel
.
Pages/Index.cshtml.cs
:
public IndexModel(
IConfiguration config,
IConfigurationMonitor monitor,
FileService fileService)
{
_config = config;
_monitor = monitor;
_fileService = fileService;
}
Monitor konfiguracji (_monitor
) służy do włączania lub wyłączania monitorowania i ustawiania bieżącego stanu opinii interfejsu użytkownika:
public IActionResult OnPostStartMonitoring()
{
_monitor.MonitoringEnabled = true;
_monitor.CurrentState = "Monitoring!";
return RedirectToPage();
}
public IActionResult OnPostStopMonitoring()
{
_monitor.MonitoringEnabled = false;
_monitor.CurrentState = "Not monitoring";
return RedirectToPage();
}
Po OnPostStartMonitoring
wyzwoleniu monitorowanie jest włączone, a bieżący stan zostanie wyczyszczone. Po OnPostStopMonitoring
wyzwoleniu monitorowanie jest wyłączone, a stan jest ustawiony tak, aby odzwierciedlał, że monitorowanie nie występuje.
Przyciski w interfejsie użytkownika włączają i wyłączają monitorowanie.
Pages/Index.cshtml
:
<button class="btn btn-success" asp-page-handler="StartMonitoring">
Start Monitoring
</button>
<button class="btn btn-danger" asp-page-handler="StopMonitoring">
Stop Monitoring
</button>
Monitorowanie buforowanych zmian plików
Zawartość pliku może być buforowana w pamięci przy użyciu polecenia IMemoryCache. Buforowanie w pamięci zostało opisane w temacie Pamięć podręczna w pamięci . Bez wykonywania dodatkowych kroków, takich jak implementacja opisana poniżej, nieaktualne (nieaktualne) dane są zwracane z pamięci podręcznej, jeśli dane źródłowe się zmienią.
Na przykład nie biorąc pod uwagę stanu buforowanego pliku źródłowego podczas odnawiania przesuwanego okresu wygaśnięcia prowadzi do nieaktualnych danych plików w pamięci podręcznej. Każde żądanie dotyczące danych odnawia okres wygaśnięcia, ale plik nigdy nie jest ponownie ładowany do pamięci podręcznej. Wszystkie funkcje aplikacji korzystające z buforowanej zawartości pliku mogą otrzymywać nieaktualną zawartość.
Używanie tokenów zmian w scenariuszu buforowania plików zapobiega obecności nieaktualnej zawartości pliku w pamięci podręcznej. Przykładowa aplikacja demonstruje implementację podejścia.
W przykładzie użyto metody GetFileContent
do:
- Zwracanie zawartości pliku.
- Zaimplementuj algorytm ponawiania z wykładniczym wycofywaniem, aby uwzględnić przypadki, w których problem z dostępem do pliku tymczasowo opóźnia odczytywanie zawartości pliku.
Utilities/Utilities.cs
:
public async static Task<string> GetFileContent(string filePath)
{
var runCount = 1;
while(runCount < 4)
{
try
{
if (File.Exists(filePath))
{
using (var fileStreamReader = File.OpenText(filePath))
{
return await fileStreamReader.ReadToEndAsync();
}
}
else
{
throw new FileNotFoundException();
}
}
catch (IOException ex)
{
if (runCount == 3 || ex.HResult != -2147024864)
{
throw;
}
else
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, runCount)));
runCount++;
}
}
}
return null;
}
Zostanie FileService
utworzony element do obsługi buforowanych odnośników plików. Wywołanie GetFileContent
metody usługi próbuje uzyskać zawartość pliku z pamięci podręcznej w pamięci i zwrócić ją do obiektu wywołującego (Services/FileService.cs
).
Jeśli zawartość buforowana nie zostanie znaleziona przy użyciu klucza pamięci podręcznej, zostaną wykonane następujące akcje:
- Zawartość pliku jest uzyskiwana przy użyciu polecenia
GetFileContent
. - Token zmiany jest uzyskiwany od dostawcy plików za pomocą elementu IFileProviders.Watch. Wywołanie zwrotne tokenu jest wyzwalane po zmodyfikowaniu pliku.
- Zawartość pliku jest buforowana z przesuwanym okresem wygaśnięcia . Token zmiany jest dołączany z elementem MemoryCacheEntryExtensions.AddExpirationToken , aby wykluczyć wpis pamięci podręcznej, jeśli plik ulegnie zmianie w pamięci podręcznej.
W poniższym przykładzie pliki są przechowywane w katalogu głównym zawartości aplikacji. Element IHostingEnvironment.ContentRootFileProvider służy do uzyskiwania wskaźnika IFileProvider wskazującego na element ContentRootPath. Element filePath
jest uzyskiwany za pomocą elementu IFileInfo.PhysicalPath.
public class FileService
{
private readonly IMemoryCache _cache;
private readonly IFileProvider _fileProvider;
private List<string> _tokens = new List<string>();
public FileService(IMemoryCache cache, IHostingEnvironment env)
{
_cache = cache;
_fileProvider = env.ContentRootFileProvider;
}
public async Task<string> GetFileContents(string fileName)
{
var filePath = _fileProvider.GetFileInfo(fileName).PhysicalPath;
string fileContent;
// Try to obtain the file contents from the cache.
if (_cache.TryGetValue(filePath, out fileContent))
{
return fileContent;
}
// The cache doesn't have the entry, so obtain the file
// contents from the file itself.
fileContent = await GetFileContent(filePath);
if (fileContent != null)
{
// Obtain a change token from the file provider whose
// callback is triggered when the file is modified.
var changeToken = _fileProvider.Watch(fileName);
// Configure the cache entry options for a five minute
// sliding expiration and use the change token to
// expire the file in the cache if the file is
// modified.
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5))
.AddExpirationToken(changeToken);
// Put the file contents into the cache.
_cache.Set(filePath, fileContent, cacheEntryOptions);
return fileContent;
}
return string.Empty;
}
}
Element FileService
jest zarejestrowany w kontenerze usługi wraz z usługą buforowania pamięci.
W pliku Startup.ConfigureServices
:
services.AddMemoryCache();
services.AddSingleton<FileService>();
Model strony ładuje zawartość pliku przy użyciu usługi.
W metodzie strony OnGet
Indeks (Pages/Index.cshtml.cs
):
var fileContent = await _fileService.GetFileContents("poem.txt");
CompositeChangeToken, klasa
Aby reprezentować co najmniej jedno IChangeToken
wystąpienie w jednym obiekcie, użyj CompositeChangeToken klasy .
var firstCancellationTokenSource = new CancellationTokenSource();
var secondCancellationTokenSource = new CancellationTokenSource();
var firstCancellationToken = firstCancellationTokenSource.Token;
var secondCancellationToken = secondCancellationTokenSource.Token;
var firstCancellationChangeToken = new CancellationChangeToken(firstCancellationToken);
var secondCancellationChangeToken = new CancellationChangeToken(secondCancellationToken);
var compositeChangeToken =
new CompositeChangeToken(
new List<IChangeToken>
{
firstCancellationChangeToken,
secondCancellationChangeToken
});
HasChanged
w raportach true
tokenu złożonego, jeśli jakikolwiek reprezentowany token HasChanged
to true
. ActiveChangeCallbacks
w raportach true
tokenu złożonego, jeśli jakikolwiek reprezentowany token ActiveChangeCallbacks
to true
. Jeśli wystąpi wiele współbieżnych zdarzeń zmiany, wywołanie zwrotne zmiany złożonej jest wywoływane jednorazowo.
Dodatkowe zasoby
- Buforowanie w pamięci w ASP.NET Core
- Buforowanie rozproszone w ASP.NET Core
- Buforowanie odpowiedzi na platformie ASP.NET Core
- Oprogramowanie pośredniczące buforowania odpowiedzi w programie ASP.NET Core
- Pomocnik tagów pamięci podręcznej w ASP.NET Core MVC
- Pomocnik tagów rozproszonej pamięci podręcznej w ASP.NET Core