• KestrelServer


    KestrelServer

    跨平台是ASP.NET Core一个显著的特性,而KestrelServer是目前微软推出了唯一一个能够真正跨平台的Server。KestrelServer利用一个名为KestrelEngine的网络引擎实现对请求的监听、接收和响应。KetrelServer之所以具有跨平台的特质,源于KestrelEngine是在一个名为libuv的跨平台网络库上开发的。

    目录
    一、libuv
    二、KestrelServer
    三、KestrelServerOptions
    四、ApplicationLifetime
    五、设置监听地址

    一、libuv

    image说起libuv,就不得不谈谈libev,后者是Unix系统上一个事件循环和事件模型的网络库。libev因其具有的高性能成为了继lievent和Event perl module之后一套最受欢迎的网络库。由于Libev不支持Windows,有人在libev之上创建了一个抽象层以屏蔽平台之间的差异,这个抽象层就是libuv。libuv在Windows平台上是采用IOCP的形式实现的,右图揭示了libuv针对Unix和Windows的跨平台实现原理。到目前为止,libuv支持的平台已经不限于Unix和Windows了,包括Linux(2.6)、MacOS和Solaris (121以及之后的版本)在内的平台在libuv支持范围之内。

    二、KestrelServer

    如下所示的代码片段体现了KestrelServer这个类型的定义。除了实现接口IServer定义的Features属性之外,KestrelServer还具有一个类型为KestrelServerOptions的只读属性Options。这个属性表示对KestrelServer所作的相关设置,我们在调用构造函数时通过输入参数options所代表的IOptions<KestrelServerOptions>对象对这个属性进行初始化。构造函数还具有另两个额外的参数,它们的类型分别是IApplicationLifetime和ILoggerFactory,后者用于创建记录日志的Logger,前者与应用的生命周期管理有关。

       1: public class KestrelServer : IServer
       2: {   
       3:     public IFeatureCollection       Features { get; }
       4:     public KestrelServerOptions     Options { get; }
       5:  
       6:     public KestrelServer(IOptions<KestrelServerOptions> options,IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory);
       7:     public void Dispose();
       8:     public void Start<TContext>(IHttpApplication<TContext> application);
       9: }

    我们一般通过调用WebHostBuilder的扩展方法UseKestrel方法来完成对KestrelServer的注册。如下面的代码片段所示,UseKestrel方法具有两个重载,其中一个具有同一个类型为Action<KestrelServerOptions>的参数,我们可以利用这个参数直接完成对KestrelServerOptions的设置。

       1: public static class WebHostBuilderKestrelExtensions
       2: {
       3:     public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder);
       4:     public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<KestrelServerOptions> options);
       5: }


    三、KestrelServerOptions

    由于Server负责请求的监听、接收和响应,所以Server是影响整个Web应用响应能力和吞吐量最大的因素之一,为了更加有效地使用Server,我们往往针对具体的网络负载状况对其作针对性的设置。对于KestrelServer来说,在构造函数中作为参数指定的KestrelServerOptions对象代表针对它所做的设置。我们针对KestrelServer所做的设置主要体现在KestrelServerOptions类型的如下5个属性上。

       1: public class KestrelServerOptions
       2: {   
       3:     //省略其他成员
       4:     public int          MaxPooledHeaders { get; set; }
       5:     public int          MaxPooledStreams { get; set; }
       6:     public bool         NoDelay { get; set; }
       7:     public TimeSpan     ShutdownTimeout { get; set; }
       8:     public int          ThreadCount { get; set; }
       9: }

    KestrelServerOptions注册的KetrelServer在管道中会以依赖注入的方式被创建,并采用构造器注入的方式提供其构造函数的参数options,由于这个参数类型为IOptions<KestrelServerOptions>,所以我们利用Options模型以配置的方式来指定KestrelServerOptions对象承载的设置。比如我们可以将KestrelServer的相关配置定义在如下一个JSON文件中。

       1: { 
       2:     "noDelay"         : false, 
       3:     "shutdownTimeout" : "00:00:10", 
       4:     "threadCount"     : 10 
       5: } 

    为了让应用加载这么一个配置文件(文件名假设为“KestrelServerOptions.json”),我们只需要在启动类型(Startup)类的ConfigureServces方法中按照如下的方式利用ConfigurationBuilder加载这个配置文件并生成相应的Configuration对象,最后按照Options模型的编程方式完成KestrelServerOptions类型和该对象的映射即可。

       1: public class Startup
       2: {
       3:     //其他成员
       4:     public void ConfigureServices(IServiceCollection services)
       5:     {
       6:         IConfiguration configuration = new ConfigurationBuilder()
       7:             .AddJsonFile("KestrelServerOptions.json")
       8:             .Build();
       9:         services.Configure<KestrelServerOptions>(configuration);
      10:     }
      11: }


    四、ApplicationLifetime

    我们将所有实现了IApplicationLifetime接口的所有类型及其对应对象统称为ApplicationLifetime。从命名的角度来看,ApplicationLifetime貌似是对当前应用生命周期的描述,而实际上它存在的目的仅仅是在应用启动和关闭(只要是关闭)时对相关组件发送通知而已。如下面的代码片段所示,IApplicationLifetime接口具有三个CancellationToken类型的属性(ApplicationStarted、ApplicationStopping和ApplicationStopped),我们可以利用它们是否已经被取消(Cancel)确定当前应用的状态(已经开启、正在关闭和已经关闭)。如果试图关闭应用,StopApplication方法应该被调用以发出应用正在被关闭的通知。对于KestrelServer来说,如果请求处理线程中发生未被处理异常,它会调用这个方法

       1: public interface IApplicationLifetime
       2: {
       3:     CancellationToken ApplicationStarted { get; }
       4:     CancellationToken ApplicationStopping { get; }
       5:     CancellationToken ApplicationStopped { get; }
       6:  
       7:     void StopApplication();
       8: }

    ASP.NET Core默认使用的ApplicationLifetime是具有如下定义的一个同名类型。可以看出它实现的三个属性返回的CancellationToken对象是通过三个对应的CancellationTokenSource生成。除了实现IApplicationLifetime接口的StopApplication方法用于发送“正在关闭”通知之外,这个类型还定义了额外两个方法(NotifyStarted和NotifyStopped)用于发送“已经开启/关闭”的通知。

       1: public class ApplicationLifetime : IApplicationLifetime
       2: {
       3:     private readonly CancellationTokenSource _startedSource = new CancellationTokenSource();
       4:     private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource();
       5:     private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource();    
       6:  
       7:     public CancellationToken ApplicationStarted
       8:     {
       9:         get { return this._startedSource.Token; }
      10:     }
      11:     public CancellationToken ApplicationStopped
      12:     {
      13:         get { return this._stoppedSource.Token; }
      14:     }
      15:     public CancellationToken ApplicationStopping
      16:     {
      17:         get { return this._stoppingSource.Token; }
      18: }
      19:  
      20:     public void NotifyStarted()
      21:     {
      22:         this._startedSource.Cancel(false);
      23:     }
      24:     public void NotifyStopped()
      25:     {
      26:         this._stoppedSource.Cancel(false);
      27:     }
      28:     public void StopApplication()
      29:     {
      30:         this._stoppingSource.Cancel(false);
      31:     }
      32: }

    一个ASP.NET Core应用利用管道处理请求,所以管道的生命周期等同于应用自身的生命周期。当我们调用Run方法开启WebHost时,请求处理管道被构建出来。如果管道在处理请求时发生未被处理的异常,管道的Sever会调用ApplicationLifeTime对象的StopApplication方法向WebHost发送关闭应用的通知以便后者执行一些回收释放工作

    五、设置监听地址

    在演示的实例中,我们实际上并不曾为注册的KestrelServer指定一个监听地址,从运行的效果我们不难看出,WebHost在这种情况下会指定“http://localhost:5000”为默认的监听地址,Server的监听地址自然可以显式指定。在介绍如何通过编程的方式为Server指定监听地址之前,我们有先来认识一个名为ServerAddressesFeature的特性。

    我们知道表示Server的接口IServer中定义了一个类型为IFeatureCollection 的只读属性Features,它表示用于描述当前Server的特性集合,ServerAddressesFeature作为一个重要的特性,就包含在这个集合之中。我们所说的ServerAddressesFeature对象是对所有实现了IServerAddressesFeature接口的所有类型及其对应对象的统称,该接口具有一个唯一的只读属性返回Server的监听地址列表。ASP.NET Core默认使用的ServerAddressesFeature是具有如下定义的同名类型。

       1: public interface IServerAddressesFeature
       2: {
       3:     ICollection<string> Addresses { get; }
       4: }
       5:  
       6: public class ServerAddressesFeature : IServerAddressesFeature
       7: {
       8:     public ICollection<string> Addresses { get; }
       9: }

    对于WebHost在通过依赖注入的方式创建的Server,由它的Features属性表示的特性集合中会默认包含这么一个ServerAddressesFeature对象。如果没有一个合法的监听地址被添加到这个 ServerAddressesFeature对象的地址列表中,WebHost会将显式指定的地址(一个或者多个)添加到该列表中。我们显式指定的监听地址实际上是作为WebHost的配置保存在一个Configuration对象上,配置项对应的Key为“server.urls”,WebHostDefaults的静态只读属性ServerUrlsKey返回的就是这么一个Key。

       1: new WebHostBuilder()
       2:     .UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:3721/")
       3:     .UseMyKestrel()
       4:     .UseStartup<Startup>()
       5:     .Build()
       6:      .Run();

    WebHost的配置最初来源于创建它的WebHostBuilder,后者提供了一个UseSettings方法来设置某个配置项的值,所以我们可以采用如下的方式来指定监听地址(“http://localhost:3721/”)。不过,针对监听地址的显式设置,最直接的编程方式还是调用WebHostBuilder的扩展方法UseUrls,如下面的代码片段所示,该方法的实现逻辑与上面完全一致。

       1: public static class WebHostBuilderExtensions
       2: {
       3:     public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls) 
       4:     =>hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls)) ;    
       5: }
    作者:蒋金楠 
    微信公众账号:大内老A
    微博:www.weibo.com/artech
  • 相关阅读:
    CF1477F
    UR2 树上 GCD
    CF1491
    碎碎念——Nothing is but what is not
    理希的高考前胡诌
    理希的OI记——补集
    老年人的赛前康复计划
    react中受控组件、非受控组件、纯函数、高阶函数、高阶组件。
    react当中refs
    react当中Props
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5602373.html
Copyright © 2020-2023  润新知