借助.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]: 承载服务启动流程[下篇]

[ASP.NET Core 3框架揭秘] 服务承载系统[1]: 承载长时间运行的服务[上篇]的更多相关文章

  1. ASP.NET Core 6框架揭秘实例演示[21]:如何承载你的后台服务

    借助 .NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中.任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,A ...

  2. ASP.NET Core 6框架揭秘实例演示[22]:如何承载你的后台服务[补充]

    借助 .NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中.任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,A ...

  3. ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁

    ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来.从设计模式的角度来讲,"管道"是构建者( ...

  4. ASP.NET Core 6框架揭秘实例演示[25]:配置与承载环境的应用

    与服务注册一样,针对配置的设置同样可以采用三种不同的编程模式.第一种是利用WebApplicationBuilder的Host属性返回的IHostBuilder对象,它可以帮助我们设置面向宿主和应用的 ...

  5. [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期

    生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...

  6. [ASP.NET Core 3框架揭秘] 依赖注入[7]:服务消费

    包含服务注册信息的IServiceCollection集合最终被用来创建作为依赖注入容器的IServiceProvider对象.当需要消费某个服务实例的时候,我们只需要指定服务类型调用IService ...

  7. [ASP.NET Core 3框架揭秘] 依赖注入[6]:服务注册

    通过<利用容器提供服务>我们知道作为依赖注入容器的IServiceProvider对象是通过调用IServiceCollection接口的扩展方法BuildServiceProvider创 ...

  8. [ASP.NET Core 3框架揭秘] 依赖注入[5]: 利用容器提供服务

    毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容 ...

  9. [ASP.NET Core 3框架揭秘] 文件系统[1]:抽象的“文件系统”

    ASP.NET Core应用 具有很多读取文件的场景,比如配置文件.静态Web资源文件(比如CSS.JavaScript和图片文件等)以及MVC应用的View文件,甚至是直接编译到程序集中的内嵌资源文 ...

随机推荐

  1. Python语言学习前提:循环语句

    一.循环语句 1.循环语句:允许执行下一个语句或语句组多次 2. 循环类型 3. 循环控制语句 4. while 循环语句 a.while循环语句:在某个条件下,循环执行某段程序,以处理需要重复处理的 ...

  2. mybatis学习笔记四

    记录下动态sql的常用标签: 1.where 一般用作数据操作添加的条件 例子: <select id="selectByRoleId" resultMap="re ...

  3. 894A. QAQ#(暴力)

    题目出处:http://codeforces.com/problemset/problem/894/A 题目大意:计数依次出现QAQ的次数 #include<iostream> using ...

  4. IE浏览器弹出的文件下载工具条实现自动另存为

    IE浏览器中如果有一个文件可以下载,用鼠标点击一下文件的超链接,会在IE浏览器下方出现一个工具条,工具条上主要有“打开” “保存”两个按钮. 如果点击“保存”按钮,会把附件保存到系统的“下载”或“Do ...

  5. Sequence Diagram时序图 - 应该是最简洁有力的业务了

    直接看UML吧,一目了然,不用解释.自信男人,无须多言. 这是用ListView显示Post的流程. 这是Uppdate User Profile的流程.自信男人,无须多言.

  6. the least-squares criterion|Sxx|Sxy|Syy|Regression Equation|Outliers|Influential Observations|curvilinear regression|linear regression

    4.2 The Regression Equation Because we could draw many different lines through the cluster of data p ...

  7. 通过 WMI 返回硬盘序列号的一点小问题

    通过 SELECT * FROM Win32_PhysicalMedia 可以返回硬盘序列号,但是在实际使用中某些情况下(原因未知)返回的 SerialNumber 是实际硬盘序列号的字符串按字符Ur ...

  8. IPC之——消息队列

    消息队列作用: 可以用于两个没有联系的进程间通信,创建一个消息队列类似于打开了一个文件,两个不同的进程都可以进行操作 消息队列之函数介绍: 头文件:<sys/type.h> <sys ...

  9. Arcpy处理修改shapefile FeatureClass 线要素坐标

    需求:在开发的webgis系统中需要将道路矢量数据与谷歌地图瓦片叠加,谷歌地图瓦片在国家测绘局的要求是进行了偏移处理的,人称“火星坐标系GCJ_02”,道路数据是WGS-84坐标系下的经纬度坐标,现在 ...

  10. java后台使用HttpServletRequest接收参数转换为model

    当前端需要传图片时,后台用MultipartHttpServletRequest接收参数,request接收过来的参数有很多弊端,需要包装成自己的model就得做转化 弊端: 1.所接收的参数类型无法 ...