什么是Generic Host ?

这是在Asp.Net Core 2.1加入了一种新的Host,现在2.1版本的Asp.Net Core中,有了两种可用的Host。

  • Web Host –适用于托管Web程序的Host,就是我们所熟悉的在Asp.Net Core应用程序的Mai函数中用CreateWebHostBuilder创建出来的常用的WebHost。

  • Generic Host (ASP.NET Core 2.1版本才有) – 适用于托管非 Web 应用(例如,运行后台任务的应用)。 在未来的版本中,通用主机将适用于托管任何类型的应用,包括 Web 应用。 通用主机最终将取代 Web 主机,这大概也是这种类型的主机叫做通用主机的原因,在本博客中,我们将结合源码,讨论通用主机的工作原理。

为什么要用通用主机?

通用主机,让我可以用编写Asp.Net Core的思想(例如控制反转、依赖注入、IOC容器)来简化控制台应用程序的创建(个人见解),主机负责程序的启动和生存周期的管理,这对于不处理HTTP请求的应用程序非常有用(处理HTTP请求的是Web应用程序,用Web Host托管),通用主机的目标是将HTTP管道从Web Host中脱离出来,使得Asp.Net Core的那套东西也适用于其他.Net Core程序。

Demo下载

在开始跟随我分析通用主机之前,大家可以到Github下载这个官方Demo。

https://github.com/aspnet/Docs/tree/master/aspnetcore/fundamentals/host/generic-host/samples/

如果觉得下载一整个比较慢,可以从我的这个Github仓库下载,没有其他多余内容,国内Github比较慢,如果你从官方那个仓库下载可能会需要很长时间甚至失败。

https://github.com/liuzhenyulive/Generic-Host-Demo

Generic Host 和Web Host 对比

首先,大家打开下载下来的这个官方Demo,进入Main函数。

可以看到,这简直就是一个精简版的Asp.Net Core应用程序,对这个Main函数中出现的所有方法,大家对Asp.Net Core Web应用程序比较熟悉,所以我与Asp.net core 的Webhost做了一个对比,来帮助大家找找感觉。

通用主机 Web主机
new HostBuilder() WebHost.CreateDefaultBuilder(args)
ConfigureAppConfiguration
(用于配置Configuration)
WebHost也有这个方法,只是大家默认可能没有调用。
ConfigureServices
(用于配置Service,也就是依赖注入)
WebHost其实也有ConfigureServices方法,可以这么调用。

但是我们一般很少这么用,一般都是放在Startup的ConfigureServices方法中进行依赖注入。
ConfigureLogging
(是本应用程序所需要的配置,非必需)
WebHost还是有!
builder.RunConsoleAsync()

RunConsoleAsync中其实是对hostbuilder进行
Builder然后Run
CreateWebHostBuilder(args).Build().Run();

也就是Main函数中的Build().Run();


无无无无
Startup中的Configure()方法
Asp.net core在此方法中进行Http请求管道的配置

综上对比,我做了如下概括!

  1. 通用主机(Generic Host)有的 Web Host都有。
  2. Web Host的Http Pipeline即Startup.Configure() 在通用主机中没有。

这就应证了开头所说的:通用主机的目标是将HTTP管道从Web Host中脱离出来,使得Asp.Net Core的那套东西也适用于其他.Net Core程序。

如何使用?

Run函数解读

我觉得要知道怎么用,那么我们就首先要知道Host的Run方法内到底是在执行什么?

所以我们深入源码,一路F12!

builder.RunConsoleAsync(); =>hostBuilder.UseConsoleLifetime().Build().RunAsync(cancellationToken);=> await host.StartAsync(token);

总算找到了,最关键的在这里。

public async Task StartAsync(CancellationToken cancellationToken = default (CancellationToken))
{
this._logger.Starting();
TaskCompletionSource<object> completionSource1 = new TaskCompletionSource<object>(); ref CancellationToken local = ref cancellationToken;
TaskCompletionSource<object> completionSource2 = completionSource1;
local.Register((Action<object>) (obj => ((TaskCompletionSource<object>) obj).TrySetCanceled()), (object) completionSource2); IHostLifetime hostLifetime1 = this._hostLifetime;
TaskCompletionSource<object> completionSource3 = completionSource1;
hostLifetime1.RegisterDelayStartCallback((Action<object>) (obj => ((TaskCompletionSource<object>) obj).TrySetResult((object) null)), (object) completionSource3); IHostLifetime hostLifetime2 = this._hostLifetime;
ApplicationLifetime applicationLifetime = this._applicationLifetime;
hostLifetime2.RegisterStopCallback((Action<object>) (obj => (obj as IApplicationLifetime)?.StopApplication()), (object) applicationLifetime); object task = await completionSource1.Task;

this._hostedServices = this.Services.GetService<IEnumerable<IHostedService>>(); foreach (IHostedService hostedService in this._hostedServices) await hostedService.StartAsync(cancellationToken).ConfigureAwait(false

);

      this._applicationLifetime?.NotifyStarted();
this._logger.Started();
}

