.NET Core 3.0之深入源码理解Host(二)

写在前面

停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了。本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Program的创建与守护。

关于Host,我们最容易想到的就是程序的启动与停止,而其中隐藏着非常关键的功能,就是Host的初始化,我们所需要的所有资源都必须而且应该在程序启动过程中初始化完成,当然本文的主要内容并不是Host初始化,前文已经累述。当然,为了更好的守护与管理已经启动的Host,.NET Core 3.0将程序的生命周期事件的订阅开放给开发者,当然也包括自定义的Host Service对象。

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

.NET Core 3.0中创建Long Run Program

IHost与IHostBuilder

当我们创建Long Run Program时,会首先关注程序的启动与停止,.NET Core 3.0为此提供了一个接口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:  }

该接口含有一个只读属性:IServiceProvider Services { get; },通过该属性,我们可以拿到所有Host初始化时所注入的对象信息。

IHostBuilder接口所承担的核心功能就是程序的初始化,通过:IHost Build()来完成,当然只需要运行一次即可。其初始化内容一般包括以下几个功能:

另外需要说明的是,以上功能的初始化,是通过IHostBuilder提供的接口获取用户输入的信息后,通过调用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对象,那么我们如何自定义呢,其实很简单只需要实现IHostService,并在ConfigureServices中调用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:  }

根据源码我们可以知道,该接口只有两个方法,即代码程序开始与停止的方法。具体的实现可以参考如下:

   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管理对象的入口,所有需要压入到Host托管的对象都必须要实现此接口。

Host生命周期的管理

该接口提供了一种我们可以在程序运行期间进行管理的功能,如程序的启动与停止事件的订阅,关于Host生命周期的管理,主要由IHostApplicationLifetime和IHostLifetime这两个接口来完成。

以下是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:  }

总结

至此,我们知道了创建Long Run Program所需要关注的几个点,分别是继承IHostService、订阅程序的生命周期时间以及Host的初始化过程。相对来说这段内容还是比较简单的,但是开发过程中,依然会遇到很多的问题,比如任务的定时机制、消息的接入、以及程序的性能优化等等,这些都需要我们在实践中进一步总结完善。

原文地址:https://www.cnblogs.com/edison0621/p/11520497.html

时间: 2024-11-08 18:48:02

.NET Core 3.0之深入源码理解Host(二)的相关文章

.NET Core 3.0之深入源码理解Configuration(二)

原文:.NET Core 3.0之深入源码理解Configuration(二) 文件型配置基本内容 上一篇文章讨论了Configuration的几个核心对象,本文继续讨论Configuration中关于文件型配置的相关内容.相比较而言,文件型配置的使用场景更加广泛,用户自定义配置扩展也可以基于文件型配置进行扩展.如果需要查看上一篇文章,可以点击移步. .NET Core文件型配置中我们提供了三种主要的实现,分别是JSON.XML.INI,请查看下图 由图可知,这三种配置的实现方式是一样的,当然了

.NET Core 3.0之深入源码理解Startup的注册及运行

原文:.NET Core 3.0之深入源码理解Startup的注册及运行 写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程序的起点.通过使用Startup,可以配置化处理所有向应用程序所做的请求的管道,同时也可以减少.NET应用程序对单一服务器的依赖性,使我们在更大程度上专注于面向多服务器为中心的开发模式. 目录: Startup讨论 Starup所承担的角色 Startup编写规范 ConfigureServices C

.NET Core 3.0之深入源码理解Configuration(一)

原文:.NET Core 3.0之深入源码理解Configuration(一) Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并会向其添加Provider,在我们使用配置信息的时候,会从内存中获取相应的Provider实例. .NET Core采用了统一的调用方式来加载不同类型的配置信息,并通过统一的抽象接口IConfigurationSourc

.NET Core 3.0之深入源码理解Configuration(三)

写在前面 上一篇文章讨论了文件型配置的基本内容,本篇内容讨论JSON型配置的实现方式,理解了这一种配置类型的实现方式,那么其他类型的配置实现方式基本可以触类旁通.看过了上一篇文章的朋友,应该看得出来似曾相识.此图主要表达了文件型配置的实现,当然其他配置,包括自定义配置,都会按照这样的方式去实现. JSON配置组件的相关内容 该组件有四个类 JsonConfigurationExtensions JsonConfigurationSource JsonConfigurationFileParser

Android7.0 Phone应用源码分析(二) phone来电流程分析

接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有来电通知时,首先接收到消息的是Modem层,然后Medoem再上传给RIL层,RIL进程通过sokcet将消息发送给RILJ(framework层的RIL),同样进入RILJ的processResponse方法,根据上一章节去电流程的分析得知,来电属于UnSolicited消息,事件ID是 RIL_

Android7.0 Phone应用源码分析(三) phone拒接流程分析

接上篇博文:Android7.0 Phone应用源码分析(二) phone来电流程分析 今天我们再来分析下Android7.0 的phone的拒接流程 下面先来看一下拒接电话流程时序图 步骤1:滑动按钮到拒接图标,会调用到AnswerFragment的onDecline方法 com.android.incallui.AnswerFragment public void onDecline(Context context) { getPresenter().onDecline(context);

【春华秋实】深入源码理解.NET Core中Startup的注册及运行

原文:[春华秋实]深入源码理解.NET Core中Startup的注册及运行 写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程序的起点.通过使用Startup,可以配置化处理所有向应用程序所做的请求的管道,同时也可以减少.NET应用程序对单一服务器的依赖性,使我们在更大程度上专注于面向多服务器为中心的开发模式. 目录: Startup讨论 Starup所承担的角色 Startup编写规范 ConfigureServices

Android # 4.0.x(1-3) 源码 下载 编译

Android 4.0源码下载方法:repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1 官方下载页面:http://source.android.com/source/downloading.html Android SDK 4.0官方下载页面:http://developer.android.com/sdk/android-4.0.html android 4.0.3最新源码下载

eclipse 编译tomcat8.0.26的源码

第一次写东西, 如果有不对的地方,请大神指正,我会尽快修正…… 参考:http://www.cnblogs.com/lanxuezaipiao/p/3640923.html 1.从tomcat官网(http://tomcat.apache.org/)下载 源码 apache-tomcat-8.0.26-src.zip . 2.下载项目构建工具 ant (http://ant.apache.org/).解压后, 配置环境变量,并修改path值. 例如,ANT_HOME="D:\Program Fi