NET Core应用?

在《历数依赖注入的N种玩法》演示系统自动注册服务的实例中,我们会发现输出的列表包含两个特殊的服务,它们的对应的服务接口分别是IApplicationLifetime和IHostingEnvironment,我们将分别实现这两个接口的服务统称在ApplicationLifetime和HostingEnvironment。我们从其命名即可以看出ApplicationLifetime与应用的声明周期有关,而HostingEnvironment则用来表示当前的执行环境,本篇文章我们着重来了解ApplicationLifetime与整个AASP.NET Core应用的生命周期有何关系。[本文已经同步到《ASP.NET Core框架揭秘》之中]

目录
一、ApplicationLifetime
二、WebHost的Run方法
三、远程关闭应用

一、ApplicationLifetime

从命名的角度来看,ApplicationLifetime貌似是对当前应用生命周期的描述,而实际上它存在的目的仅仅是在应用启动和关闭时对相关组件发送相应的信号或者通知而已。如下面的代码片段所示,IApplicationLifetime接口具有三个CancellationToken类型的属性(ApplicationStarted、ApplicationStopping和ApplicationStopped),如果需要在应用自动和终止前后执行某种操作,我们可以注册相应的回调在这三个CancellationToken对象上。除了这三个类型为CancellationToken的属性,IApplicationLifetime接口还定义了一个StopApplication方法,我们可以调用这个方法发送关闭应用的信号,并最终真正地关闭应用。

   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 _startedSource.Token; }
  10:     }
  11:     public CancellationToken ApplicationStopped
  12:     {
  13:         get { return _stoppedSource.Token; }
  14:     }
  15:     public CancellationToken ApplicationStopping
  16:     {
  17:         get { return _stoppingSource.Token; }
  18:     }
  19:  
  20:     public void NotifyStarted()
  21:     {
  22:         _startedSource.Cancel(false);
  23:     }
  24:     public void NotifyStopped()
  25:     {
  26:         _stoppedSource.Cancel(false);
  27:     }
  28:     public void StopApplication()
  29:     {
  30:         _stoppingSource.Cancel(false);
  31:     }
  32: }

当WebHost因Start方法的执行而被开启的时候,它最终会调用ApplicationLifetime的NotifyStarted方法对外发送应用被成功启动的信号。不知道读者朋友们又被注意到,WebHost仅仅定义了启动应用的Start方法,并不曾定义终止应用的Stop或者Close方法,它仅仅在Dispose方法中调用了ApplicationLifetime的StopApplication方法。

   1: public class WebHost : IWebHost
   2: {    
   3:     private ApplicationLifetime _applicationLifetime;
   4:     public IServiceProvider Services { get;}
   5:  
   6:     public void Start()
   7:     {
   8:        ...
   9:         _applicationLifetime.NotifyStarted();
  10:     }
  11:  
  12:     public void Dispose()
  13:     {
  14:         _applicationLifetime.StopApplication();
  15:         (this.Services as IDisposable)?.Dispose();
  16:         _applicationLifetime.NotifyStopped();
  17:     }
  18:     ...
  19: }

二、WebHost的Run方法

我们知道启动应用最终是通过调用作为宿主的WebHost的Start方法来完成的,但是我们之前演示的所有实例都不曾显式地调用过这个方法,我们调用的是它的扩展方法Run。毫无疑问,WebHost的Run方法肯定会调用Start方法来开启WebHost,但是除此之外,这个Run方法还有何特别之处呢?

Run方法的目的除了启动WebHost之外,它实际上会阻塞当前进程直到应用关闭。我们知道应用的关闭的意图是通过利用ApplicationLifetime发送相应信号的方式实现的,所以这个Run方法在启动WebHost的时候,会以阻塞当前线程的方式等待直至接收到这个信号。如下所示的代码片段基本上体现了这两个扩展方法Run的实现逻辑。

   1: public static class WebHostExtensions
   2: {
   3:     public static void Run(this IWebHost host)
   4:     {
   5:         using (CancellationTokenSource cts = new CancellationTokenSource())
   6:         {
   7:             //Ctrl+C: 关闭应用
   8:             Console.CancelKeyPress +=  (sender, args) =>
   9:             {
  10:                 cts.Cancel();
  11:                 args.Cancel = true;
  12:             };
  13:             host.Run(cts.Token);
  14:         }
  15:     }
  16:  
  17:     public static void Run(this IWebHost host, CancellationToken token)
  18:     {
  19:         using (host)
  20:         {
  21:             //显示应用基本信息
  22:             host.Start();
  23:             IApplicationLifetime applicationLifetime = host.Services.GetService<IApplicationLifetime>();
  24:             token.Register(state => ((IApplicationLifetime)state).StopApplication(), applicationLifetime);
  25:             applicationLifetime.ApplicationStopping.WaitHandle.WaitOne();
  26:         }
  27:     }
  28: }

