日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

ASP.NET?Core?6框架揭秘實例演示之如何承載你的后臺服務_實用技巧

作者:Artech ? 更新時間: 2022-05-18 編程語言

借助 .NET提供的服務承載(Hosting)系統(tǒng),我們可以將一個或者多個長時間運行的后臺服務寄宿或者承載我們創(chuàng)建的應用中。任何需要在后臺長時間運行的操作都可以定義成標準化的服務并利用該系統(tǒng)來承載,ASP.NET Core應用最終也體現(xiàn)為這樣一個承載服務。

借助 .NET提供的服務承載(Hosting)系統(tǒng),我們可以將一個或者多個長時間運行的后臺服務寄宿或者承載我們創(chuàng)建的應用中。任何需要在后臺長時間運行的操作都可以定義成標準化的服務并利用該系統(tǒng)來承載,ASP.NET Core應用最終也體現(xiàn)為這樣一個承載服務。(本篇提供的實例已經(jīng)匯總到《ASP.NET Core 6框架揭秘-實例演示版》)

[S1401]利用承載服務收集性能指標

承載服務的項目一般會采用“Microsoft.NET.Sdk.Worker”這個SDK。服務承載模型涉及的接口和類型大都定義在“Microsoft.Extensions.Hosting.Abstractions”這個NuGet包,而具體實現(xiàn)在由NuGet包“Microsoft.Extensions.Hosting”來提供。我們演示的承載服務會定時采集當前進程的性能指標并將其分發(fā)出去。我們只關(guān)注處理器使用率、內(nèi)存使用量和網(wǎng)絡吞吐量這三種典型的指標,為此我們定義了如下這個PerformanceMetrics類型。我們并不會實現(xiàn)真正的性能指標收集,定義的靜態(tài)方法Create會利用隨機生成的指標來創(chuàng)建PerformanceMetrics對象。

public class PerformanceMetrics
{
    private static readonly Random _random = new();

    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()
    {
        Processor 	= _random.Next(1, 8),
        Memory 	= _random.Next(10, 100) * 1024 * 1024,
        Network 	= _random.Next(10, 100) * 1024 * 1024
    };
}

承載服務通過IHostedService接口表示,該接口定義的StartAsync和StopAsync方法可以啟動與關(guān)閉服務。我們將性能指標采集服務定義成如下這個PerformanceMetricsCollector類型。在實現(xiàn)的StartAsync方法中,我們一個定時器每隔5秒調(diào)用Create方法創(chuàng)建一個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;
    }
}

服務承載系統(tǒng)通過IHost接口表示承載服務的宿主,該對象在應用啟動過程中采用Builder模式由對應的IHostBuilder對象來構(gòu)建。HostBuilder類型是對IHostBuilder接口的默認實現(xiàn),所以我們采用如下方式創(chuàng)建一個HostBuilder對象,并調(diào)用其Build方法來提供作為宿主的IHost對象。在調(diào)用Build方法構(gòu)建IHost對象之前,我們調(diào)用了ConfigureServices方法將PerformancceMetricsCollector注冊成針對IHostedService接口的服務,并將生命周期模式設置成Singleton。

using App;
new HostBuilder()
.ConfigureServices(svcs => svcs
    .AddSingleton())
    .Build()
    .Run();

我們最后調(diào)用Run方法啟動通過IHost對象表示的承載服務宿主,進而啟動由它承載的PerformancceMetricsCollector服務,該服務將以圖1所示的形式每隔5秒在控制臺上輸出“采集”的性能指標。

圖1 承載指標采集服務

除了采用一般的服務注冊方式,我們還可以按照如下的方式調(diào)用IServiceCollection接口的AddHostedService擴展方法來對承載服務PerformanceMetricsCollector進行注冊。我們一般也不會通過調(diào)用構(gòu)造函數(shù)的方式創(chuàng)建HostBuilder對象,而是使用定義在Host類型中的 工廠方法CreateDefaultBuilder創(chuàng)建來構(gòu)建IHostBuilder對象。

using App;
Host.CreateDefaultBuilder(args)
    .ConfigureServices(svcs => svcs.AddHostedService())
    .Build()
    .Run();

[S1402]依賴注入的應用

