?
          寫在前面

          停了近一個(gè)月的技術(shù)博客,隨著正式脫離996的魔窟,接下來也正式恢復(fù)了。本文從源碼角度進(jìn)一步討論.NET Core 3.0
          中關(guān)于Host擴(kuò)展的一些技術(shù)點(diǎn),主要討論Long Run Program的創(chuàng)建與守護(hù)。


          關(guān)于Host,我們最容易想到的就是程序的啟動(dòng)與停止,而其中隱藏著非常關(guān)鍵的功能,就是Host的初始化,我們所需要的所有資源都必須而且應(yīng)該在程序啟動(dòng)過程中初始化完成,當(dāng)然本文的主要內(nèi)容并不是Host初始化,前文已經(jīng)累述。當(dāng)然,為了更好的守護(hù)與管理已經(jīng)啟動(dòng)的Host,.NET
          Core 3.0將程序的生命周期事件的訂閱開放給開發(fā)者,當(dāng)然也包括自定義的Host Service對(duì)象。

          注:本文代碼基于.NET Core 3.0 Preview9


          <https://img2018.cnblogs.com/blog/533598/201909/533598-20190914220234020-1611831647.png>

          .NET Core 3.0中創(chuàng)建Long Run Program

          IHost與IHostBuilder

          當(dāng)我們創(chuàng)建Long Run Program時(shí),會(huì)首先關(guān)注程序的啟動(dòng)與停止,.NET Core
          3.0為此提供了一個(gè)接口IHost,該接口位于Microsoft.Extensions.Hosting類庫中,其源碼如下:
          1: /// <summary> 2: /// A program abstraction. 3: /// </summary> 4: public
          interface IHost : IDisposable 5: { 6: /// <summary> 7: /// The programs
          configured services. 8: /// </summary> 9: IServiceProvider Services { get; }
          10:? 11: /// <summary> 12: /// Start the program. 13: /// </summary> 14:
          /// <param name="cancellationToken">Used to abort program start.</param> 15:
          /// <returns>A <see cref="Task"/> that will be completed when the <see
          cref="IHost"/> starts.</returns> 16: Task StartAsync(CancellationToken
          cancellationToken =default); 17: ? 18: /// <summary> 19: /// Attempts to
          gracefully stop the program. 20: /// </summary> 21: /// <param
          name="cancellationToken">Used to indicate when stop should no longer be
          graceful.</param> 22: /// <returns>A <see cref="Task"/> that will be completed
          when the <see cref="IHost"/> stops.</returns> 23: Task
          StopAsync(CancellationToken cancellationToken =default); 24: }
          該接口含有一個(gè)只讀屬性:IServiceProvider Services { get; },通過該屬性,我們可以拿到所有Host初始化時(shí)所注入的對(duì)象信息。

          IHostBuilder接口所承擔(dān)的核心功能就是程序的初始化,通過:IHost
          Build()來完成,當(dāng)然只需要運(yùn)行一次即可。其初始化內(nèi)容一般包括以下幾個(gè)功能:


          <https://img2018.cnblogs.com/blog/533598/201909/533598-20190914220234654-1227722267.png>


          另外需要說明的是,以上功能的初始化,是通過IHostBuilder提供的接口獲取用戶輸入的信息后,通過調(diào)用Build()方法來完成初始化。以下為IHostBuilder的部分源代碼:
          1: /// <summary> 2: /// Set up the configuration for the builder itself.
          This will be used to initialize the <see cref="IHostEnvironment"/> 3: /// for
          use later in the build process. This can be called multiple times and the
          results will be additive. 4: /// </summary> 5: /// <param
          name="configureDelegate">The delegate for configuring the <see
          cref="IConfigurationBuilder"/> that will be used 6: /// to construct the <see
          cref="IConfiguration"/> for the host.</param> 7: /// <returns>The same
          instance of the <see cref="IHostBuilder"/> for chaining.</returns> 8: public
          IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder>
          configureDelegate) 9: { 10:
          _configureHostConfigActions.Add(configureDelegate ??throw new
          ArgumentNullException(nameof(configureDelegate))); 11: return this; 12: } 13:
          ? 14: /// <summary> 15: /// Adds services to the container. This can be
          called multiple times and the results will be additive. 16: /// </summary> 17:
          /// <param name="configureDelegate">The delegate for configuring the <see
          cref="IConfigurationBuilder"/> that will be used 18: /// to construct the <see
          cref="IConfiguration"/> for the host.</param> 19: /// <returns>The same
          instance of the <see cref="IHostBuilder"/> for chaining.</returns> 20: public
          IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection>
          configureDelegate) 21: { 22: _configureServicesActions.Add(configureDelegate
          ??throw new ArgumentNullException(nameof(configureDelegate))); 23: return this;
          24:} 25: ? 26: /// <summary> 27: /// Overrides the factory used to create
          the service provider. 28: /// </summary> 29: /// <typeparam
          name="TContainerBuilder">The type of the builder to create.</typeparam> 30:
          /// <param name="factory">A factory used for creating service providers.</param>
          31:/// <returns>The same instance of the <see cref="IHostBuilder"/> for
          chaining.</returns> 32: public IHostBuilder
          UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder>
          factory) 33: { 34: _serviceProviderFactory = new
          ServiceFactoryAdapter<TContainerBuilder>(factory ??throw new
          ArgumentNullException(nameof(factory))); 35: return this; 36: } 37: ? 38:
          /// <summary> 39: /// Enables configuring the instantiated dependency
          container. This can be called multiple times and 40: /// the results will be
          additive. 41: /// </summary> 42: /// <typeparam name="TContainerBuilder">The
          type of the builder to create.</typeparam> 43: /// <param
          name="configureDelegate">The delegate for configuring the <see
          cref="IConfigurationBuilder"/> that will be used 44: /// to construct the <see
          cref="IConfiguration"/> for the host.</param> 45: /// <returns>The same
          instance of the <see cref="IHostBuilder"/> for chaining.</returns> 46: public
          IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext,
          TContainerBuilder> configureDelegate) 47: { 48:
          _configureContainerActions.Add(new
          ConfigureContainerAdapter<TContainerBuilder>(configureDelegate 49: ?? throw
          new ArgumentNullException(nameof(configureDelegate)))); 50: return this; 51: }
          IHostService

          文章開頭有說過自定義Host
          Service對(duì)象,那么我們?nèi)绾巫远x呢,其實(shí)很簡(jiǎn)單只需要實(shí)現(xiàn)IHostService,并在ConfigureServices中調(diào)用services.AddHostedService<MyServiceA>()即可,以下是IHostService的源碼:
          1: /// <summary> 2: /// Defines methods for objects that are managed by the
          host. 3: /// </summary> 4: public interface IHostedService 5: { 6: ///
          <summary> 7: /// Triggered when the application host is ready to start the
          service. 8: /// </summary> 9: /// <param name="cancellationToken">Indicates
          that the start process has been aborted.</param> 10: Task
          StartAsync(CancellationToken cancellationToken); 11: ? 12: /// <summary> 13:
          /// Triggered when the application host is performing a graceful shutdown. 14:
          /// </summary> 15: /// <param name="cancellationToken">Indicates that the
          shutdown process should no longer be graceful.</param> 16: Task
          StopAsync(CancellationToken cancellationToken); 17: }
          根據(jù)源碼我們可以知道,該接口只有兩個(gè)方法,即代碼程序開始與停止的方法。具體的實(shí)現(xiàn)可以參考如下:
          1: public class MyServiceA : IHostedService, IDisposable 2: { 3: private
          bool _stopping; 4: private Task _backgroundTask; 5: ? 6: public
          MyServiceA(ILoggerFactory loggerFactory) 7: { 8: Logger =
          loggerFactory.CreateLogger<MyServiceB>(); 9: } 10: ? 11: public ILogger
          Logger { get; } 12: ? 13: public Task StartAsync(CancellationToken
          cancellationToken) 14: { 15: Logger.LogInformation("MyServiceB is starting."
          ); 16: _backgroundTask = BackgroundTask(); 17: return Task.CompletedTask;
          18: } 19: ? 20: private async Task BackgroundTask() 21: { 22: while
          (!_stopping) 23: { 24: await Task.Delay(TimeSpan.FromSeconds(7)); 25:
          Logger.LogInformation("MyServiceB is doing background work."); 26: } 27: ?
          28: Logger.LogInformation("MyServiceB background task is stopping."); 29: }
          30:? 31: public async Task StopAsync(CancellationToken cancellationToken) 32:
          { 33: Logger.LogInformation("MyServiceB is stopping."); 34: _stopping =
          true; 35: if (_backgroundTask != null) 36: { 37: // TODO: cancellation 38:
          await _backgroundTask; 39: } 40: } 41: ? 42: public void Dispose() 43:
          { 44: Logger.LogInformation("MyServiceB is disposing."); 45: } 46: }
          IHostService是我們自定義Host管理對(duì)象的入口,所有需要壓入到Host托管的對(duì)象都必須要實(shí)現(xiàn)此接口。

          Host生命周期的管理


          該接口提供了一種我們可以在程序運(yùn)行期間進(jìn)行管理的功能,如程序的啟動(dòng)與停止事件的訂閱,關(guān)于Host生命周期的管理,主要由IHostApplicationLifetime和IHostLifetime這兩個(gè)接口來完成。

          以下是IHostApplicationLifetime的源碼?
          1: public interface IHostApplicationLifetime 2: { 3: /// <summary> 4: ///
          Triggered when the application host has fully started. 5: /// </summary> 6:
          CancellationToken ApplicationStarted { get; } 7: ? 8: /// <summary> 9: ///
          Triggered when the application host is performing a graceful shutdown. 10: ///
          Shutdown will block until this event completes. 11: /// </summary> 12:
          CancellationToken ApplicationStopping { get; } 13: ? 14: /// <summary> 15:
          /// Triggered when the application host is performing a graceful shutdown. 16:
          /// Shutdown will block until this event completes. 17: /// </summary> 18:
          CancellationToken ApplicationStopped { get; } 19: ? 20: /// <summary> 21:
          /// Requests termination of the current application. 22: /// </summary> 23:
          void StopApplication(); 24: }
          IHostLifetime源碼如下:
          1: public interface IHostLifetime 2: { 3: /// <summary> 4: /// Called at
          the start of <see cref="IHost.StartAsync(CancellationToken)"/> which will wait
          until it's complete before 5: /// continuing. This can be used to delay
          startup until signaled by an external event. 6: /// </summary> 7: /// <param
          name="cancellationToken">Used to indicate when stop should no longer be
          graceful.</param> 8: /// <returns>A <see cref="Task"/>.</returns> 9: Task
          WaitForStartAsync(CancellationToken cancellationToken); 10: ? 11: ///
          <summary> 12: /// Called from <see cref="IHost.StopAsync(CancellationToken)"/>
          to indicate that the host is stopping and it's time to shut down. 13: ///
          </summary> 14: /// <param name="cancellationToken">Used to indicate when stop
          should no longer be graceful.</param> 15: /// <returns>A <see
          cref="Task"/>.</returns> 16: Task StopAsync(CancellationToken
          cancellationToken); 17: }
          具體的使用可以參考如下代碼:
          1: public class MyLifetime : IHostLifetime, IDisposable 2: { 3: .........
          4:? 5: private IHostApplicationLifetime ApplicationLifetime { get; } 6: ? 7:
          public ConsoleLifetime(IHostApplicationLifetime applicationLifetime) 8: { 9:
          ApplicationLifetime = applicationLifetime ??throw new
          ArgumentNullException(nameof(applicationLifetime)); 10: } 11: ? 12: public
          Task WaitForStartAsync(CancellationToken cancellationToken) 13: { 14:
          _applicationStartedRegistration =
          ApplicationLifetime.ApplicationStarted.Register(state => 15: { 16:
          ((ConsoleLifetime)state).OnApplicationStarted(); 17: }, 18: this); 19:
          _applicationStoppingRegistration =
          ApplicationLifetime.ApplicationStopping.Register(state => 20: { 21:
          ((ConsoleLifetime)state).OnApplicationStopping(); 22: }, 23: this); 24: ?
          25: ....... 26: ? 27: return Task.CompletedTask; 28: } 29: ? 30: private
          void OnApplicationStarted() 31: { 32: Logger.LogInformation("Application
          started. Press Ctrl+C to shut down."); 33: Logger.LogInformation("Hosting
          environment: {envName}", Environment.EnvironmentName); 34:
          Logger.LogInformation("Content root path: {contentRoot}",
          Environment.ContentRootPath); 35: } 36: ? 37: private void
          OnApplicationStopping() 38: { 39: Logger.LogInformation("Application is
          shutting down..."); 40: } 41: ? 42: ........ 43: }
          總結(jié)

          至此,我們知道了創(chuàng)建Long Run
          Program所需要關(guān)注的幾個(gè)點(diǎn),分別是繼承IHostService、訂閱程序的生命周期時(shí)間以及Host的初始化過程。相對(duì)來說這段內(nèi)容還是比較簡(jiǎn)單的,但是開發(fā)過程中,依然會(huì)遇到很多的問題,比如任務(wù)的定時(shí)機(jī)制、消息的接入、以及程序的性能優(yōu)化等等,這些都需要我們?cè)趯?shí)踐中進(jìn)一步總結(jié)完善。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                人人干人人草 | 新版中文在线官网 | 午夜黄色剧厂 | 99久久久无码国产精品无卡 | 日本全黄裸体片 |