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

来源:http://www.cnblogs.com/artech/archive/2016/12/05/application-life-time.html
-Advertisement-
Play Games

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


在《曆數依賴註入的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的響應。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文使用yum軟體包管理工具基於CentOS7.2版本配置apache和tom。 雲伺服器選購完畢後,安裝Xshell軟體,輸入用戶名密碼即可遠程登陸登錄(centos用戶名預設是root)。 1,下載安裝java(這裡使用的是yum軟體包管理器,他會自動配置好環境變數) yum –y instal ...
  • http://man.linuxde.net/ ...
  • 源碼環境的搭建 Install源碼 Install源碼 >sudo apt-cache search linux-source linux-source - Linux kernel source with Ubuntu patches linux-source-4.4.0 - Linux kern ...
  • 下載JProfiler包 wget http://download-keycdn.ej-technologies.com/jprofiler/jprofiler_linux_9_2.rpm 安裝JProfiler rpm -ivh jprofiler_linux_9_2.rpm 安裝JProfile ...
  • 每次搭建新伺服器,都要來來回回把這些包再裝一下,來來回回搞了不下20遍了吧,原來都是憑經驗,配置過程中重覆入坑是難免的,故寫此文做個備忘。雖然有像xampp這樣的集成包,但是在生產環境的Linux發行版上,還是通過包管理工具安裝會放心。這次新買的伺服器是CentOS 7(7.2)系統,相關配置也都以 ...
  • 系統環境:CentOS 6.6 首先查看伺服器上是否已安裝了svn # rpm -qa subversion 如果沒有安裝,則執行此命令 # yum list subversion //查看svn包名 # yum install -y subversion.x86_64 //yum安裝svn 創建s ...
  • orch的目標是在建立科學演算法的同時,要有最大的靈活性和速度,而這一過程非常簡單。Torch擁有一個大社區驅動包的生態系統,涉及機器學習、電腦視覺、信號處理、並行處理、圖像、視頻、音頻和網路等,並建立在Lua社區基礎之上。 ...
  • 當初在shell中, 看到">&1"和">&2"始終不明白什麼意思.經過在網上的搜索得以解惑.其實這是兩種輸出. 在 shell 程式中,最常使用的 FD (file descriptor) 大概有三個, 分別是: 0 是一個文件描述符,表示標準輸入(stdin)1 是一個文件描述符,表示標準輸出( ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...