服務承載系統(tǒng)整合依賴注入框架,針對承載服務的注冊實際上就是將它注冊到依賴注入框架中。既然承載服務實例最終是通過依賴注入容器提供的,那么它自身所依賴的服務當然也可以進行注冊。我們接下來將PerformanceMetricsCollector提供的性能指標收集功能分解到由四個接口表示的服務中,IProcessorMetricsCollector、IMemoryMetricsCollector和INetworkMetricsCollector接口代表的服務分別用于收集三種對應的性能指標,而IMetricsDeliverer接口表示的服務則負責將收集的性能指標發(fā)送出去。

public interface IProcessorMetricsCollector
{
    int GetUsage();
}
public interface IMemoryMetricsCollector
{
    long GetUsage();
}
public interface INetworkMetricsCollector
{
    long GetThroughput();
}

public interface IMetricsDeliverer
{
    Task DeliverAsync(PerformanceMetrics counter);
}

我們定義的MetricsCollector類型實現(xiàn)了三個性能指標采集接口,采集的性能指標直接來源于通過靜態(tài)方法Create創(chuàng)建的PerformanceMetrics對象。MetricsDeliverer類型實現(xiàn)了IMetricsDeliverer接口,實現(xiàn)的DeliverAsync方法直接將PerformanceMetrics對象承載的性能指標輸出到控制臺上。

public class MetricsCollector :
    IProcessorMetricsCollector,
    IMemoryMetricsCollector,
    INetworkMetricsCollector
{
    long INetworkMetricsCollector.GetThroughput() => PerformanceMetrics.Create().Network;

    int IProcessorMetricsCollector.GetUsage() => PerformanceMetrics.Create().Processor;

    long IMemoryMetricsCollector.GetUsage() => PerformanceMetrics.Create().Memory;
}

public class MetricsDeliverer : IMetricsDeliverer
{
    public Task DeliverAsync(PerformanceMetrics counter)
    {
        Console.WriteLine($"[{DateTimeOffset.UtcNow}]{counter}");
        return Task.CompletedTask;
    }
}

由于整個性能指標的采集工作被分解到四個接口表示的服務之中,所以我們可以采用如下所示的方式重新定義承載服務類型PerformanceMetricsCollector。如代碼片段所示,我們在構(gòu)造函數(shù)中注入四個依賴服務,StartAsync方法利用注入的IProcessorMetricsCollector、IMemoryMetricsCollector和INetworkMetricsCollector對象采集對應的性能指標,并利用IMetricsDeliverer對象將其發(fā)送出去。

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;
    }
}

在調(diào)用IHostBuilder接口的Build方法將IHost對象構(gòu)建出來之前,包括承載服務在內(nèi)的所有服務都可以通過它的ConfigureServices方法進行了注冊。修改后的程序啟動之后同樣會在控制臺上看到圖14-1所示的輸出結(jié)果(S1402)。

using App;
var collector = new MetricsCollector();
Host.CreateDefaultBuilder(args)
    .ConfigureServices(svcs => svcs
        .AddHostedService()
        .AddSingleton(collector)
        .AddSingleton(collector)
        .AddSingleton(collector)
        .AddSingleton())
    .Build()
    .Run();

[S1403]配置選項的應用

真正的應用開發(fā)基本都會使用到配置選項,比如我們演示程序中性能指標采集的時間間隔就應該采用配置選項來指定。由于涉及對性能指標數(shù)據(jù)的發(fā)送,所以最好將發(fā)送的目標地址定義在配置選項中。如果有多種傳輸協(xié)議可供選擇,就可以定義相應的配置選項。 .NET應用推薦采用Options模式來使用配置選項,所以可以定義如下這個MetricsCollectionOptions類型來承載三種配置選項。

public class MetricsCollectionOptions
{
    public TimeSpan 		CaptureInterval { get; set; }
    public TransportType 	Transport { get; set; }
    public Endpoint 		DeliverTo { get; set; }
}
public enum TransportType
{
    Tcp,
    Http,
    Udp
}
public class Endpoint
{
    public string 	Host { get; set; }
    public int 	        Port { get; set; }
    public override string ToString() => $"{Host}:{Port}";
}

傳輸協(xié)議和目標地址使用在MetricsDeliverer服務中,所以我們對它進行了如下的修改。如代碼片段所示,我們在構(gòu)造函數(shù)中利用注入的IOptions服務來提供上面的兩個配置選項。在實現(xiàn)的DeliverAsync方法中,我們將采用的傳輸協(xié)議和目標地址輸出到控制臺上。

public class MetricsDeliverer : IMetricsDeliverer
{
    private readonly TransportType _transport;
    private readonly Endpoint 	     _deliverTo;

