ASP.NET Core에서 변경 토큰을 사용하여 변경 내용 검색
참고 항목
이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
Important
이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.
현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
변경 토큰은 상태 변경 내용을 추적하는 데 사용되는 범용의 하위 수준 구성 요소입니다.
IChangeToken 인터페이스
IChangeToken은 변경이 발생했다는 알림을 전파합니다. IChangeToken
은 Microsoft.Extensions.Primitives 네임스페이스에 상주합니다. Microsoft.Extensions.Primitives NuGet 패키지는 ASP.NET Core 앱에 암시적으로 제공됩니다.
IChangeToken
에는 두 가지 속성이 있습니다.
- ActiveChangeCallbacks는 토큰이 사전에 콜백을 발생시키는지 여부를 나타냅니다.
ActiveChangedCallbacks
가false
로 설정된 경우 콜백은 호출되지 않고 앱은 변경에 대해HasChanged
를 폴링해야 합니다. 또한 변경이 발생하지 않거나 기본 변경 리스너가 삭제되거나 사용하지 않도록 설정된 경우에도 토큰을 절대로 취소할 수 없습니다. - HasChanged는 변경이 발생했는지 나타내는 값을 수신합니다.
IChangeToken
인터페이스는 RegisterChangeCallback(Action<Object>, Object) 메서드를 포함하며 이는 토큰이 변경될 때 호출되는 콜백을 등록합니다. HasChanged
는 콜백이 호출되기 전에 설정되어야 합니다.
ChangeToken 클래스
ChangeToken은 변경이 발생했다는 알림을 전파하는 데 사용되는 정적 클래스입니다. ChangeToken
은 Microsoft.Extensions.Primitives 네임스페이스에 상주합니다. Microsoft.Extensions.Primitives NuGet 패키지는 ASP.NET Core 앱에 암시적으로 제공됩니다.
ChangeToken.OnChange(Func<IChangeToken>, Action) 메서드는 토큰이 변경될 때마다 호출할 Action
을 등록합니다.
Func<IChangeToken>
은 출력을 생성합니다.- 토큰이 변경될 때
Action
이 호출됩니다.
ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState>, TState) 오버로드는 토큰 소비자 Action
에게 전달된 추가 TState
매개 변수를 사용합니다.
OnChange
는 IDisposable을 반환합니다. Dispose를 호출하면 토큰이 더 이상 변경 내용을 수신 대기하지 않고 토큰의 리소스가 해제됩니다.
ASP.NET Core에서 변경 토큰의 사용 예
변경 토큰은 ASP.NET Core에서 개체의 변경 내용을 모니터링하는 주요 영역에 사용됩니다.
- 파일에 대한 변경을 모니터링하기 위해 IFileProvider의 Watch 메서드는 지정된 파일 또는 조사할 폴더에 대해
IChangeToken
을 만듭니다. IChangeToken
토큰을 캐시 항목에 추가하여 변경 시 캐시 제거를 트리거할 수 있습니다.TOptions
변경의 경우, IOptionsMonitor<TOptions>의 기본 OptionsMonitor<TOptions> 구현은 둘 이상의 IOptionsChangeTokenSource<TOptions> 인스턴스를 받아들이는 오버로드를 포함합니다. 각 인스턴스는 옵션 변경을 추적하기 위해 변경 알림 콜백을 등록하도록IChangeToken
을 반환합니다.
구성 변경 모니터링
기본적으로 ASP.NET Core 템플릿은 JSON 구성 파일(appsettings.json
, appsettings.Development.json
, appsettings.Production.json
)을 사용하여 앱 구성 설정을 로드합니다.
이 파일은 reloadOnChange
매개 변수를 받아들이는 ConfigurationBuilder에 대해 AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean) 확장 메서드를 사용하여 구성합니다. reloadOnChange
는 파일 변경 시 구성을 다시 로드해야 하는지를 나타냅니다. 이 설정은 Host 편의 메서드 CreateDefaultBuilder에 표시됩니다.
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
파일 기반 구성은 FileConfigurationSource로 표현됩니다. FileConfigurationSource
는 IFileProvider를 사용하여 파일을 모니터링합니다.
기본적으로 IFileMonitor
는 FileSystemWatcher를 사용하여 구성 파일 변경을 모니터링하는 PhysicalFileProvider에서 제공합니다.
샘플 앱을 통해 구성 변경 모니터링을 위한 두 가지 구현을 설명합니다. appsettings
파일이 변경되면 두 파일 모니터링 구현 모두 사용자 지정 코드를 실행합니다. 샘플 앱은 콘솔에 메시지를 씁니다.
구성 파일의 FileSystemWatcher
는 단일 구성 파일 변경에 대해 토큰 콜백을 여러 개 트리거할 수 있습니다. 여러 토큰 콜백이 활성화될 때 사용자 지정 코드가 한 번만 실행 되도록 하기 위해 샘플의 구현은 파일 해시를 확인합니다. 샘플은 SHA1 파일 해시를 사용합니다. 재시도는 지수 백오프로 구현됩니다.
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];
}
단순 시작 변경 토큰
변경 알림을 위한 토큰 소비자 Action
콜백을 구성 로드 토큰에 등록합니다.
Startup.Configure
의 경우
ChangeToken.OnChange(
() => config.GetReloadToken(),
(state) => InvokeChanged(state),
env);
config.GetReloadToken()
은 토큰을 제공합니다. 콜백은 InvokeChanged
메서드입니다.
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)");
}
}
콜백의 state
는 모니터링할 정확한 appsettings
의 구성 파일을 지정하는 데 유용한 IWebHostEnvironment
를 전달하는 데 사용됩니다(예를 들어 개발 환경에서는 appsettings.Development.json
). 파일 해시는 구성 파일이 한 번만 변경된 경우 여러 토큰 콜백으로 인해 WriteConsole
문이 여러 번 실행되지 않도록 하는 데 사용됩니다.
이 시스템은 앱이 실행 중일 때 실행되며 사용자가 사용 중지할 수 없습니다.
서비스로 구성 변경 모니터링
이 샘플에서는 다음을 구현합니다.
- 기본 시작 토큰 모니터링
- 서비스로 모니터링
- 모니터링을 사용 및 사용 안 함으로 설정하는 메커니즘
이 샘플은 IConfigurationMonitor
인터페이스를 설정합니다.
Extensions/ConfigurationMonitor.cs
:
public interface IConfigurationMonitor
{
bool MonitoringEnabled { get; set; }
string CurrentState { get; set; }
}
구현된 클래스의 생성자인 ConfigurationMonitor
는 변경 알림을 위한 콜백을 등록합니다.
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()
은 토큰을 제공합니다. InvokeChanged
는 콜백 메서드입니다. 이 인스턴스의 state
는 모니터링 상태에 액세스하는 데 사용되는 IConfigurationMonitor
인스턴스에 대한 참조입니다. 두 개의 속성이 사용됩니다.
MonitoringEnabled
: 콜백에서 사용자 지정 코드를 실행해야 하는지를 나타냅니다.CurrentState
: UI에서 사용할 현재 모니터링 상태를 설명합니다.
InvokeChanged
메서드는 다음을 제외하고 이전 방법과 유사합니다.
MonitoringEnabled
가true
가 아닌 경우 해당 코드를 실행하지 않습니다.WriteConsole
출력의 현재state
를 출력합니다.
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}");
}
}
}
인스턴스 ConfigurationMonitor
는 Startup.ConfigureServices
에서 서비스로 등록됩니다.
services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();
인덱스 페이지에서는 구성 모니터링을 통해 사용자 컨트롤을 제공합니다. IConfigurationMonitor
의 인스턴스는 IndexModel
에 삽입됩니다.
Pages/Index.cshtml.cs
:
public IndexModel(
IConfiguration config,
IConfigurationMonitor monitor,
FileService fileService)
{
_config = config;
_monitor = monitor;
_fileService = fileService;
}
구성 모니터(_monitor
)는 모니터링을 사용 설정 또는 해제하고 UI 피드백에 대한 현재 상태를 설정합니다.
public IActionResult OnPostStartMonitoring()
{
_monitor.MonitoringEnabled = true;
_monitor.CurrentState = "Monitoring!";
return RedirectToPage();
}
public IActionResult OnPostStopMonitoring()
{
_monitor.MonitoringEnabled = false;
_monitor.CurrentState = "Not monitoring";
return RedirectToPage();
}
OnPostStartMonitoring
이 트리거되면 모니터링을 사용하도록 설정하고 현재 상태가 지워집니다. OnPostStopMonitoring
이 트리거되면 모니터링을 사용하지 않도록 설정하고 모니터링이 발생하지 않음을 반영하도록 상태를 설정합니다.
UI의 단추로 모니터링을 사용 설정 및 해제합니다.
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>
캐시된 파일 변경 모니터링
파일 콘텐츠는 IMemoryCache를 사용하여 메모리 내에 캐시할 수 있습니다. 메모리 내 캐싱은 메모리 내 캐시 토픽에서 설명합니다. 아래에 설명된 구현과 같은 추가 단계를 수행하지 않으면 소스 데이터가 변경될 경우 캐시에서 부실(오래된) 데이터가 반환됩니다.
예를 들어 상대(sliding) 만료 기간을 갱신할 때 캐시된 소스 파일의 상태를 고려하지 않으면 부실 캐시 데이터가 발생합니다. 데이터에 대한 각 요청은 상대(sliding) 만료 기간을 갱신하지만 파일은 캐시에 다시 로드되지 않습니다. 파일의 캐시된 콘텐츠를 사용하는 모든 앱 기능은 부실 콘텐츠를 받을 수 있습니다.
파일 캐싱 시나리오에서 변경 토큰을 사용하면 캐시에 부실 파일 콘텐츠가 생기는 것을 방지할 수 있습니다. 샘플 앱에서 이러한 방법의 구현을 보여 줍니다.
이 샘플에서는 GetFileContent
를 사용하여 다음을 수행합니다.
- 파일 콘텐츠를 반환합니다.
- 파일 액세스 문제로 인해 파일 내용 읽기가 일시적으로 지연되는 경우를 해결하기 위해 지수 백오프를 사용하여 재시도 알고리즘을 구현합니다.
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;
}
캐시된 파일 조회를 처리하기 위해 FileService
가 생성됩니다. 서비스의 GetFileContent
메서드 호출은 메모리 내 캐시에서 파일 콘텐츠를 가져와 호출자(Services/FileService.cs
)에게 반환하려고 합니다.
캐시 키를 사용하여 캐시된 콘텐츠를 찾을 수 없으면 다음 작업이 수행됩니다.
GetFileContent
를 사용하여 파일 콘텐츠를 가져옵니다.- IFileProviders.Watch를 통해 파일 공급자로부터 변경 토큰을 가져옵니다. 파일이 수정될 때 토큰의 콜백이 트리거됩니다.
- 상대(sliding) 만료 기간과 함께 파일 콘텐츠가 캐시됩니다. 변경 토큰은 MemoryCacheEntryExtensions.AddExpirationToken과 연결되어 파일이 캐시되는 동안 변경되면 캐시 항목을 제거합니다.
다음 예제에서 파일은 앱의 콘텐츠 루트에 저장됩니다. IWebHostEnvironment.ContentRootFileProvider
는 앱의 IWebHostEnvironment.ContentRootPath
를 가리키는 IFileProvider를 가져오는 데 사용됩니다. filePath
는 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;
}
}
FileService
가 메모리 캐싱 서비스와 함께 서비스 컨테이너에 등록됩니다.
Startup.ConfigureServices
의 경우
services.AddMemoryCache();
services.AddSingleton<FileService>();
페이지 모델은 서비스를 사용하여 파일의 콘텐츠를 로드합니다.
인덱스 페이지의 OnGet
메서드(Pages/Index.cshtml.cs
):
var fileContent = await _fileService.GetFileContents("poem.txt");
CompositeChangeToken 클래스
단일 개체에 있는 하나 이상의 IChangeToken
인스턴스를 나타내는 경우 CompositeChangeToken 클래스를 사용합니다.
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
가 true
인 경우 복합 토큰의 HasChanged
는 true
를 보고합니다. 표시된 모든 토큰의 ActiveChangeCallbacks
가 true
인 경우 복합 토큰의 ActiveChangeCallbacks
는 true
를 보고합니다. 여러 동시 변경 이벤트가 발생하면 복합 변경 콜백이 한 번 호출됩니다.
변경 토큰은 상태 변경 내용을 추적하는 데 사용되는 범용의 하위 수준 구성 요소입니다.
IChangeToken 인터페이스
IChangeToken은 변경이 발생했다는 알림을 전파합니다. IChangeToken
은 Microsoft.Extensions.Primitives 네임스페이스에 상주합니다. Microsoft.AspNetCore.App 메타패키지를 사용하지 않는 앱의 경우 Microsoft.Extensions.Primitives NuGet 패키지에 대한 패키지 참조를 생성합니다.
IChangeToken
에는 두 가지 속성이 있습니다.
- ActiveChangeCallbacks는 토큰이 사전에 콜백을 발생시키는지 여부를 나타냅니다.
ActiveChangedCallbacks
가false
로 설정된 경우 콜백은 호출되지 않고 앱은 변경에 대해HasChanged
를 폴링해야 합니다. 또한 변경이 발생하지 않거나 기본 변경 리스너가 삭제되거나 사용하지 않도록 설정된 경우에도 토큰을 절대로 취소할 수 없습니다. - HasChanged는 변경이 발생했는지 나타내는 값을 수신합니다.
IChangeToken
인터페이스는 RegisterChangeCallback(Action<Object>, Object) 메서드를 포함하며 이는 토큰이 변경될 때 호출되는 콜백을 등록합니다. HasChanged
는 콜백이 호출되기 전에 설정되어야 합니다.
ChangeToken 클래스
ChangeToken은 변경이 발생했다는 알림을 전파하는 데 사용되는 정적 클래스입니다. ChangeToken
은 Microsoft.Extensions.Primitives 네임스페이스에 상주합니다. Microsoft.AspNetCore.App 메타패키지를 사용하지 않는 앱의 경우 Microsoft.Extensions.Primitives NuGet 패키지에 대한 패키지 참조를 생성합니다.
ChangeToken.OnChange(Func<IChangeToken>, Action) 메서드는 토큰이 변경될 때마다 호출할 Action
을 등록합니다.
Func<IChangeToken>
은 출력을 생성합니다.- 토큰이 변경될 때
Action
이 호출됩니다.
ChangeToken.OnChange<TState>(Func<IChangeToken>, Action<TState>, TState) 오버로드는 토큰 소비자 Action
에게 전달된 추가 TState
매개 변수를 사용합니다.
OnChange
는 IDisposable을 반환합니다. Dispose를 호출하면 토큰이 더 이상 변경 내용을 수신 대기하지 않고 토큰의 리소스가 해제됩니다.
ASP.NET Core에서 변경 토큰의 사용 예
변경 토큰은 ASP.NET Core에서 개체의 변경 내용을 모니터링하는 주요 영역에 사용됩니다.
- 파일에 대한 변경을 모니터링하기 위해 IFileProvider의 Watch 메서드는 지정된 파일 또는 조사할 폴더에 대해
IChangeToken
을 만듭니다. IChangeToken
토큰을 캐시 항목에 추가하여 변경 시 캐시 제거를 트리거할 수 있습니다.TOptions
변경의 경우, IOptionsMonitor<TOptions>의 기본 OptionsMonitor<TOptions> 구현은 둘 이상의 IOptionsChangeTokenSource<TOptions> 인스턴스를 받아들이는 오버로드를 포함합니다. 각 인스턴스는 옵션 변경을 추적하기 위해 변경 알림 콜백을 등록하도록IChangeToken
을 반환합니다.
구성 변경 모니터링
기본적으로 ASP.NET Core 템플릿은 JSON 구성 파일(appsettings.json
, appsettings.Development.json
, appsettings.Production.json
)을 사용하여 앱 구성 설정을 로드합니다.
이 파일은 reloadOnChange
매개 변수를 받아들이는 ConfigurationBuilder에 대해 AddJsonFile(IConfigurationBuilder, String, Boolean, Boolean) 확장 메서드를 사용하여 구성합니다. reloadOnChange
는 파일 변경 시 구성을 다시 로드해야 하는지를 나타냅니다. 이 설정은 WebHost 편의 메서드 CreateDefaultBuilder에 표시됩니다.
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
파일 기반 구성은 FileConfigurationSource로 표현됩니다. FileConfigurationSource
는 IFileProvider를 사용하여 파일을 모니터링합니다.
기본적으로 IFileMonitor
는 FileSystemWatcher를 사용하여 구성 파일 변경을 모니터링하는 PhysicalFileProvider에서 제공합니다.
샘플 앱을 통해 구성 변경 모니터링을 위한 두 가지 구현을 설명합니다. appsettings
파일이 변경되면 두 파일 모니터링 구현 모두 사용자 지정 코드를 실행합니다. 샘플 앱은 콘솔에 메시지를 씁니다.
구성 파일의 FileSystemWatcher
는 단일 구성 파일 변경에 대해 토큰 콜백을 여러 개 트리거할 수 있습니다. 여러 토큰 콜백이 활성화될 때 사용자 지정 코드가 한 번만 실행 되도록 하기 위해 샘플의 구현은 파일 해시를 확인합니다. 샘플은 SHA1 파일 해시를 사용합니다. 재시도는 지수 백오프로 구현됩니다.
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];
}
단순 시작 변경 토큰
변경 알림을 위한 토큰 소비자 Action
콜백을 구성 로드 토큰에 등록합니다.
Startup.Configure
의 경우
ChangeToken.OnChange(
() => config.GetReloadToken(),
(state) => InvokeChanged(state),
env);
config.GetReloadToken()
은 토큰을 제공합니다. 콜백은 InvokeChanged
메서드입니다.
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)");
}
}
콜백의 state
는 모니터링할 정확한 appsettings
의 구성 파일을 지정하는 데 유용한 IHostingEnvironment
를 전달하는 데 사용됩니다(예를 들어 개발 환경에서는 appsettings.Development.json
). 파일 해시는 구성 파일이 한 번만 변경된 경우 여러 토큰 콜백으로 인해 WriteConsole
문이 여러 번 실행되지 않도록 하는 데 사용됩니다.
이 시스템은 앱이 실행 중일 때 실행되며 사용자가 사용 중지할 수 없습니다.
서비스로 구성 변경 모니터링
이 샘플에서는 다음을 구현합니다.
- 기본 시작 토큰 모니터링
- 서비스로 모니터링
- 모니터링을 사용 및 사용 안 함으로 설정하는 메커니즘
이 샘플은 IConfigurationMonitor
인터페이스를 설정합니다.
Extensions/ConfigurationMonitor.cs
:
public interface IConfigurationMonitor
{
bool MonitoringEnabled { get; set; }
string CurrentState { get; set; }
}
구현된 클래스의 생성자인 ConfigurationMonitor
는 변경 알림을 위한 콜백을 등록합니다.
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()
은 토큰을 제공합니다. InvokeChanged
는 콜백 메서드입니다. 이 인스턴스의 state
는 모니터링 상태에 액세스하는 데 사용되는 IConfigurationMonitor
인스턴스에 대한 참조입니다. 두 개의 속성이 사용됩니다.
MonitoringEnabled
: 콜백에서 사용자 지정 코드를 실행해야 하는지를 나타냅니다.CurrentState
: UI에서 사용할 현재 모니터링 상태를 설명합니다.
InvokeChanged
메서드는 다음을 제외하고 이전 방법과 유사합니다.
MonitoringEnabled
가true
가 아닌 경우 해당 코드를 실행하지 않습니다.WriteConsole
출력의 현재state
를 출력합니다.
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}");
}
}
}
인스턴스 ConfigurationMonitor
는 Startup.ConfigureServices
에서 서비스로 등록됩니다.
services.AddSingleton<IConfigurationMonitor, ConfigurationMonitor>();
인덱스 페이지에서는 구성 모니터링을 통해 사용자 컨트롤을 제공합니다. IConfigurationMonitor
의 인스턴스는 IndexModel
에 삽입됩니다.
Pages/Index.cshtml.cs
:
public IndexModel(
IConfiguration config,
IConfigurationMonitor monitor,
FileService fileService)
{
_config = config;
_monitor = monitor;
_fileService = fileService;
}
구성 모니터(_monitor
)는 모니터링을 사용 설정 또는 해제하고 UI 피드백에 대한 현재 상태를 설정합니다.
public IActionResult OnPostStartMonitoring()
{
_monitor.MonitoringEnabled = true;
_monitor.CurrentState = "Monitoring!";
return RedirectToPage();
}
public IActionResult OnPostStopMonitoring()
{
_monitor.MonitoringEnabled = false;
_monitor.CurrentState = "Not monitoring";
return RedirectToPage();
}
OnPostStartMonitoring
이 트리거되면 모니터링을 사용하도록 설정하고 현재 상태가 지워집니다. OnPostStopMonitoring
이 트리거되면 모니터링을 사용하지 않도록 설정하고 모니터링이 발생하지 않음을 반영하도록 상태를 설정합니다.
UI의 단추로 모니터링을 사용 설정 및 해제합니다.
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>
캐시된 파일 변경 모니터링
파일 콘텐츠는 IMemoryCache를 사용하여 메모리 내에 캐시할 수 있습니다. 메모리 내 캐싱은 메모리 내 캐시 토픽에서 설명합니다. 아래에 설명된 구현과 같은 추가 단계를 수행하지 않으면 소스 데이터가 변경될 경우 캐시에서 부실(오래된) 데이터가 반환됩니다.
예를 들어 상대(sliding) 만료 기간을 갱신할 때 캐시된 소스 파일의 상태를 고려하지 않으면 부실 캐시 데이터가 발생합니다. 데이터에 대한 각 요청은 상대(sliding) 만료 기간을 갱신하지만 파일은 캐시에 다시 로드되지 않습니다. 파일의 캐시된 콘텐츠를 사용하는 모든 앱 기능은 부실 콘텐츠를 받을 수 있습니다.
파일 캐싱 시나리오에서 변경 토큰을 사용하면 캐시에 부실 파일 콘텐츠가 생기는 것을 방지할 수 있습니다. 샘플 앱에서 이러한 방법의 구현을 보여 줍니다.
이 샘플에서는 GetFileContent
를 사용하여 다음을 수행합니다.
- 파일 콘텐츠를 반환합니다.
- 파일 액세스 문제로 인해 파일 내용 읽기가 일시적으로 지연되는 경우를 해결하기 위해 지수 백오프를 사용하여 재시도 알고리즘을 구현합니다.
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;
}
캐시된 파일 조회를 처리하기 위해 FileService
가 생성됩니다. 서비스의 GetFileContent
메서드 호출은 메모리 내 캐시에서 파일 콘텐츠를 가져와 호출자(Services/FileService.cs
)에게 반환하려고 합니다.
캐시 키를 사용하여 캐시된 콘텐츠를 찾을 수 없으면 다음 작업이 수행됩니다.
GetFileContent
를 사용하여 파일 콘텐츠를 가져옵니다.- IFileProviders.Watch를 통해 파일 공급자로부터 변경 토큰을 가져옵니다. 파일이 수정될 때 토큰의 콜백이 트리거됩니다.
- 상대(sliding) 만료 기간과 함께 파일 콘텐츠가 캐시됩니다. 변경 토큰은 MemoryCacheEntryExtensions.AddExpirationToken과 연결되어 파일이 캐시되는 동안 변경되면 캐시 항목을 제거합니다.
다음 예제에서 파일은 앱의 콘텐츠 루트에 저장됩니다. IHostingEnvironment.ContentRootFileProvider는 앱의 ContentRootPath를 가리키는 IFileProvider를 가져오는 데 사용합니다. filePath
는 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;
}
}
FileService
가 메모리 캐싱 서비스와 함께 서비스 컨테이너에 등록됩니다.
Startup.ConfigureServices
의 경우
services.AddMemoryCache();
services.AddSingleton<FileService>();
페이지 모델은 서비스를 사용하여 파일의 콘텐츠를 로드합니다.
인덱스 페이지의 OnGet
메서드(Pages/Index.cshtml.cs
):
var fileContent = await _fileService.GetFileContents("poem.txt");
CompositeChangeToken 클래스
단일 개체에 있는 하나 이상의 IChangeToken
인스턴스를 나타내는 경우 CompositeChangeToken 클래스를 사용합니다.
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
가 true
인 경우 복합 토큰의 HasChanged
는 true
를 보고합니다. 표시된 모든 토큰의 ActiveChangeCallbacks
가 true
인 경우 복합 토큰의 ActiveChangeCallbacks
는 true
를 보고합니다. 여러 동시 변경 이벤트가 발생하면 복합 변경 콜백이 한 번 호출됩니다.
추가 리소스
ASP.NET Core