Configuration of ASP. NET Core (5): configuration synchronization [design], asp. netcore
The so-called "configuration synchronization" in this section mainly involves two aspects: first, how to monitor the configuration source and automatically load its data when it changes, the purpose is to synchronize the Configuration carried by the Configuration object in the application with the Configuration source data. Second, how to send notifications to the application when the Configuration changes carried by the Configuration object are released, finally, let the application use the latest configuration.
1. Configure and configure source Synchronization
The configuration model provides three native ConfigurationProvider (JsonConfigrationProvider, XmlConfigurationProvider, and IniConfigurationProvider) that allow us to use files in three formats (JSON, XML, and INI) as the source for configuring raw data, therefore, configuring synchronization for physical files is a major application area for configuring synchronization mechanisms. In the example shown above, physical file-based synchronization is achieved by calling the ConfigurationRoot Extension Method ReloadOnChanged.
This extension method is defined in the NuGet package "Microsoft. extensions. configuration. in FileProviderExtensions, in addition to the method used in the instance we demonstrate, this ReloadOnChanged method also has the following two additional loads. For these three ReloadOnChanged method overloading, the final implementation falls on the third overload. The most essential physical file monitoring function is the responsibility of an object named FileProvider.
1: public static class FileProviderExtensions
2: {
3: public static IConfigurationRoot ReloadOnChanged(
4: this IConfigurationRoot config, string filename);
5:
6: public static IConfigurationRoot ReloadOnChanged(
7: this IConfigurationRoot config, string basePath, string filename);
8:
9: public static IConfigurationRoot ReloadOnChanged(this IConfigurationRoot config,
10: IFileProvider fileProvider, string filename);
11: }
The so-called FileProvider is a general term for all types that implement the IFileProvider interface and their objects. The IFileProvier interface is defined in the namespace "Microsoft. aspNet. in FileProviders, it provides abstract directories and file information by defining the methods in it. The methods related to file monitoring are also defined in this interface. As shown in the following code snippet, IFileProvier has three methods. GetDirectoryContents and GetFileInfo are used to provide information about directories and files. We only need to pay attention to the Watch method designed to monitor file changes.
1: public interface IFileProvider
2: {
3: IDirectoryContents GetDirectoryContents(string subpath);
4: IFileInfo GetFileInfo(string subpath);
5: IChangeToken Watch(string filter);
6: }
A FileProvider always targets a specific directory. The Watch method's parameter filter aims to help filter out the files to be monitored. This parameter is a string that can carry a wildcard ("*"), such as "*. * "indicates all files, while" *. json indicates that all extensions are ". json file. If you need to monitor a specific file in the current directory, you can directly use the file name as a parameter. The return type of the Watch method is the IChangeToken interface defined below. We can regard it as a token for passing data transformation notifications.
1: public interface IChangeToken
2: {
3: bool HasChanged { get; }
4: bool ActiveChangeCallbacks { get; }
5:
6: IDisposable RegisterChangeCallback(Action<object> callback, object state);
7: }
The read-only attribute HasChanged of IChangeToken indicates whether the target data has changed. You can call its RegisterChangeCallback method to register a callback operation that needs to be performed when data changes. The corresponding types of objects returned by this method must implement the IDisposable interface. The contact for callback registration can be completed through the Dispose method. The other read-only attribute ActiveChangeCallbacks of the IChangeToken interface indicates whether to actively perform the registration callback operation when data changes. In fact, the returned type of the IConfiguration GetReloadToke method is such an interface. We will introduce the specific objects returned by this method in the next section.
When we specify a specific FileProvider object to call the extension method ReloadOnChanged of ConfigurationRoot, the latter will call the RegisterChangeCallback method of this FileProvider to register a callback when the specified file changes. As for the callback of this registration, it will call the Reload method of ConfigurationRoot to Reload the configuration data. Since such a callback is registered, the method only needs to call the Watch method of FileProvider to monitor changes to the specified file. The code snippets shown below basically reflect the logic of the ReloadOnChanged method.
1: public static IConfigurationRoot ReloadOnChanged(
2: this IConfigurationRoot config, IFileProvider fileProvider, string filename)
3: {
4: Action<object> callback = null;
5: callback = _ =>
6: {
7: config.Reload();
8: fileProvider.Watch(filename).RegisterChangeCallback(callback, null);
9: };
10: fileProvider.Watch(filename).RegisterChangeCallback(callback, null);
11: return config;
12: }
If we call another ReloadOnChanged method overload by specifying the Directory and file name, the latter will create a PhysicalFileProvider object based on the specified directory and call the above overload as a parameter. As the name suggests, PhysicalFileProvider is a FileProvider for a specific physical file. It uses a FileSystemWatcher object to monitor the specified file. The implementation logic of this ReloadOnChanged method is reflected in the code snippets shown below. When we only specify the monitoring file name to call the first ReloadOnChanged Method for overloading, This method uses the directory where the current application is located as the parameter to call the above overload.
1: public static class FileProviderExtensions
2: {
3: public static IConfigurationRoot ReloadOnChanged(
4: this IConfigurationRoot config, string basePath, string filename)
5: => config.ReloadOnChanged(new PhysicalFileProvider(basePath), filename);
6: // other members
7: }
II. Application reload Configuration
ConfigurationRoot binds the ReloadOnChanged method of the extension method to a specific physical file. Any modification to the file will prompt the call of the Reload method, this ensures that the data it carries is always synchronized with the configuration source. Now we will discuss another topic of configuration synchronization, that is, how to use the new configuration without restarting the application. To understand the solution to this problem, let's first talk about defining the GetReloadToken method that has been deliberately avoided in the IConfiguration interface.
1: public interface IConfiguration
2: {
3: // other members
4: IChangeToken GetReloadToken();
5: }
As shown in the code snippet above, the returned type of the GetReloadToken method is the IChangeToken interface discussed above. we can regard the latter as a token for passing data change information. For a Configuration object, the so-called data transformation reflects the re-loading of the ConfigurationRoot object of the Configuration root node. Therefore, the ChangeToken object returned by this method reflects the Configuration changes caused by the last loading.
1: public class ConfigurationReloadToken : IChangeToken
2: {
3: public void OnReload();
4: public IDisposable RegisterChangeCallback(Action<object> callback,
5: object state);
6:
7: public bool ActiveChangeCallbacks { get; }
8: public bool HasChanged { get; }
9: }
For the two default types (ConfigurationRoot and ConfigurationSection) that implement the IConfiguration interface, their GetReloadToken method returns a ConfigurationReloadToken object. As shown in the code snippet above, in addition to implementing all the Members defined in the IConfiguration interface, the ConfigurationReloadToken also has another method named OnReload. When the configuration data changes, that is, when you call the Reload method of ConfigurationRoot to re-load the configuration, this method is called to send a signal that the configuration has changed.
The logic used to pass configuration changes in ConfigurationReloadToken is actually very simple. The specific logic is completed by a CancellationTokenSource object. If you are familiar with asynchronous programming for tasks, I believe you will not be unfamiliar with this type. In general, we can use CancellationTokenSource to send a "cancel Task" signal to an asynchronous Task.
1: public class ConfigurationReloadToken : IChangeToken
2: {
3: private CancellationTokenSource tokenSource = new CancellationTokenSource();
4:
5: public void OnReload() => tokenSource.Cancel();
6: public IDisposable RegisterChangeCallback(Action<object> callback, object state)
7: => tokenSource.Token.Register(callback, state);
8:
9: public bool ActiveChangeCallbacks { get; } = true;
10: public bool HasChanged
11: {
12: get { return tokenSource.IsCancellationRequested; }
13: }
14: }
As shown in the code snippet above, ConfigurationReloadToken is essentially a CancellationTokenSource object encapsulation. When the OnReload method is called, it directly calls the Cancel Method of CancellationTokenSource to send a request to Cancel the task, the HasChanged attribute uses the IsCancellationRequested attribute of CancellationTokenSource to determine whether the configuration data has changed by determining whether the task cancels the request and sends the request. The callback registered through RegisterChangeCallback is finally registered to the CancellationToken object created by CancellationTokenSource. Therefore, once the OnReload method is called, the registered callback is automatically executed. The ActiveChangeCallbacks attribute of ConfigurationReloadToken always returns True.
The two types of ConfigurationRoot and ConfigurationSection implement the GetReloadToken method in the following form respectively. From the given code snippets, it is not difficult to see that all the ConfigurationSection objects and the ConfigurationRoot objects that serve as their root objects, their GetReloadToken Methods return the same ConfigurationReloadToken object at the same time. When the Reload method of ConfigurationRoot is called, The OnReload method of the current ConfigurationReloadToken object will be called. After that, a new ConfigurationReloadToken object will be created and replaced with the original object.
1: public class ConfigurationRoot : IConfigurationRoot
2: {
3: private ConfigurationReloadToken reloadToken = new ConfigurationReloadToken();
4:
5: public IChangeToken GetReloadToken()
6: {
7: return reloadToken;
8: }
9:
10: public void Reload()
11: {
12: // omitting the re-load configuration code
13: Interlocked.Exchange<ConfigurationReloadToken>(ref this._reloadToken,
14: new ConfigurationReloadToken()).OnReload();
15: }
16: // other members
17: }
18:
19: public class ConfigurationSection : IConfigurationSection, IConfiguration
20: {
21: private readonly ConfigurationRoot root;
22: public IChangeToken GetReloadToken()
23: {
24: return root.GetReloadToken();
25: }
26: // other members
27: }
It is precisely because the GetReloadToken method does not guarantee that the same ConfigurationReloadToken object is returned each time. Therefore, when we register the configuration and load callback, we need to complete the callback registration for the new ConfigurationReloadToken object in the callback, in fact, this is what we demonstrated above. In addition, calling the RegisterChangeCallback method will return an object that implements the IDisposable interface of the type. Do not forget to call its Dispose method to avoid Memory leakage.
1: public class Program
2: {
3: private static IDisposable callbackRegistration;
4: private static void OnSettingChanged(object state)
5: {
6: callbackRegistration?.Dispose();
7: IConfiguration configuration = (IConfiguration)state;
8: Console.WriteLine(configuration.Get<ThreadPoolSettings>());
9: callbackRegistration = configuration.GetReloadToken()
10: .RegisterChangeCallback(OnSettingChanged, state);
11: }
12: }
Configuration of ASP. NET Core (1): Read configuration information
Configuration of ASP. NET Core (2): Detailed Configuration Model
ASP. NET Core configuration (3): bind the configuration to an object [Part 1]
ASP. NET Core configuration (3): bind the configuration to an object [Part 2]
Configuration of ASP. NET Core (4): diverse configuration sources [Part 1]
Configuration of ASP. NET Core (4): diverse configuration sources [Part 1]
Configuration of ASP. NET Core (4): diverse configuration sources [Part II]
Configuration of ASP. NET Core (5): configuration synchronization [Part 1]
Configuration of ASP. NET Core (5): configuration synchronization [Part II]
Reference page: http://qingqingquege.cnblogs.com/p/5933752.html