上面这个代码片段还体现了另一个细节。虽然WebHost实现了IDisposable接口,原则上我们需要在关闭的时候显式地调用其Dispose方法。针对这个方法的调用非常重要,因为它的ServiceProvider只能在这个方法被调用时才能被回收释放。但是之前所有演示的实例都没有这么做,因为Run方法会自动帮助回收释放掉指定的这个WebHost。

三、远程关闭应用

既然WebHost在启动之后会利用ApplicationLifetime等待Stopping信号的发送,这就意味着组成ASP.NET Core管道的服务器和任何一个中间件都可以在适当的时候调用ApplicationLifetime的StopApplication来关闭应用。对于《服务器在管道中的“龙头”地位》介绍的KestrelServer,我们知道在构造这个对象的时候必须指定一个ApplicationLifetime对象,其根本的目的在于当发送某些无法恢复的错误时,它可以利用这个对象关闭应用。

接下来我们通过实例的方式来演示如何在一个中间件中利用这个ApplicationLifetime对象实现对应用的远程关闭,为此我们将这个中间件命名为RemoteStopMiddleware。RemoteStopMiddleware实现远程关闭应用的原理很简单,我们远程发送一个Head请求,并且在该请求中添加一个名为“Stop-Application”的报头传到希望关闭应用的意图,该中间件接收到这个请求之后会关闭应用,而响应中会添加一个“Application-Stopped”报头表明应用已经被关闭。

   1: public class RemoteStopMiddleware
   2: {
   3:     private RequestDelegate _next;
   4:     private const string     RequestHeader      = "Stop-Application";
   5:     private const string     ResponseHeader     = "Application-Stopped";
   6:  
   7:     public RemoteStopMiddleware(RequestDelegate next)
   8:     {
   9:         _next = next;
  10:     }
  11:  
  12:     public async Task Invoke(HttpContext context, IApplicationLifetime lifetime)
  13:     {
  14:         if (context.Request.Method == "HEAD" && context.Request.Headers[RequestHeader].FirstOrDefault() == "Yes")
  15:         {
  16:             context.Response.Headers.Add(ResponseHeader, "Yes");
  17:             lifetime.StopApplication();
  18:         }
  19:         else
  20:         {
  21:             await  _next(context);
  22:         }
  23:     }
  24: }

如上所示的代码片段是RemoteStopMiddleware这个中间件的完整定义,实现逻辑很简单,完全没有必要再赘言解释。我们在一个控制台应用中采用如下的程序启动一个Hello World应用,并注册此RemoteStopMiddleware中间件。在启动这个应用之后,我们借助Fiddler发送向目标地址发送三次请求,其中第一次和第三次普通的GET请求,而第二次则是为了远程关闭应用的HEAD请求。如下所示的是三次请求与响应的内容,由于应用被第二次请求关闭,所以第三次请求会返回一个状态码为502的响应。

   1: //第1次请求与响应
   2: GET http://localhost:5000/ HTTP/1.1
   3: User-Agent: Fiddler
   4: Host: localhost:5000
   5:  
   6: HTTP/1.1 200 OK
   7: Date: Sun, 06 Nov 2016 06:15:03 GMT
   8: Transfer-Encoding: chunked
   9: Server: Kestrel
  10:  
  11: Hello world!
  12:  
  13: //第2次请求与响应
  14: HEAD http://localhost:5000/ HTTP/1.1
  15: Stop-Application: Yes
  16: User-Agent: Fiddler
  17: Host: localhost:5000
  18:  
  19: HTTP/1.1 200 OK
  20: Date: Sun, 06 Nov 2016 06:15:34 GMT
  21: Server: Kestrel
  22: Application-Stopped: Yes
  23:  
  24: //第3次请求与响应
  25: GET http://localhost:5000/ HTTP/1.1
  26: User-Agent: Fiddler
  27: Host: localhost:5000
  28:  
  29: HTTP/1.1 502 Fiddler - Connection Failed
  30: Date: Sun, 06 Nov 2016 06:15:44 GMT
  31: Content-Type: text/html; charset=UTF-8
  32: Connection: close
  33: Cache-Control: no-cache, must-revalidate
  34: Timestamp: 14:15:44.790
  35:  
  36: [Fiddler] The connection to 'localhost' failed...
