如何远程关闭一个ASP.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...

时间: 2024-12-13 01:16:27

如何远程关闭一个ASP.NET Core应用?的相关文章

Kubernetes初探[1]:部署你的第一个ASP.NET Core应用到k8s集群

转自:https://www.cnblogs.com/RainingNight/p/first-aspnetcore-app-in-k8s.html Kubernetes简介 Kubernetes是Google基于Borg开源的容器编排调度引擎,作为CNCF(Cloud Native Computing Foundation)最重要的组件之一,它的目标不仅仅是一个编排系统,而是提供一个规范,可以让你来描述集群的架构,定义服务的最终状态,Kubernetes可以帮你将系统自动得达到和维持在这个状态

使用Visual Studio Code创建第一个ASP.NET Core应用程序

全文翻译自:Your First ASP.NET Core Application on a Mac Using Visual Studio Code 这篇文章将向你展示如何在Mac上写出你的第一个ASP.NET Core应用程序. 本文内容: 搭建开发环境 使用Yeoman搭建应用程序 使用Visual Studio Core开发ASP.NET应用程序 使用Kestrel在本地运行这个应用程序 将这个应用程序发布至Azure 学习资源 搭建开发环境 在你的开发机上下载并安装.Net Core和

记一个ASP.Net Core配置文件问题

最近排查一个ASP.Net Core项目的Bug,用LogInformation()记录一些运行日志,本地测试日志记录正常,然后发到RC环境测试,结果发现死活没有日志信息. 首先想到就是LogLevel设置有问题.检查了基础的配置文件(appsettings.json)没有问题,而RC环境的配置文件(appsettings.RC.json)未配置Logging节点,也就不会覆盖. 1 "Logging": { 2 "IncludeScopes": false, 3

从零写一个Asp.net core手脚架(模型验证)

一个asp.net core项目,一定包含了各种的实体,在RESTful api里面,有很多的参数传递,不建立实体则大量的参数需要自定验证正确性,并且Action上面会写的密密麻麻的参数 在asp.net 2.0的时候,就推出了ModelState,顾名思义,这个是模型状态,用于验证实体对象的 如何模型验证 用法是在需要验证的地方打上继承了ValidationAttribute的特性,比如常见的RequiredAttribute,这个是验证对象是否存在 /// <summary> /// Ad

【翻译】在Mac上使用VSCode创建你的第一个Asp.Net Core应用

Setting Up Your Development Environment 设置你的开发环境 To setup your development machine download and install .NET Core and Visual Studio Code with the C# extension. Node.js and npm is also required. If not already installed visit nodejs.org. 首先在你的机器上下载.Ne

创建第一个ASP.NET CORE程序 使用DBFirst模式连接数据库

第一步: 第二步: 第三步: 第四步:在控制台输入命令 (1)Install-Package Microsoft.EntityFrameworkCore (2)Install-Package Microsoft.EntityFrameworkCore.SqlServer (3)Install-Package Microsoft.EntityFrameworkCore.Tools (4)Install-Package Microsoft.EntityFrameworkCore.SqlServer.

利用一个ASP.NET Core应用来发布静态文件

虽然ASP.NET Core是一款"动态"的Web服务端框架,但是在很多情况下都需要处理针对静态文件的请求,最为常见的就是这对JavaScript脚本文件.CSS样式文件和图片文件的请求.针对不同格式的静态文件请求的处理,ASP.NET Core为我们提供了三个中间件,它们将是本系列文章论述的重点.不过在针对对它们展开介绍之前,我们照理通过一些简单的实例来体验一下如何在一个ASP.NET Core应用中发布静态文件.[本文已经同步到<ASP.NET Core框架揭秘>之中]

用VSCode开发一个asp.net core 2.0+angular 5项目(4): Angular5全局错误处理

第一部分: http://www.cnblogs.com/cgzl/p/8478993.html 第二部分: http://www.cnblogs.com/cgzl/p/8481825.html 第三部分: https://www.cnblogs.com/cgzl/p/8525541.html 这篇文章将介绍angular 5的全局错误处理. 需要使用到代码: https://pan.baidu.com/s/1F0KjbwVE8_Tzfwy69Alp-A angular 5 全局错误处理 参考文

从零写一个Asp.net core手脚架 (异常处理)

既然是手脚架,那么肯定得明白,手脚架是有限资源的一个整合,我们尽可能完善它,并保留可扩展性才是最终目的,尽可能减少硬编码,让业务不满足的情况下,可以自行修改 我们把解决方案取名Asp.netCoreTemplate,项目取名Core.Template Core.Template顾名思义,核心的模板,并不是我们最终生成项目的手脚架 手脚架叫Core.Template.CLI 构建处理异常的中间件 我们创建一个Middleware文件夹,单击右键新建项->中间件类 取名ExceptionMiddle