什么是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. Dubbo学习-源码学习

    Dubbo概述 dubbo框架提供多协议远程调用,服务提供方可以是分布式部署.dubbo框架可以很简单的帮我们实现微服务. 此处援引官网上图片 dubbo分为客户端和服务提供方 服务方将服务注册到注册 ...

  2. Xapth 添加注释头

    private static void updateMybatisXml(String url, String username, String password) { DocumentBuilder ...

  3. JavaScirpt的this指向 apply().call(),bind()个人笔记

    写在前头: 本站内容为个人学习记录,纯属个人观点,不喜勿喷,欢迎指正! 笔记记录缘由:JavaScript的流行趋势已经势不可挡,衍生的AngularJs,Node.js,BootStrmp中小企业的 ...

  4. 洛谷 P1054 解题报告

    P1054 等价表达式 题目描述 明明进了中学之后,学到了代数表达式.有一天,他碰到一个很麻烦的选择题.这个题目的题干中首先给出了一个代数表达式,然后列出了若干选项,每个选项也是一个代数表达式,题目的 ...

  5. pycharm的安装及用法

    俗话说的好:"工欲善其事,必先利其器",程序员写代码,肯定不能在编译器里面写,这有一个全宇宙最好的python编译器,叫做pycharm ,他的优点呢,大家可以看一下: 优点:1, ...

  6. 分布式架构真正适用于大型互联网项目的架构! dubbo+zookeeper+springmvc+mybatis+shiro+redis

     分类: 分布式技术(3)    目录(?)[+]   平台简介 Jeesz是一个分布式的框架,提供项目模块化.服务化.热插拔的思想,高度封装安全性的Java EE快速开发平台. Jeesz本身集成D ...

  7. 解决_CRT_SECURE_NO_WARNINGS警告

    VS中: 工程属性->属性->配置属性->C/C++->命令行 在命令行中添加一行: /D _CRT_SECURE_NO_WARNINGS OK!

  8. 利用javaScript实现鼠标在文字上悬浮时弹出悬浮层

    在人人,CSDN等一些网站,当鼠标在某个东西上悬浮时,会弹出一个悬浮层,鼠标移开悬浮层消失.比如说CSDN的通知(应该是进入写新文章的页面后页面上方的那个铃铛),具体是什么实现的呢?上代码: < ...

  9. 10.app后端选择什么开发语言

    在qq上,经常看到有创业团队的创始人一直都招不到技术人员,除了项目的因素外,很大的原因就是所需要掌握的开发语言偏门.通过阅读本文,详细了解选择开发语言的核心原则,使各位心里对开发语言的选择更加有数. ...

  10. Python中从SQL型数据库读写dataframe型数据

    Python的pandas包对表格化的数据处理能力很强,而SQL数据库的数据就是以表格的形式储存,因此经常将sql数据库里的数据直接读取为dataframe,分析操作以后再将dataframe存到sq ...