作者:蒋金楠 
微信公众账号:大内老A
微博:www.weibo.com/artech

NET Core应用?的更多相关文章

  1. ASP.NET Core 之 Identity 入门(一)

    前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库,负责对用户的身份进行认证,总体来说的话,没有MVC 5 里面那么复杂,因为在MVC 5里面引入了OW ...

  2. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  3. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

  4. .Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法

    在2.3中完成依赖注入后,这次主要实现栏目的添加功能.按照前面思路栏目有三种类型,常规栏目即可以添加子栏目也可以选择是否添加内容,内容又可以分文章或其他类型,所以还要添加一个模块功能.这次主要实现栏目 ...

  5. ASP.NET Core应用的错误处理[3]:ExceptionHandlerMiddleware中间件如何呈现“定制化错误页面”

    DeveloperExceptionPageMiddleware中间件利用呈现出来的错误页面实现抛出异常和当前请求的详细信息以辅助开发人员更好地进行纠错诊断工作,而ExceptionHandlerMi ...

  6. EntityFramework Core Raw SQL

    前言 本节我们来讲讲EF Core中的原始查询,目前在项目中对于简单的查询直接通过EF就可以解决,但是涉及到多表查询时为了一步到位就采用了原始查询的方式进行.下面我们一起来看看. EntityFram ...

  7. ASP.NET Core MVC/WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  8. ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”

    在<ASP.NET Core应用的错误处理[1]:三种呈现错误页面的方式>中,我们通过几个简单的实例演示了如何呈现一个错误页面,这些错误页面的呈现分别由三个对应的中间件来完成,接下来我们将 ...

  9. Asp.net Core中使用Session

    前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...

  10. ASP.NET Core 中文文档 第四章 MVC(3.8)视图中的依赖注入

    原文:Dependency injection into views 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:孟帅洋(书缘) ASP.NET Core 支持在视图中使用 依赖 ...

随机推荐

  1. 换iphone5屏幕你花了多少钱?不防我们看下市场的批发价格

    看来人家批发价也不便宜啊,你修一块花了多少米 免费b2b平台  US $1 - 79.99 / Piece Factory Price for iphone 5" lcd alibaba   ...

  2. 利用Handler访问网络数据

    废话不多白吃,代码如下: 1.MainActivity package com.yz.day11_22_handler;import android.app.Activity;import andro ...

  3. 【转】Windows的多线程编程,C/C++

    在Windows的多线程编程中,创建线程的函数主要有CreateThread和_beginthread(及_beginthreadex). CreateThread 和 ExitThread    使 ...

  4. win32 应用程序 添加资源

    一.资源 1.字符串资源 LoadString LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 二.窗口类 1.系统类 T ...

  5. [AapacheBench工具]web性能压力测试工具的应用与实践

    背景:网站性能压力测试是性能调优过程中必不可少的一环.服务器负载太大而影响程序效率是很常见的事情,一个网站到底能够承受多大的用户访问量经常是我们最关心的问题.因此,只有让服务器处在高压情况下才能真正体 ...

  6. Entity Framework做IN查询

    开发中遇到的Too high level of nesting for select错误 项目使用了Entity Framework结合Mysql, 遇到了一个非常奇怪的性能问题,一个看起来非常简单的 ...

  7. SSL协议详解

    背景介绍    最近在看<密码学与网络安全>相关的书籍,这篇文章主要详细介绍一下著名的网络安全协议SSL. 在开始SSl介绍之前,先给大家介绍几个密码学的概念和相关的知识.     1.密 ...

  8. 关于Redis info的参数总结

    Redis官网对 info 已经讲解的比较清楚的,参考文档 .可以看到,info的输出结果是分几块的,有Servers.Clients.Memory等等,通过info后面接这些参数,可以指定输出某一块 ...

  9. 在VS2010中使用Outlook工具栏

    参考资料:微软MSDN.VS2010示例代码 一开始上段子总是能活跃气氛,等哪天我再打开自己的这篇博客,总是能够让自己傻傻的乐一下. 我一女同学,毕业去一大公司应聘,竞争很激烈,最后剩下她和一位女士. ...

  10. 问题解决——SolidWorks 已停止工作 (Windows7 + SolidWorks 2010 SP0.0)

    给同事的SolidWorks解决问题时偶然间发现的. -------------------------------------------------------------- 本文原创,转载请注明 ...