知道大家都喜欢Yellow色,所以我用Yellow把最关键的代码标示出来了,那么这些代码有什么含义呢?

this._hostedServices = this.Services.GetService<IEnumerable<IHostedService>>();

这一行的意思是,从容器中取出所有实现了IHostedService的服务。

这就意味着,我们实现了IHostedService后,需要把该Service注册到IOC容器中。

foreach (IHostedService hostedService in this._hostedServices)
      await hostedService.StartAsync(cancellationToken).ConfigureAwait(false);

执行每个服务的StartAsync方法。

所以,大家是不是冥冥中猜到了怎么用的呢?

我总结的步骤如下:

  1. 自定义一个Service,继承 IHostedService接口。
  2. 实现 IHostedService的StartAsync方法,把需要执行的任务放到这个方法中。
  3. 把该服务注册到IOC容器(ServiceCollection)中。

自定义任务的运行

对于步骤1和2,对应的代码如下:

public class PrintTextToConsoleService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private readonly IOptions<AppConfig> _appConfig;
private Timer _timer; public PrintTextToConsoleService(ILogger<PrintTextToConsoleService> logger, IOptions<AppConfig> appConfig)
{
_logger = logger;
_appConfig = appConfig;
} public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Starting"); _timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds()); return Task.CompletedTask;
} private void DoWork(object state)
{
_logger.LogInformation($"Background work with text: {_appConfig.Value.TextToPrint}");
} public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Stopping."); _timer?.Change(Timeout.Infinite, ); return Task.CompletedTask;
} public void Dispose()
{
_timer?.Dispose();
}
}

可以看到,在StartAsync中,定义了一个定时任务,带定时任务每五秒执行一次DoWork方法。

在DoWork方法中,日志记录器记录了一段内容。

因为在Main方法中,对Log进行了如下的配置。

所以,一旦日志记录了内容,该内容就会在控制台中输出。

对于步骤3,对应的代码如下

public static async Task Main(string[] args)
{
var builder = new HostBuilder() //实例化一个通用主机
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: true);
config.AddEnvironmentVariables(); if (args != null)
{
config.AddCommandLine(args);
}
}) //配置Configuration
.ConfigureServices((hostContext, services) =>
{
services.AddOptions();
services.Configure<AppConfig>(hostContext.Configuration.GetSection("AppConfig"));
services.AddSingleton

<IHostedService, PrintTextToConsoleService>

();
}) //配置Service (依赖注入)
.ConfigureLogging((hostingContext, logging) => {
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
}); //配置Log (本项目中要利用Log把内容在控制台输出) await builder.RunConsoleAsync(); //在控制台应用程序中运行通用主机
}

黄色部分,把实现了IHostedService接口的PrintTextToConsoleService注册到容器中。

F5 运行

可以看到,控制台中,每五秒就有一次内容输出,说明DoWork方法没五秒被执行了一次,也说明PrintTextToConsoleService的StartAsync被成功调用了。

希望本文对帮助大家理解通用主机能够有所帮助,如果对.Net Core的源码分析、潮流新技术感兴趣

欢迎关注我

不定期推出实用干活,谢谢!

参考文献

https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-2.1

