用服务器做网站空间,搜索引擎优化的目标体系包括哪些,河南做网站那家最好,个人网站怎么自己备案很多时候我们都会有设计一个后台服务的需求#xff0c;比如#xff0c;传统的Windows Service#xff0c;或者Linux下的守护进程。这类应用的一个共同特点就是后台运行#xff0c;并且不占用控制台界面。通常情况下#xff0c;后台服务在提供服务时#xff0c;会通过日志… 很多时候我们都会有设计一个后台服务的需求比如传统的Windows Service或者Linux下的守护进程。这类应用的一个共同特点就是后台运行并且不占用控制台界面。通常情况下后台服务在提供服务时会通过日志输出来记录服务处理的详细信息用户也可以根据具体需要来设置不同的日志级别Log Level从而决定日志的输出详细程度。无论是传统的Windows Service还是Linux守护进程都是开发人员非常熟悉的应用程序形式开发技术和开发模式都是大家所熟知的那么在.NET Core中又如何专业地实现这类后台服务应用呢其实.NET Core的开发人员应该早就接触过并且使用过某种基于.NET Core的后台服务的开发技术了它就是ASP.NET Core。ASP.NET Core应用程序在启动后通过监听端口接受来自客户端的HTTP请求然后根据路由策略定位到某个控制器Controller的某个方法Action上接着将处理结果又以HTTP Response的形式返回给客户端此处描述省略了Filter等步骤。ASP.NET Core作为后台服务的一个最大特点是它是专为HTTP协议定制的也就是说ASP.NET Core有着非常强大的处理HTTP协议与通信管道的能力。很显然在某些场景中服务端与客户端的通信并非基于HTTP协议甚至于后台服务仅仅是在本地处理一些批量的事务并不会涉及与其它服务或者客户端的交互。在这种情况下使用ASP.NET Core就会显得比较重了。在上面我特别强调了“专业地”三个字如何理解什么叫“专业”我想简单地说就是我们所设计的后台服务程序在基础设施部分能够做到与ASP.NET Core相当的编程模型并且能够达到与ASP.NET Core相当的扩展能力具体地说主要有以下几个方面具有非常好的隔离性开发者只需要关注怎么实现自己的后台服务逻辑即可不需要关注服务运行的保障体系比如如何正常终止服务、如何写入日志、如何管理对象生命周期等等具有非常好的编程体验使用过ASP.NET Core的开发者能够快速上手直击主题快速实现业务处理逻辑可扩展、可配置的应用程序配置体系可扩展、可配置的日志体系可扩展、可配置的依赖注入体系对服务宿主环境的区分。比如在ASP.NET Core中通常分为Development、Test、Staging、Production等环境不同的环境可以有不同的配置信息等在.NET Core 2.1以前要在后台服务中自己实现上述各项是很不容易的但从.NET Core 2.1开始我们就可以直接使用.NET Generic Host体系来实现自己的后台服务程序也称为服务宿主程序。根据微软官方文档服务宿主程序分为两种Web Hosting和Generic Hosting前者主要处理HTTP请求ASP.NET Core就是基于Web Hosting但在今后Generic Hosting会一统江湖以做到能够同时处理HTTP和非HTTP两种不同的使用场景。基于.NET Generic Host我们可以打造自己的服务宿主Service Hosting框架以便在实际项目中能够基于这个框架来快速实现不同的后台服务应用场景。设计从本质上讲一个.NET Core服务宿主程序只需要实现IHostedService接口然后在控制台应用程序中通过HostBuilder来建立一个Host实例并将IHostedService的实例注册到Host中然后直接运行即可。下面的代码展示了这种最基础的实现方式class MyService : IHostedService{ public Task StartAsync(CancellationToken cancellationToken) { Console.WriteLine(Host Started); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) { Console.WriteLine(Host Stopped); return Task.CompletedTask; }}class Program{ static async Task Main(string[] args) { var hostBuilder new HostBuilder() .ConfigureServices(serviceCollection { serviceCollection.AddSingletonIHostedService, MyService(); }); await hostBuilder.RunConsoleAsync(); }}我们已经成功地实现了一个服务宿主程序请使用C# 7.2或更高的版本来编译上面的代码因为使用了异步Main函数。执行程序会在控制台打印Host Started的字样并提示目前的执行环境为Production按下CTRLC可以结束程序的执行。在按下CTRLC时控制台又会输出Host Stopped字样然后程序退出。上面的代码最关键的一点就是要将IHostedService的实现类注册到依赖注入框架中于是Host Builder在运行主机Host的时候就会通过IHostedService接口类型找到已注册的实例然后运行服务。通过Host Builder我们还可以对宿主程序的执行环境、配置信息、日志等各方面进行配置从而提供更为强大的服务端功能。比如在上面的代码中仅仅是通过Console.WriteLine的调用来输出信息这种做法并不好因为如果服务运行于后台是不能访问控制台的我们需要日志发布机制。由此可见还有很多工作我们需要完成总结起来我们希望有一个简单的框架在这个框架中配置、日志、宿主环境等等设置都已遵循常规的标准做法我们只需要关注于实现上面的StartAsync和StopAsync方法即可这样的框架基本上也就能够满足大多数的服务宿主应用程序的开发需求。所谓的“遵循常规的标准做法”意思就是可以通过配置文件、命令行或者环境变量来指定目前的宿主环境是Development、Test、Staging还是Production可以通过配置文件、命令行或者环境变量来提供程序执行的配置信息可以提供基本的日志定义和输出机制比如可以通过配置文件来配置日志系统并将日志输出到控制台还可以提供一些额外的编程接口以保证循环任务的合理退出、资源的合理释放等等根据上述需求分析以及.NET Core中服务宿主程序的基本实现技术我做出了如下的设计设计一个ServiceHost的类型它的主要任务就是托管一种后台服务它包含服务的启动与停止的逻辑。因此ServiceHost是IHostedService的一种实现设计一个ServiceRunner的类型它的主要任务是配置运行环境并对ServiceHost进行注册。因此ServiceRunner基本上就类似于ASP.NET Core中Startup类的职责在里面可以进行各种配置和服务注册为ServiceHost的执行提供环境基于这样的设计当我需要实现一个宿主服务时我只需要继承ServiceHost类实现其中的StartAsync和StopAsync方法然后运行ServiceRunner即可达到上述“标准做法”的要求。当然还可以继承ServiceRunner以实现一些运行环境的高级配置。下面的类图展示了这样一种设计上面的设计可以看到ServiceHost类提供了两个抽象方法StartAsync、StopAsync这两个方法都可以支持基于任务的异步执行模式Task-based Asynchronous PatternTAP在实际应用中只需要实现这两个方法即可。ServiceHost所提供的OnHostStarted、OnHostStopped以及OnHostStopping回调方法会在ServiceHost的生命周期的特定阶段被调用到因此如果有需要在服务启动完成、服务准备停止以及服务完成停止这几个阶段进行额外的处理的话就可以根据自己的需要来重载这几个方法。而服务宿主环境的配置就实现在ServiceRunner中。ServiceRunner提供了类似ASP.NET Core中Startup类的一系列方法在这些方法中ServiceRunner完成了对应用程序配置信息、宿主环境配置信息、日志以及类型依赖项的配置工作。同样开发者也可以根据自己的需要重载这些方法来完成额外的配置任务。综上所述整体设计既满足了简化开发任务的需求又满足了提供必要扩展的需要。具体代码这里就不贴了请直接下载本文的附件其中包含完整的代码。接下来我们来了解一下基于该服务宿主框架的几个常用开发模式。使用这里介绍几种不同的应用场景下使用我们的服务宿主框架的方法供大家参考。基本用法下面的代码就是最简单的使用方式可以看到与上面的代码相比我们已经可以使用日志来输出信息了并且更重要的是应用程序的配置信息都可以放在appsettings.json文件中不仅如此宿主程序的运行环境配置在hostsettings.json文件中还可以根据当前的宿主环境来选择不同的配置文件。这些行为已经跟ASP.NET Core的执行行为非常相似了。更有趣的是ServiceRunner的ConfigureAppConfiguration方法中默认加入了通过环境变量以及命令行的方式来实现程序的配置因此开发出来的服务宿主程序可以很方便地集成在容器环境中。class MyService : ServiceHost{ private readonly ILogger logger; public MyService(ILoggerMyService logger, IApplicationLifetime applicationLifetime) : base(applicationLifetime) this.logger logger; public override Task StartAsync(CancellationToken cancellationToken) { this.logger.LogInformation(MyService started.); return Task.CompletedTask; } public override Task StopAsync(CancellationToken cancellationToken) { this.logger.LogInformation(MyService stopped.); return Task.CompletedTask; }}class Program{ static async Task Main(string[] args) { var serviceRunner new ServiceRunnerMyService(); await serviceRunner.RunAsync(args); }}代码执行效果如下合理终止无限循环的服务端任务另一个使用场景就是当ServiceHost启动的时候会启动一个后台任务不停地执行一些处理逻辑直到用户按下CTRLC才会停止这个重复执行的任务并正常终止程序。使用上面的服务宿主框架也很容易实现class MyService : ServiceHost{ private readonly ILogger logger; private readonly CancellationTokenSource cancellationTokenSource new CancellationTokenSource(); private readonly ListTask tasks new ListTask(); public MyService(ILoggerMyService logger, IApplicationLifetime applicationLifetime) : base(applicationLifetime) { this.logger logger; } public override Task StartAsync(CancellationToken cancellationToken) { var task Task.Run(async () { while (!cancellationTokenSource.IsCancellationRequested) { logger.LogInformation($Task executing at {DateTime.Now}); await Task.Delay(1000); } }); tasks.Add(task); return Task.CompletedTask; } public override Task StopAsync(CancellationToken cancellationToken) { Task.WaitAll(tasks.ToArray(), 5000); logger.LogInformation(Host stopped.); return Task.CompletedTask; } protected override void Dispose(bool disposing) { logger.LogInformation(Host disposed.); base.Dispose(disposing); } protected override void OnHostStopping() { logger.LogInformation(Host stopping requested.); this.cancellationTokenSource.Cancel(); }}class Program{ static async Task Main(string[] args) { var serviceRunner new ServiceRunnerMyService(); await serviceRunner.RunAsync(args); }}主要思路就是在MyService中定义一个CancellationTokenSource在OnHostStopping的回调函数中调用Cancel方法触发取消事件然后在任务的运行体中判断是否已经发起了“取消”请求。执行结果如下Serilog的集成与使用我们还可以非常方便地在我们的服务宿主程序中使用Serilog以实现强大的日志功能代码如下class MyService : ServiceHost{ private readonly Microsoft.Extensions.Logging.ILogger logger; public MyService(ILoggerMyService logger, IApplicationLifetime applicationLifetime) : base(applicationLifetime) this.logger logger; public override Task StartAsync(CancellationToken cancellationToken) { this.logger.LogInformation(MyService started.); return Task.CompletedTask; } public override Task StopAsync(CancellationToken cancellationToken) { this.logger.LogInformation(MyService stopped.); return Task.CompletedTask; }}class SerilogSampleRunner : ServiceRunnerMyService{ protected override void ConfigureLogging(HostBuilderContext context, ILoggingBuilder logging) { // Leave this method blank to remove any logging configuration from base implementation. } protected override IHostBuilder ConfigureAdditionalFeatures(IHostBuilder hostBuilder) { return hostBuilder.UseSerilog((hostBuilderConfig, loggerConfig) { loggerConfig.ReadFrom.Configuration(hostBuilderConfig.Configuration); }); }}class Program{ static async Task Main(string[] args) { var serviceRunner new SerilogSampleRunner(); await serviceRunner.RunAsync(args); }}执行上面的代码可以看到输出日志的格式发生了变化Serilog有很多插件可以很方便地将日志输出到各种不同的载体比如文件、数据库、Azure托管的消息总线等等有兴趣的读者可以上Serilog的官方网站了解这里就不详细介绍了。总结本文介绍了基于.NET Core通用主机Generic Host的服务宿主框架的设计与实现并给出了三个应用场景的案例代码详细代码可以点击文后的下载链接进行下载。有关.NET Core Generic Host以及本文介绍的框架还有很多高级功能和特殊用法有需要的读者可以在本文留言共同探讨。原文地址http://sunnycoding.cn/2019/01/29/implementing-generic-purpose-service-hosting-framework-2/.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com