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是如何启动的。通过上面代码,我们看出整个过程分为这么几步来执行:

  1. 创建了一个缺省的Builder,Host.CreateDefaultBuilder(args)
  2. 配置WebHost 的缺省信息,.ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});
  3. 对这个创建的WebHost进行了两个操作,Build()一下,然后Run()

这就结束了,看似很简单,其实一脸闷逼。这个过程到底做了哪些工作,我们还需要通过源码进行深入的解析。

Github源码地址:https://github.com/dotnet/aspnetcore/tree/release/3.1

扩展源码:https://github.com/dotnet/extensions/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、创建缺省的WebHostBuilderConfigureWebHostDefaults内部调用ConfigureWebHost方法,创建了一个WebHostBuilder,这是关键之处,这里我们就形成了基于HostBuilderWebHostBuilder的承载系统。在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 启动过程(一)的更多相关文章

  1. 《ASP.NET Core 高性能系列》ASP.NET Core的启动过程(1)

    一.一切从头开始 简述:知道事情的真相就应该从头 开始,下面我们代码先行 public class Program { public static void Main(string[] args) { ...

  2. 如何在ASP.NET Core程序启动时运行异步任务(1)

    原文:Running async tasks on app startup in ASP.NET Core (Part 1) 作者:Andrew Lock 译者:Lamond Lu 背景 当我们做项目 ...

  3. 如何在ASP.NET Core程序启动时运行异步任务(3)

    原文:Running async tasks on app startup in ASP.NET Core (Part 3) 作者:Andrew Lock 译者:Lamond Lu 之前我写了两篇有关 ...

  4. ASP.NET Core 的启动和运行机制

    目录 ASP .NET Core 的运行机制 ASP .NET Core 的启动 ASP .NET Core 的管道和中间件 参考 ASP .NET Core 的运行机制 Web Server: AS ...

  5. 一题多解,ASP.NET Core应用启动初始化的N种方案[下篇]

    [接上篇]"天下大势,分久必合,合久必分",ASP.NET应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系 ...

  6. 如何在ASP.NET Core程序启动时运行异步任务(2)

    原文:Running async tasks on app startup in ASP.NET Core (Part 2) 作者:Andrew Lock 译者:Lamond Lu 在我的上一篇博客中 ...

  7. 在 ASP.NET Core 程序启动前运行你的代码

    一.前言 在进行 Web 项目开发的过程中,可能会存在一些需要经常访问的静态数据,针对这种在程序运行过程中可能几乎不会发生变化的数据,我们可以尝试在程序运行前写入到缓存中,这样在系统后续使用时就可以直 ...

  8. 一题多解,ASP.NET Core应用启动初始化的N种方案[上篇]

    ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来.在ASP.NET Core的发展历史上先后出现了三种应用承载 ...

  9. (1)ASP.NET Core 应用启动Startup类简介

    1.前言 Core与早期版本的 ASP.NET 对比,配置应用程序的方式的 Global.asax.FilterConfig.cs和RouteConfig.cs 都被Program.cs 和 Star ...

随机推荐

  1. 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)

    参考网址:图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) - 51CTO.COM 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath ...

  2. 【mysql】截取查询分析

    1. 慢查询日志 1.1 是什么 (1)MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL ...

  3. java String数组城市

    String[] citys = {"北京","天津","河北","山西","内蒙古"," ...

  4. FLV简介

    FLV (Flash Video) 是由 Adobe 公司推出的一种封装格式,主要用于流媒体系统. FLV 封装的媒体文件具有体积轻巧.封装播放简单等特点,很适合网络应用. 目前各浏览器普遍使用 Fl ...

  5. 并发编程之:JMM

    并发编程之:JMM 大家好,我是小黑,一个在互联网苟且偷生的农民工. 上一期给大家分享了关于Java中线程相关的一些基础知识.在关于线程终止的例子中,第一个方法讲到要想终止一个线程,可以使用标志位的方 ...

  6. 2020年秋游戏开发-flappy bird

    此作业要求参考https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11577 GitHub地址为https://github.com/15011 ...

  7. Linkerd 2.10(Step by Step)—3. 自动轮换控制平面 TLS &Webhook TLS 凭证

    Linkerd 2.10 系列 快速上手 Linkerd v2 Service Mesh(服务网格) 腾讯云 K8S 集群实战 Service Mesh-Linkerd2 & Traefik2 ...

  8. 并发编程之:AQS源码解析

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 在Java并发编程中,经常会用到锁,除了Synchronized这个JDK关键字以外,还有Lock接口下面的各种锁实现,如重入锁ReentrantLo ...

  9. Kubernetes的安装部署

    前言:简述kubernetes(k8s)集群 k8s集群基本功能组件由master和node组成. master节点上主要有kube-apiserver.kube-scheduler.kube-con ...

  10. 性能测试工具JMeter 基础(九)—— 测试元件: 逻辑控制器之交替控制器

    交替控制器:根据被控制器触发执行次数,去依次执行控制器下的子节点(逻辑控制器.采样器),可以由线程组的线程数.循环次数.逻辑控制器触发. 交替控制器(lnterleave Controller) 简单 ...