Asp.net Core 2.1新功能Generic Host(通用主机),了解一下的更多相关文章

  1. net Core 2.1新功能Generic Host(通用主机)

    net Core 2.1新功能Generic Host(通用主机) http://doc.okbase.net/CoderAyu/archive/301859.html 什么是Generic Host ...

  2. Razor Page–Asp.Net Core 2.0新功能

    Razor Page介绍 前言 上周期待已久的Asp.Net Core 2.0提前发布了,一下子Net圈热闹了起来,2.0带来了很多新的特性和新的功能,其中Razor Page引起我的关注,作为web ...

  3. ASP.NET Core 2.0 新功能汇总

    前言 ASP.NET Core 的变化和发展速度是飞快的,当你发现你还没有掌握 ASP.NET Core 1.0 的时候, 2.0 已经快要发布了,目前 2.0 处于 Preview 1 版本,意味着 ...

  4. Vue.js与 ASP.NET Core 服务端渲染功能整合

    http://mgyongyosi.com/2016/Vuejs-server-side-rendering-with-aspnet-core/ 原作者:Mihály Gyöngyösi 译者:oop ...

  5. 探索ASP.Net Core 3.0系列六:ASP.NET Core 3.0新特性启动信息中的结构化日志

    前言:在本文中,我将聊聊在ASP.NET Core 3.0中细小的变化——启动时记录消息的方式进行小的更改. 现在,ASP.NET Core不再将消息直接记录到控制台,而是正确使用了logging 基 ...

  6. .NET平台系列8 .NET Core 各版本新功能

    系列目录     [已更新最新开发文章,点击查看详细] .NET Core 自2016年6月27日发布第一个正式版本以来,它主打的跨平台和高性能特效吸引了许多开发者,包括Java.PHP等语言的开发者 ...

  7. asp.net core mvc实现伪静态功能

    在大型网站系统中,为了提高系统访问性能,往往会把一些不经常变得内容发布成静态页,比如商城的产品详情页,新闻详情页,这些信息一旦发布后,变化的频率不会很高,如果还采用动态输出的方式进行处理的话,肯定会给 ...

  8. ASP.NET Core 实现带认证功能的Web代理服务器

    引言 最近在公司开发了一个项目,项目部署架构图如下: 思路 如图中文本所述,公司大数据集群不允许直接访问外网,需要一个网关服务器代理请求,本处服务器A就是边缘代理服务器的作用. 通常技术人员最快捷的思 ...

  9. 在ASP.NET Core中给上传图片功能添加水印

    在传统的.NET框架中,我们给图片添加水印有的是通过HttpModules或者是HttpHandler,然后可以通过以下代码添加水印: var image = new WebImage(imageBy ...

随机推荐

  1. ArrayList源码分析超详细

    ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要分析的类(ztrl+N查找ArraLi ...

  2. 使用Android Studio Gradle实现友盟多渠道打包

    最新项目中要求在友盟后台看到不同渠道的统计,Android大大小小的应用市场要几百个,要一个一个手工打包那一天也干不完,还好是有大牛的,弄出了好多解决方法,就Gradle做一下记录和分享,首先看一些理 ...

  3. QT中对内存的管理

    在QT中,一切继承自QT自有类的类,如果存在parent指针,那么当parent指针delete时,该类中的指针(它们都属于parent指针对应的child指针)也会被delete.综上,如果我们的窗 ...

  4. Flask入门之自定义过滤器(匹配器)

    1.  动态路由的匹配器? 不知道这种叫啥名,啥用法,暂且叫做匹配器吧. Flask自带的匹配器可以说有四种吧(保守数字,就我学到的) 动态路由本身,可以传任何参数字符串或者数字,如:<user ...

  5. linux学习(JDK,Tomcat,nginx)安装

    最近学习了在linux,在上面搭建了一个tomcat的服务器,后来又使用nginx进行反向代理了一下下,希望文章对初学者有所帮助. 1.安装JDK.(采用jdk的rpm包进行安装) 安装jdk之前需要 ...

  6. ubuntu18.04安装安装JDK

    1.前提准备: 下载JDK:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 2. ...

  7. WBS 与 甘特图

    WBS:工作分解结构(Work Breakdown Structure) 创建WBS:创建WBS是把项目 交付成果和项目工作分解成较小的,更易于管理的组成部分的过程. WBS是项目管理重要的专业术语之 ...

  8. golang项目中使用条件编译

    golang项目中使用条件编译 C语言中的条件编译 golang中没有类似C语言中条件编译的写法,比如在C代码中可以使用如下语法做一些条件编译,结合宏定义来使用可以实现诸如按需编译release和de ...

  9. 前端为什么非要动静分离 说一下CDN托管的意义

    大型Web应用对速度的追求并没有止步于仅仅利用浏览器缓存,因为浏览器缓存始终只是为了提升二次访问的速度,对于首次访问的加速,我们需要从网络层面进行优化,最常见的手段就是CDN(Content Deli ...

  10. (WCF初体验)WCF的认证和消息保护

    最近做WCF开发,有个需求是在服务端做认证,网上查资料了解到可以用UserName和Password 来做认证,只需要写好配置文件和在服务端写好验证类就行了,但是网上普遍的博文都是需要用证书,而我自己 ...