重新整理 .net core 實踐篇———承載[外篇]

前言

簡單介紹一下承載。

正文

名稱叫做承載,其實就是.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 定義的一個框架了。

裏面有依賴注入、配置、環境、日誌。

所以我們依賴這個框架就可以馬上專注我們的業務。

下一節看下這種框架是如何設計的。

下一節承載的設計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章