借助.NET Core提供的承載(Hosting)系統,我們可以將任意一個或者多個長時間運行(Long-Running)的服務寄宿或者承載於托管進程中。ASP.NET Core應用僅僅是該承載系統的一種典型的服務類型而已,任何需要在后台長時間運行的操作都可以定義成標准化的服務並利用該系統來承載。
一、承載長時間運行服務
一個ASP.NET Core應用本質上是一個需要長時間運行的服務,開啟這個服務是為了啟動一個網絡監聽器。當監聽到抵達的HTTP請求之后,該監聽器會將請求傳遞給應用提供的管道進行處理。管道完成了對請求處理之后會生成HTTP響應,並通過監聽器返回客戶端。除了這種最典型的承載服務,我們還有很多其他的服務承載需求,下面通過一個簡單的實例來演示如何承載一個服務來收集當前執行環境的性能指標
我們演示的承載服務會定時采集並分發當前進程的性能指標。簡單起見,我們只關注處理器使用率、內存使用量和網絡吞吐量這3種典型的性能指標,為此定義了下面的PerformanceMetrics類型。我們並不會實現真正的性能指標收集,所以定義靜態方法Create利用隨機生成的指標數據創建一個PerformanceMetrics對象。
public class PerformanceMetrics { private static readonly Random _random = new Random(); public int Processor { get; set; } public long Memory { get; set; } public long Network { get; set; } public override string ToString() => $"CPU: {Processor * 100}%; Memory: {Memory / (1024 * 1024)}M; Network: {Network / (1024 * 1024)}M/s"; public static PerformanceMetrics Create() => new PerformanceMetrics { Processor = _random.Next(1, 8), Memory = _random.Next(10, 100) * 1024 * 1024, Network = _random.Next(10, 100) * 1024 * 1024 }; }
承載服務通過IHostedService接口表示,該接口定義的StartAsync方法和StopAsync方法可以啟動與關閉服務。我們將性能指標采集服務定義成如下這個實現了該接口的PerformanceMetricsCollector類型。在實現的StartAsync方法中,我們利用Timer創建了一個調度器,每隔5秒它會調用Create方法創建一個PerformanceMetrics對象,並將它承載的性能指標輸出到控制台上。這個Timer對象會在實現的StopAsync方法中被釋放。
public sealed class PerformanceMetricsCollector : IHostedService { private IDisposable _scheduler; public Task StartAsync(CancellationToken cancellationToken) { _scheduler = new Timer(Callback, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); return Task.CompletedTask; static void Callback(object state) { Console.WriteLine($"[{DateTimeOffset.Now}]{PerformanceMetrics.Create()}"); } } public Task StopAsync(CancellationToken cancellationToken) { _scheduler?.Dispose(); return Task.CompletedTask; } }
承載系統通過IHost接口表示承載服務的宿主,該對象在應用啟動過程中采用Builder模式由對應的IHostBuilder對象來創建。HostBuilder類型是對IHostBuilder接口的默認實現,所以可以采用如下方式創建一個HostBuilder對象,並調用其Build方法來提供作為宿主的IHost對象。
class Program { static void Main() { new HostBuilder() .ConfigureServices(svcs => svcs .AddSingleton<IHostedService, PerformanceMetricsCollector>()) .Build() .Run(); } }
在調用Build方法之前,可以調用IHostBuilder接口的ConfigureServices方法將PerformancceMetricsCollector注冊成針對IHostedService接口的服務,並將生命周期模式設置成Singleton。除了采用普通的依賴服務注冊方式,針對IHostedService服務的注冊還可以調用IServiceCollection接口的AddHostedService<THostedService>擴展方法來完成,如下所示的編程方式與上面是完全等效的。
class Program { static void Main() { new HostBuilder() .ConfigureServices(svcs => svcs.AddHostedService<PerformanceMetricsCollector>()) .Build() .Run(); } }
最后調用Run方法啟動通過IHost對象表示的承載服務宿主,進而啟動由它承載的PerformancceMetricsCollector服務,該服務將以下圖所示的形式每隔5秒顯示由它“采集”的性能指標。(源代碼從這里下載)
二、依賴注入
服務承載系統無縫整合了依賴注入框架。從上面給出的代碼可以看出,針對承載服務的注冊實際上就是將它注冊到依賴注入框架中。既然承載服務實例最終是通過依賴注入框架提供的,那么它自身所依賴的服務當然也可以注冊到依賴注入框架中。下面將PerformanceMetricsCollector承載的性能指標收集功能分解到由4個接口表示的服務中,其中IProcessorMetricsCollector、IMemoryMetricsCollector和INetworkMetricsCollector接口代表的服務分別用於收集3種對應性能指標,而IMetricsDeliverer接口表示的服務則負責將收集的性能指標發送出去。
public interface IProcessorMetricsCollector { int GetUsage(); } public interface IMemoryMetricsCollector { long GetUsage(); } public interface INetworkMetricsCollector { long GetThroughput(); } public interface IMetricsDeliverer { Task DeliverAsync(PerformanceMetrics counter); }
我們定義的FakeMetricsCollector類型實現了3個性能指標采集接口,它們采集的性能指標直接來源於通過靜態方法Create創建的PerformanceMetrics對象。FakeMetricsDeliverer類型實現了IMetricsDeliverer接口,在實現的DeliverAsync方法中,它直接將PerformanceMetrics對象承載性能指標輸出到控制台上。
public class FakeMetricsCollector : IProcessorMetricsCollector, IMemoryMetricsCollector, INetworkMetricsCollector { long INetworkMetricsCollector.GetThroughput() => PerformanceMetrics.Create().Network; int IProcessorMetricsCollector.GetUsage() => PerformanceMetrics.Create().Processor; long IMemoryMetricsCollector.GetUsage() => PerformanceMetrics.Create().Memory; } public class FakeMetricsDeliverer : IMetricsDeliverer { public Task DeliverAsync(PerformanceMetrics counter) { Console.WriteLine($"[{DateTimeOffset.UtcNow}]{counter}"); return Task.CompletedTask; } }
由於整個性能指標的采集工作被分解到4個接口表示的服務之中,所以可以采用如下所示的方式重新定義承載服務類型PerformanceMetricsCollector。如下面的代碼片段所示,可以直接在構造函數中注入4個依賴服務。對於在StartAsync方法創建的調用器來說,它會利用3個對應的服務采集3種類型的性能指標,並利用IMetricsDeliverer服務將其發送出去。
public sealed class PerformanceMetricsCollector : IHostedService { private readonly IProcessorMetricsCollector _processorMetricsCollector; private readonly IMemoryMetricsCollector _memoryMetricsCollector; private readonly INetworkMetricsCollector _networkMetricsCollector; private readonly IMetricsDeliverer _MetricsDeliverer; private IDisposable _scheduler; public PerformanceMetricsCollector( IProcessorMetricsCollector processorMetricsCollector, IMemoryMetricsCollector memoryMetricsCollector, INetworkMetricsCollector networkMetricsCollector, IMetricsDeliverer MetricsDeliverer) { _processorMetricsCollector = processorMetricsCollector; _memoryMetricsCollector = memoryMetricsCollector; _networkMetricsCollector = networkMetricsCollector; _MetricsDeliverer = MetricsDeliverer; } public Task StartAsync(CancellationToken cancellationToken) { _scheduler = new Timer(Callback, null, TimeSpan.FromSeconds(5),TimeSpan.FromSeconds(5)); return Task.CompletedTask; async void Callback(object state) { var counter = new PerformanceMetrics { Processor = _processorMetricsCollector.GetUsage(), Memory = _memoryMetricsCollector.GetUsage(), Network = _networkMetricsCollector.GetThroughput() }; await _MetricsDeliverer.DeliverAsync(counter); } } public Task StopAsync(CancellationToken cancellationToken) { _scheduler?.Dispose(); return Task.CompletedTask; } }
在調用IHostBuilder接口的Build方法創建作為宿主的IHost對象之前,包括承載服務在內的所有服務都可以通過它的ConfigureServices方法進行注冊,我們采用如下方式注冊了作為承載服務的PerformanceMetricsCollector和它依賴的4個服務。修改后的程序啟動之后同樣會在控制台上看到上面圖片所示的輸出結果。(源代碼從這里下載)
class Program { static void Main() { var collector = new FakeMetricsCollector(); new HostBuilder() .ConfigureServices(svcs => svcs .AddSingleton<IProcessorMetricsCollector>(collector) .AddSingleton<IMemoryMetricsCollector>(collector) .AddSingleton<INetworkMetricsCollector>(collector) .AddSingleton<IMetricsDeliverer, FakeMetricsDeliverer>() .AddSingleton<IHostedService, PerformanceMetricsCollector>()) .Build() .Run(); } }
服務承載系統[1]: 承載長時間運行的服務[上篇]
服務承載系統[2]: 承載長時間運行的服務[下篇]
服務承載系統[3]: 服務承載模型[上篇]
服務承載系統[4]: 服務承載模型[下篇]
服務承載系統[5]: 承載服務啟動流程[上篇]
服務承載系統[6]: 承載服務啟動流程[下篇]