    public MetricsDeliverer(IOptions optionsAccessor)
    {
        var options 	= optionsAccessor.Value;
        _transport 	= options.Transport;
        _deliverTo 	= options.DeliverTo;
    }

    public Task DeliverAsync(PerformanceMetrics counter)
    {
        Console.WriteLine($"[{DateTimeOffset.Now}]Deliver performance counter {counter} to {_deliverTo} via {_transport}");
        return Task.CompletedTask;
    }
}

承載服務類型PerformanceMetricsCollector同樣應該采用這種方式來提取表示性能指標采集頻率的配置選項。如下所示的代碼片段是PerformanceMetricsCollector采用配置選項后的完整定義。

public sealed class PerformanceMetricsCollector : IHostedService
{
    private readonly IProcessorMetricsCollector 		_processorMetricsCollector;
    private readonly IMemoryMetricsCollector 		_memoryMetricsCollector;
    private readonly INetworkMetricsCollector 		_networkMetricsCollector;
    private readonly IMetricsDeliverer 			_metricsDeliverer;
    private readonly TimeSpan 				_captureInterval;
    private IDisposable? 				_scheduler;

    public PerformanceMetricsCollector(
        IProcessorMetricsCollector processorMetricsCollector,
        IMemoryMetricsCollector memoryMetricsCollector,
        INetworkMetricsCollector networkMetricsCollector,
        IMetricsDeliverer metricsDeliverer,
        IOptions optionsAccessor)
    {
        _processorMetricsCollector 	= processorMetricsCollector;
        _memoryMetricsCollector 	= memoryMetricsCollector;
        _networkMetricsCollector 	= networkMetricsCollector;
        _metricsDeliverer		= metricsDeliverer;
        _captureInterval 		= optionsAccessor.Value.CaptureInterval;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
       _scheduler = new Timer(Callback, null, TimeSpan.FromSeconds(5), _captureInterval);
       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;
    }
}

配置文件配置選項的常用來源,所以我們在根目錄下添加了一個名為appsettings.json的配置文件,并在其中定義如下內(nèi)容來提供上述三個配置選項。由Host類型的CreateDefaultBuilder工廠方法創(chuàng)建的IHostBuilder對象會自動加載這個配置文件。

{
  "MetricsCollection": {
    "CaptureInterval": "00:00:05",
    "Transport": "Udp",
    "DeliverTo": {
      "Host": "192.168.0.1",
      "Port": 3721
    }
  }
}

我們接下來對演示程序做相應的改動。之前針對依賴服務的注冊是通過調(diào)用IHostBuilder對象的ConfigureServices方法利用作為參數(shù)的Action對象完成的,該接口還有一個ConfigureServices方法重載,它的參數(shù)類型為Action,作為輸入的HostBuilderContext上下文可以提供表示應用配置的IConfiguration對象。

using App;
var collector = new MetricsCollector();
Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, svcs) => svcs
        .AddHostedService()
        .AddSingleton(collector)
        .AddSingleton(collector)
        .AddSingleton(collector)
        .AddSingleton()
        .Configure(context.Configuration.GetSection("MetricsCollection")))
    .Build()
    .Run();

我們利用提供的Action委托通過調(diào)用IServiceCollection接口的Configure擴展方法從提供的HostBuilderContext對象中提取出配置,并對MetricsCollectionOptions配置選項做了綁定。我們修改后的程序運行之后在控制臺上會輸出如圖2所示結(jié)果。

圖2 引入配置選項

[S1404]提供針對環(huán)境的配置

應用程序總是針對某個具體環(huán)境進行部署的,開發(fā)(Development)、預發(fā)(Staging)和產(chǎn)品(Production)是三種典型的部署環(huán)境,這里的部署環(huán)境在服務承載系統(tǒng)中統(tǒng)稱為承載環(huán)境(Hosting Environment)。一般來說,不同的承載環(huán)境往往具有不同的配置選項,下面我們將演示如何為不同的承載環(huán)境提供相應的配置選項。具體的做法很簡單:將共享或者默認的配置定義在基礎(chǔ)配置文件(如appsettings.json)中,將差異化的部分定義在針對具體環(huán)境的配置文件(如appsettings.staging.json和appsettings.production.json)中。對于我們演示的實例來說,我們可以采用圖3所示的方式添加額外的兩個配置文件來提供針對預發(fā)環(huán)境和產(chǎn)品環(huán)境的差異化配置。

圖3 針對承載環(huán)境的配置文件

