1. 程式人生 > >[ASP.NET Core 3框架揭祕] 服務承載系統[1]: 承載長時間執行的服務[上篇]

[ASP.NET Core 3框架揭祕] 服務承載系統[1]: 承載長時間執行的服務[上篇]

藉助.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]: 承載服務啟動流程[下篇]