1. 程式人生 > >如何遠端關閉一個ASP.NET Core應用?

如何遠端關閉一個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次請求與響應
            
           

相關推薦

如何遠端關閉一個ASP.NET Core應用

在《歷數依賴注入的N種玩法》演示系統自動註冊服務的例項中,我們會發現輸出的列表包含兩個特殊的服務,它們的對應的服務介面分別是IApplicationLifetime和IHostingEnvironment,我們將分別實現這兩個介面的服務統稱在ApplicationLifetime和HostingEnviron

ASP.NET Core中的快取[1]:如何在一個ASP.NET Core應用中使用快取

.NET Core針對快取提供了很好的支援 ,我們不僅可以選擇將資料快取在應用程序自身的記憶體中,還可以採用分散式的形式將快取資料儲存在一個“中心資料庫”中。對於分散式快取,.NET Core提供了針對Redis和SQL Server的原生支援。除了這個獨立的快取系統之外,ASP.NET Core還藉助一箇中

Kubernetes初探[1]:部署你的第一個ASP.NET Core應用到k8s叢集

Kubernetes簡介 Kubernetes是Google基於Borg開源的容器編排排程引擎,作為CNCF(Cloud Native Computing Foundation)最重要的元件之一,它的目標不僅僅是一個編排系統,而是提供一個規範,可以讓你來描述叢集的架構,定義服務的最終狀態,Kubernete

Linux使用Jexus托管Asp.Net Core應用程序

技術 文件目錄 只需要 true 沒有 repr tag 博文 env 第一步 安裝.Net Core環境 安裝 dotnet 環境參見官方網站 https://www.microsoft.com/net/core。 選擇對應的系統版本進行安裝。安裝完成過後 輸入命令查

在 Azure WebApps 中運行64位 Asp.net Core 應用

需求 正常 mmu www. module .config 正在 external doc 作為微軟下一代的開源的跨平臺的開發框架, Asp.net core 正在吸引越來越多的開發者基於其構建現代 web 應用。 目前, Azure App Service 也實現了對 a

運行Vue在ASP.NET Core應用程序並部署在IIS上

生產環境 所在 來講 一個 重寫 文章 .net core 設置 分享 前言 從.NET Core 1.0開始我們就將其應用到項目中,但是呢我對ASP.NET Core一些原理也還未開始研究,僅限於會用,不過園子中已有大量文章存在,借著有點空余時間,我們來講講如何利用AS

ASP.NET Core應用程序部署至生產環境中(CentOS7)

for linux home web 虛擬 direct director block bic 閱讀目錄 環境說明 準備你的ASP.NET Core應用程序 安裝CentOS7 安裝.NET Core SDK for CentOS7。 部署ASP.NET

使用Docker部署ASP.NET Core應用程序實踐

4.0 cor run .com cnblogs pda word 本地配置 問題 前言 最近把很火的Docker給看了,於是就磨拳擦掌要去實踐一下。於是就拿之前一個aps.net core的項目(已被停止)去練手。該項目之前在ubuntu14.04上確保可以正常運行,所以

在 Docker 中部署 ASP.NET CORE 應用

post netcore 工作 ros core 指定 們的 本地 body 有了 Docker 之後, 部署起來卻這間非常方便,環境不用搭了, 直接創建一個 microsoft/aspnetcore 的容器, 在本地開發好後, 把內容直接部署到容器中。 下面的命令是把本

用VSCode開發一個asp.net core 2.0+angular 5項目(4): Angular5全局錯誤處理

create 打印 事件 如果 log 異步操作 truct gin 按鈕 第一部分: http://www.cnblogs.com/cgzl/p/8478993.html 第二部分: http://www.cnblogs.com/cgzl/p/8481825.html

Docker打包 Asp.Net Core應用,在CentOS上運行(轉)

ner 表示 exec 但是 服務端 名稱 pro 目前 app 轉載連接:https://www.cnblogs.com/ibeisha/archive/2017/09/09/netcoreondocker.html 本文主要介紹下運用docker虛擬技術打包Asp.n

ASP.NET Core應用錯誤處理

最近在學習蔣金楠的ASP.NET Core 系列 真的是長知識了,博主地址:https://www.cnblogs.com/artech 一、顯示開發者異常頁面 由於ASP.NET Core應用是面向多個客戶端請求,如果一個錯誤,它並不會影響整個應用的終止,出於安全的考慮,客戶端在預設情況下應該得不到報錯

一個ASP.Net Core配置文件問題

lap 測試日誌 png 官方 pub var ast pac spa   最近排查一個ASP.Net Core項目的Bug,用LogInformation()記錄一些運行日誌,本地測試日誌記錄正常,然後發到RC環境測試,結果發現死活沒有日誌信息。   首先想到就是LogL

一個ASP.Net Core配置檔案問題

  最近排查一個ASP.Net Core專案的Bug,用LogInformation()記錄一些執行日誌,本地測試日誌記錄正常,然後發到RC環境測試,結果發現死活沒有日誌資訊。   首先想到就是LogLevel設定有問題。檢查了基礎的配置檔案(appsettings.json)沒有問題,而RC環境的配置檔案

Asp.net core實戰4: 建立你的第一個Asp.net core專案

我們劃分為四個步驟建立你的第一個專案: 1.根據官方提供的模板生成一個專案 2.使用NuGet修復所缺少的第三方庫 3.Build你的專案 4.Run你的專案   第一步:建立專案(請自行下載安裝Asp.net core SDK 2.0及Visual Studio2

【轉】CentOS 7部署ASP.NET Core應用程式

很早就看過關於net core部署在Linux上的文章,自己也曾親自將專案部署在Linux上,今天看到這篇文章,為其格式之工整而轉! 1.環境準備 網上看了一下,Linux雲伺服器還挺貴的,那就只好先用VMware虛擬機器搭建個吧。這裡我選裝的Linux系統版本的是CentOS,Linux系統眾多發行版之

ASP.NET Core 應用程式Startup類介紹 (轉載)

Startup類配置服務和應用程式的請求管道。     Startup 類 ASP.NET Core應用程式需要一個啟動類,按照慣例命名為Startup。在主程式的Web Host生成器(WebHostBuilderExtensions)的 UseStartup <TSt

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式

由於ASP.NET Core應用是一個同時處理多個請求的伺服器應用,所以在處理某個請求過程中丟擲的異常並不會導致整個應用的終止。出於安全方面的考量,為了避免敏感資訊的外洩,客戶端在預設的情況下並不會得到詳細的出錯資訊,這無疑會在開發環境下增加查錯糾錯的難度。對於生產環境來說,我們也希望終端使用者能夠根據具體的

ASP.NET Core應用針對靜態檔案請求的處理[3]: StaticFileMiddleware中介軟體如何處理針對檔案請求

我們通過《以Web的形式釋出靜態檔案》和《條件請求與區間請求》中的例項演示,以及上面針對條件請求和區間請求的介紹,從提供的功能和特性的角度對這個名為StaticFileMiddleware的中間進行了全面的介紹,接下來我們將更近一步,將從實現原理的角度來進一步認識這個中介軟體。 [本文已經同步到《ASP.NE

ASP.NET Core應用的錯誤處理[4]:StatusCodePagesMiddleware中介軟體如何針對響應碼呈現錯誤頁面

StatusCodePagesMiddleware中介軟體與ExceptionHandlerMiddleware中介軟體比較類似,它們都是在後續請求處理過程中“出錯”的情況下利用一個錯誤處理器來完成最終的請求處理與響應的任務。它們之間的差異在於對“錯誤”的界定上,對於ExceptionHandlerMiddl