前言
簡單介紹一下承載。
正文
名稱叫做承載,其實就是.net core 定義的一套長期運行的服務的規範。
這個服務可以是web服務,也可以是其他服務,比如tcp,或者一些監控服務。
這裏以監控服務爲例子:
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()
{
return $"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
};
}
internal 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.UtcNow } { PerformanceMetrics.Create() }");
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_scheduler.Dispose();
return Task.CompletedTask;
}
}
然後服務繼承IHostedService, 有一個StartAsync 和 StopAsync, 也就是開始和停止服務。
然後在main裏面進行注入:
new HostBuilder()
.ConfigureServices(svcs =>
// svcs.AddSingleton<IHostedService, PerformanceMetricsCollector>()
svcs.AddHostedService<PerformanceMetricsCollector>()
)
.Build()
.Run();
這裏就是添加這個服務,然後Build,在run 起來就好了。
之所以我們要使用這個HostBuilder,其實就是爲了裏面幫我們定義的一些依賴注入。
比如一個長期服務需要用到這幾個接口:
internal interface IMemoryMetricsCollector
{
long GetUsage();
}
internal interface INetworkMetricsCollector
{
long GetThroughput();
}
internal interface IProcessorMetricsCollector
{
int GetUsage();
}
internal interface IMetricsDeliverer
{
Task DeliverAsync(PerformanceMetrics counters);
}
他們的實現爲:
internal class FakeMetricsCollector : IMemoryMetricsCollector, INetworkMetricsCollector, IProcessorMetricsCollector
{
long INetworkMetricsCollector.GetThroughput()
{
return PerformanceMetrics.Create().Network;
}
long IMemoryMetricsCollector.GetUsage()
{
return PerformanceMetrics.Create().Memory;
}
int IProcessorMetricsCollector.GetUsage()
{
return PerformanceMetrics.Create().Processor;
}
}
public class FackMetricsDeliverer : IMetricsDeliverer
{
Task IMetricsDeliverer.DeliverAsync(PerformanceMetrics counter)
{
Console.WriteLine($"{ DateTimeOffset.UtcNow } { counter }");
return Task.CompletedTask;
}
}
然後你的服務就可以這樣寫:
internal class PerformanceMetricsCollector2 : IHostedService
{
private IDisposable _scheduler;
private readonly IProcessorMetricsCollector _processorMetricsCollector;
private readonly IMemoryMetricsCollector _memoryMetricsCollector;
private readonly INetworkMetricsCollector _networkMetricsCollector;
private readonly IMetricsDeliverer _metricsDeliverer;
public PerformanceMetricsCollector2(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;
}
}
那麼在builder的時候,你需要這樣寫:
var collector = new FakeMetricsCollector();
new HostBuilder()
.ConfigureServices(svcs =>
{
svcs.AddSingleton<IProcessorMetricsCollector>(collector);
svcs.AddSingleton<IMemoryMetricsCollector>(collector);
svcs.AddSingleton<INetworkMetricsCollector>(collector);
svcs.AddSingleton<IMetricsDeliverer, FackMetricsDeliverer>();
svcs.AddHostedService<PerformanceMetricsCollector2>();
})
.Build()
.Run();
這樣就可以注入服務了。
有了依賴注入,那麼可以做很多事情,比如說配置:
internal class FackMetricsDeliverer3 : IMetricsDeliverer
{
private readonly TransportType _transport;
private readonly EndPoint _deliverTo;
private readonly ILogger _logger;
private readonly Action<ILogger, DateTimeOffset,
PerformanceMetrics, EndPoint, TransportType, Exception> _logForDelivery;
public FackMetricsDeliverer3(IOptions<MetricsCollectionOptions> optionsAccessor,
ILogger<FackMetricsDeliverer3> logger)
{
var options = optionsAccessor.Value;
_transport = options.Transport;
_deliverTo = options.DeliverTo;
_logger = logger;
_logForDelivery = LoggerMessage.Define<DateTimeOffset, PerformanceMetrics,
EndPoint, TransportType>(LogLevel.Information, 0, $"[{0}] Deliver performance counter {1} to {2} via {3}");
}
Task IMetricsDeliverer.DeliverAsync(PerformanceMetrics counters)
{
// Console.WriteLine($"[{DateTimeOffset.Now}] Deliver performance counter {counters} to {_deliverTo} via {_transport}");
_logForDelivery(_logger, DateTimeOffset.UtcNow, counters, _deliverTo, _transport, null);
return Task.CompletedTask;
}
}
然後服務這樣寫:
internal class PerformanceMetricsCollector3 : IHostedService
{
private IDisposable _scheduler;
private readonly IProcessorMetricsCollector _processorMetricsCollector;
private readonly IMemoryMetricsCollector _memoryMetricsCollector;
private readonly INetworkMetricsCollector _networkMetricsCollector;
private readonly IMetricsDeliverer _metricsDeliverer;
private readonly TimeSpan _captureInterval;
public PerformanceMetricsCollector3(IProcessorMetricsCollector processorMetricsCollector,
IMemoryMetricsCollector memoryMetricsCollector,
INetworkMetricsCollector networkMetricsCollector,
IMetricsDeliverer metricsDeliverer,
IOptions<MetricsCollectionOptions> optionsAccesor)
{
_processorMetricsCollector = processorMetricsCollector;
_memoryMetricsCollector = memoryMetricsCollector;
_networkMetricsCollector = networkMetricsCollector;
_metricsDeliverer = metricsDeliverer;
_captureInterval = optionsAccesor.Value.CaptureInterval;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_scheduler = new Timer(Callback, null, _captureInterval, _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;
}
}
然後注入的時候這樣:
var collector = new FakeMetricsCollector();
new HostBuilder()
.ConfigureAppConfiguration(builder =>
{
builder.AddJsonFile("appSettings.json");
})
.ConfigureServices((context, svcs) =>
{
svcs.AddSingleton<IProcessorMetricsCollector>(collector);
svcs.AddSingleton<IMemoryMetricsCollector>(collector);
svcs.AddSingleton<INetworkMetricsCollector>(collector);
svcs.AddSingleton<IMetricsDeliverer, FackMetricsDeliverer2>();
svcs.AddHostedService<PerformanceMetricsCollector3>();
svcs.AddOptions().Configure<MetricsCollectionOptions>(context.Configuration.GetSection("MetricsCollection"));
})
.Build()
.Run();
就是把appSettings.json進行注入。
這裏有了配置之後,還有一個東西需要注意,那就是環境,比如不同的環境加載不同的配置:
new HostBuilder()
// 設置host 環境
.ConfigureHostConfiguration(builder => builder.AddCommandLine(args))
// 設置app 環境
.ConfigureAppConfiguration((context, builder) =>
{
builder.AddJsonFile("appSettings.json", optional: false);
Console.WriteLine(context.HostingEnvironment.EnvironmentName);
builder.AddJsonFile(path: $"appSettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
})
.ConfigureServices((context, svcs) =>
{
svcs.AddSingleton<IProcessorMetricsCollector>(collector);
svcs.AddSingleton<IMemoryMetricsCollector>(collector);
svcs.AddSingleton<INetworkMetricsCollector>(collector);
svcs.AddSingleton<IMetricsDeliverer, FackMetricsDeliverer2>();
svcs.AddHostedService<PerformanceMetricsCollector3>();
svcs.AddOptions().Configure<MetricsCollectionOptions>(context.Configuration.GetSection("MetricsCollection"));
})
.Build()
.Run();
有了不同環境不同的配置後,那麼你還需要日誌:
internal class FackMetricsDeliverer3 : IMetricsDeliverer
{
private readonly TransportType _transport;
private readonly EndPoint _deliverTo;
private readonly ILogger _logger;
private readonly Action<ILogger, DateTimeOffset,
PerformanceMetrics, EndPoint, TransportType, Exception> _logForDelivery;
public FackMetricsDeliverer3(IOptions<MetricsCollectionOptions> optionsAccessor,
ILogger<FackMetricsDeliverer3> logger)
{
var options = optionsAccessor.Value;
_transport = options.Transport;
_deliverTo = options.DeliverTo;
_logger = logger;
_logForDelivery = LoggerMessage.Define<DateTimeOffset, PerformanceMetrics,
EndPoint, TransportType>(LogLevel.Information, 0, $"[{0}] Deliver performance counter {1} to {2} via {3}");
}
Task IMetricsDeliverer.DeliverAsync(PerformanceMetrics counters)
{
// Console.WriteLine($"[{DateTimeOffset.Now}] Deliver performance counter {counters} to {_deliverTo} via {_transport}");
_logForDelivery(_logger, DateTimeOffset.UtcNow, counters, _deliverTo, _transport, null);
return Task.CompletedTask;
}
}
然後配置的時候這樣配:
// about log
var collector = new FakeMetricsCollector();
new HostBuilder()
// 設置host 環境
.ConfigureHostConfiguration(builder => builder.AddCommandLine(args))
// 設置app 環境
.ConfigureAppConfiguration((context, builder) => {
builder.AddJsonFile("appSettings.json", optional: false);
Console.WriteLine(context.HostingEnvironment.EnvironmentName);
builder.AddJsonFile(path: $"appSettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true);
})
.ConfigureServices((context, svcs) =>
{
svcs.AddSingleton<IProcessorMetricsCollector>(collector);
svcs.AddSingleton<IMemoryMetricsCollector>(collector);
svcs.AddSingleton<INetworkMetricsCollector>(collector);
svcs.AddSingleton<IMetricsDeliverer, FackMetricsDeliverer3>();
svcs.AddHostedService<PerformanceMetricsCollector3>();
svcs.AddOptions().Configure<MetricsCollectionOptions>(context.Configuration.GetSection("MetricsCollection"));
})
.ConfigureLogging((context , builder)=>
{
builder.AddConfiguration(context.Configuration.GetSection("Logging"));
builder.AddConsole();
})
.Build()
.Run();
這裏ConfigureLogging 配置了日誌,同樣配置了日誌的配置。
至此就是一個.net core 定義的一個框架了。
裏面有依賴注入、配置、環境、日誌。
所以我們依賴這個框架就可以馬上專注我們的業務。
下一節看下這種框架是如何設計的。
結
下一節承載的設計。