01 ASP.NET Core 3 启动过程(一)
ASP.NET Core 3 启动过程(一)
最近又忙于各种扯淡,今天来一个需求,明天又来一个需求,后天需求又变了,这可能是很多人遇到的情况。正在紧张的忙碌着,突然一个信息把所有计划打乱了,“这个项目很重要,要订100套,手里面的活停了,赶紧做这个”,其实所谓的100套也不过是用户想让先改他的需求的借口。这可能就是很多像我一样苦逼的程序员面对的工作,其实就是这样,这就是小公司特别是老板说了算的小公司现状。本想着打算把WPF单机项目的框架设计完之后,就回头继续处理ASP.NET Core的坑,结果WPF那边有人离职了(只剩我一个了),项目又着急(没有不急的项目),只能打道回府,停下系统这边的所有开发工作转而继续WPF的开发。即便如此,学习的脚步还是不能停下,停下就意味着没有了价值。
牢骚说完,我们还要继续努力工作,这是起码的职业素养。今天呢,我们就来看一看ASP.NET Core的启动过程。
创建项目,这里我们采用.NetCore3.1,创建空项目,项目如下:
我们通过项目的属性不难看出,ASP.NET Core 3.1本质是一个控制台应用程序。程序的入口是Program-Main
函数,这个函数内部是通过链式语法执行了一系列动作。那么我们就需要研究一下这个过程,分析一下ASP.NET Core 3.1是如何启动的。通过上面代码,我们看出整个过程分为这么几步来执行:
- 创建了一个缺省的Builder,
Host.CreateDefaultBuilder(args)
- 配置WebHost 的缺省信息,
.ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});
- 对这个创建的WebHost进行了两个操作,
Build()
一下,然后Run()
这就结束了,看似很简单,其实一脸闷逼。这个过程到底做了哪些工作,我们还需要通过源码进行深入的解析。
Github源码地址:https://github.com/dotnet/aspnetcore/tree/release/3.1
针对上面的步骤,我们看一下源码
1、创建缺省的Builder,Host.CreateDefaultBuilder(args)
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
//实例化一个HostBuilder
var builder = new HostBuilder();
//设置当前运行目录为根目录
builder.UseContentRoot(Directory.GetCurrentDirectory());
//对Host进行配置
builder.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNET_");
if (args != null)
{
config.AddCommandLine(args);
}
});
//对应用程序进行配置
builder.ConfigureAppConfiguration((hostingContext, config) =>
{
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
{
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null)
{
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
//配置日志
.ConfigureLogging((hostingContext, logging) =>
{
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
// IMPORTANT: This needs to be added *before* configuration is loaded, this lets
// the defaults be overridden by the configuration.
if (isWindows)
{
// Default the EventLogLoggerProvider to warning or above
logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
}
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
if (isWindows)
{
// Add the EventLogLoggerProvider on windows machines
logging.AddEventLog();
}
})
//使用缺省的服务提供者
.UseDefaultServiceProvider((context, options) =>
{
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
});
//返回实例化的HostBuilder
return builder;
}
2、创建缺省的WebHostBuilder
,ConfigureWebHostDefaults
内部调用ConfigureWebHost
方法,创建了一个WebHostBuilder
,这是关键之处,这里我们就形成了基于HostBuilder
和WebHostBuilder
的承载系统。在ConfigureWebHost
内部,创建一个与HostBuilder
有关联的WebHostBuilder
,然后调用传入的委托,配置了WebHost
并且设置了WebHostBuilder
,这个设置就是代码UseStartup<Startup>()
。最后配置WebHostBuilder
作为HostBuilder
的一个服务HostedService
。
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
return builder.ConfigureWebHost(webHostBuilder =>
{
WebHost.ConfigureWebDefaults(webHostBuilder);
configure(webHostBuilder);
});
}
public static class GenericHostWebHostBuilderExtensions
{
public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
var webhostBuilder = new GenericWebHostBuilder(builder);
configure(webhostBuilder);
builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>());
return builder;
}
}
public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
{
_configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
return this;
}
这里面还有一个方法WebHost.ConfigureWebDefaults(webHostBuilder);
对GenericWebHostBuilder
进行了配置。内部代码:
internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((ctx, cb) =>
{
if (ctx.HostingEnvironment.IsDevelopment())
{
StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);
}
});
builder.UseKestrel((builderContext, options) =>
{
options.Configure(builderContext.Configuration.GetSection("Kestrel"));
})
.ConfigureServices((hostingContext, services) =>
{
// Fallback
services.PostConfigure<HostFilteringOptions>(options =>
{
if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
{
// "AllowedHosts": "localhost;127.0.0.1;[::1]"
var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
// Fall back to "*" to disable.
options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
}
});
// Change notification
services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(
new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration));
services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase))
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// Only loopback proxies are allowed by default. Clear that restriction because forwarders are
// being enabled by explicit configuration.
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>();
}
services.AddRouting();
})
.UseIIS()
.UseIISIntegration();
}
3、调用Build方法和Run方法
/// <summary>
/// Run the given actions to initialize the host. This can only be called once.
/// </summary>
/// <returns>An initialized <see cref="IHost"/></returns>
public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException("Build can only be called once.");
}
_hostBuilt = true;
BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider();
return _appServices.GetRequiredService<IHost>();
}
/// <summary>
/// Runs an application and block the calling thread until host shutdown.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to run.</param>
public static void Run(this IHost host)
{
host.RunAsync().GetAwaiter().GetResult();
}
这里面主要的工作就是创建了一个承载系统,完成应用程序的启动和生命周期的管理。开启服务之后,然后就可以处理请求了。
那么什么是承载系统,承载系统到底做什么,下次我们继续。
01 ASP.NET Core 3 启动过程(一)的更多相关文章
- 《ASP.NET Core 高性能系列》ASP.NET Core的启动过程(1)
一.一切从头开始 简述:知道事情的真相就应该从头 开始,下面我们代码先行 public class Program { public static void Main(string[] args) { ...
- 如何在ASP.NET Core程序启动时运行异步任务(1)
原文:Running async tasks on app startup in ASP.NET Core (Part 1) 作者:Andrew Lock 译者:Lamond Lu 背景 当我们做项目 ...
- 如何在ASP.NET Core程序启动时运行异步任务(3)
原文:Running async tasks on app startup in ASP.NET Core (Part 3) 作者:Andrew Lock 译者:Lamond Lu 之前我写了两篇有关 ...
- ASP.NET Core 的启动和运行机制
目录 ASP .NET Core 的运行机制 ASP .NET Core 的启动 ASP .NET Core 的管道和中间件 参考 ASP .NET Core 的运行机制 Web Server: AS ...
- 一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]
[接上篇]"天下大势,分久必合,合久必分",ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系 ...
- 如何在ASP.NET Core程序启动时运行异步任务(2)
原文:Running async tasks on app startup in ASP.NET Core (Part 2) 作者:Andrew Lock 译者:Lamond Lu 在我的上一篇博客中 ...
- 在 ASP.NET Core 程序启动前运行你的代码
一.前言 在进行 Web 项目开发的过程中,可能会存在一些需要经常访问的静态数据,针对这种在程序运行过程中可能几乎不会发生变化的数据,我们可以尝试在程序运行前写入到缓存中,这样在系统后续使用时就可以直 ...
- 一题多解,ASP.NET Core应用启动初始化的N种方案[上篇]
ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来.在ASP.NET Core的发展历史上先后出现了三种应用承载 ...
- (1)ASP.NET Core 应用启动Startup类简介
1.前言 Core与早期版本的 ASP.NET 对比,配置应用程序的方式的 Global.asax.FilterConfig.cs和RouteConfig.cs 都被Program.cs 和 Star ...
随机推荐
- 【权限管理】springboot集成security
摘自: https://www.cnblogs.com/hhhshct/p/9726378.html https://blog.csdn.net/weixin_42849689/article/det ...
- LeetCoded第25题题解--K个一组翻转链表--java--链表
链表 单链表:链表中的每个元素实际上是一个单独的对象,而所有对象都通过每个元素的引用字段链接在一起. 双链表:与单链表不同的是,双链表的每个节点都含有两个引用字段. 链表优点 灵活分配内存空间 能在O ...
- 入门数据结构与算法,看这一个就够了,知识点+LeetCode实战演练
本笔记来自拉钩教育300分钟搞定算法面试 算法与数据结构 要掌握一种数据结构,就必须要懂得分析它的优点和缺点. 在考虑是否应当采用一种数据结构去辅助你的算法时,请务必考虑它的优缺点,看看它的缺点是否会 ...
- mzy git学习,初识git(一)
GIT学习 git工作区.暂存区.本地库.远程库 工作区:实际上我们工作的地方,进行写代码或者文件的地方. 暂存区:我们执行了git add 操作之后,就会被提交到暂存区. 本地库:其实最后我们需要执 ...
- dotnet OpenXML 读取 PPT 内嵌 ole 格式 Excel 表格的信息
在 Office 中,可以在 PPT 里面插入表格,插入表格有好多不同的方法,对应 OpenXML 文档存储的更多不同的方式.本文来介绍如何读取 PPT 内嵌 ole 格式的 xls+ 表格的方法 在 ...
- FeignClient注解属性configuration不生效问题排查思路
FeignClient注解属性configuration不生效问题排查思路 问题背景 我们知道,"如果需要自定义单个Feign配置,Feign的@Configuration 注解的类不能与@ ...
- 源码解析Spring AOP的加载与生效
本次博主主要进行Spring AOP这里的解析,因为在工作中使用后,却不知道背后的实现原理并在使用的过程中发现了一些认知缺陷,所以决定写这么一篇文章以供大家参考参考,进入正题. 本次博主使用了@Asp ...
- [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型
[源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 目录 [源码解析] 深度学习流水线并行 PipeDream(3)--- 转换模型 0x00 摘要 0x01 前言 1.1 改 ...
- Activiti 学习(一)—— Activiti 基础
工作流概述 在一个公司中,每一项业务的开始和结束,都可以理解为一个工作流,例如,公司的费用报销的基本流程如下: 如图所示的工作流:员工先提出费用报销申请,提交该申请给部门领导,部门领导审批后,再提交给 ...
- Spring全自动AOP和项目加入jar包
一.jar可以引进项目中,复制到路下后,要add as library,加载到工作空间中才能引入: 也jar包放在硬盘的项目目录外面,可以多个项目引入共用: 二.xml配置 1.aop全自动配置 2. ...