對于演示實例提供的三個配置選項來說,假設針對承載環(huán)境的差異化配合僅限于發(fā)送的目標終結(jié)點(IP地址和端口),我們就可以采用如下方式將它們定義在針對預發(fā)環(huán)境的appsettings.staging.json和針對產(chǎn)品環(huán)境的appsettings.production.json中。

appsettings.staging.json:

{
  "MetricsCollection": {
    "DeliverTo": {
      "Host": "192.168.0.2",
      "Port": 3721
    }
  }
}

appsettings.production.json:

{
  "MetricsCollection": {
    "DeliverTo": {
      "Host": "192.168.0.3",
      "Port": 3721
    }
  }
}

由于我們在調(diào)用Host的CreateDefaultBuilder方法時傳入了命令行參數(shù)(args),所以默認創(chuàng)建的IHostBuilder會將其作為配置源。也正因為如此,我們可以采用命令行參數(shù)的形式設置當前的承載環(huán)境(對應配置名稱為“environment”)。如圖4所示,我們分別指定不同的承載環(huán)境先后四次運行我們的程序,從輸出的IP地址可以看出,應用程序確實是根據(jù)當前承載環(huán)境加載對應的配置文件的。輸出結(jié)果還體現(xiàn)了另一個細節(jié),那就是默認采用的是產(chǎn)品(Production)環(huán)境。

圖4 針對承載環(huán)境加載配置文件

[S1405]日志的應用

應用開發(fā)中不可避免地會涉及很多針對“診斷日志”的應用,我們接下來就來演示承載服務如何記錄日志。對于我們的演示實例來說,用于發(fā)送性能指標的MetricsDeliverer對象會將收集的指標數(shù)據(jù)輸出到控制臺上,下面將這段文字以日志的形式進行輸出,為此我們將這個類型進行了如下的修改。

public class MetricsDeliverer : IMetricsDeliverer
{
    private readonly TransportType _transport;
    private readonly Endpoint _deliverTo;
    private readonly ILogger _logger;
    private readonly Action _logForDelivery;

    public MetricsDeliverer(IOptions optionsAccessor, ILogger logger)
    {
        var options = optionsAccessor.Value;
        _transport = options.Transport;
        _deliverTo = options.DeliverTo;
        _logger = logger;
        _logForDelivery = LoggerMessage.Define(LogLevel.Information, 0, "[{0}]Deliver performance counter {1} to {2} via {3}");
    }

    public Task DeliverAsync(PerformanceMetrics counter)
    {
        _logForDelivery(_logger, DateTimeOffset.Now, counter, _deliverTo, _transport, null);
        return Task.CompletedTask;
    }
}

如上面的代碼片段所示,我們利用構(gòu)造函數(shù)中注入了的ILogger對象并來記錄日志。為了避免對同一個消息模板的重復解析,我們可以使用LoggerMessage類型定義的委托對象來輸出日志,這也是MetricsDeliverer中采用的編程模式。運行修改后的程序會控制臺上的輸出如圖5所示的結(jié)果。由輸出結(jié)果可以看出,這些文字是由我們注冊的ConsoleLoggerProvider提供的ConsoleLogger對象輸出到控制臺上的。由于承載系統(tǒng)自身在進行服務承載過程中也會輸出一些日志,所以它們也會輸出到控制臺上。

圖5 將日志輸出到控制臺上

[S1406]在配置中定義日志過濾規(guī)則

如果需要對輸出的日志進行過濾,可以將過濾規(guī)則定義在配置文件中。為了避免在“產(chǎn)品”環(huán)境因輸出過多的日志影響性能,我們在appsettings.production.json配置文件中以如下的形式將類別以“Microsoft.”為前綴的日志(最低)等級設置為 Warning。

{
  "MetricsCollection": {
    "DeliverTo": {
      "Host": "192.168.0.3",
      "Port": 3721
    }
  },
  "Logging": {
    "LogLevel": {
      "Microsoft": "Warning"
    }
  }
}

如果此時分別針對開發(fā)(Development)環(huán)境和產(chǎn)品(Production)環(huán)境以命令行的形式啟動修改后的應用程序,就會發(fā)現(xiàn)針對開發(fā)環(huán)境控制臺會輸出類型前綴為“Microsoft.”的日志,但是在針對產(chǎn)品環(huán)境的控制臺上卻找不到它們的蹤影。

圖6 根據(jù)承載環(huán)境過濾日志

原文鏈接:https://www.cnblogs.com/artech/p/inside-asp-net-core-6-21.html

欄目分類
最近更新