在这个特殊的春节,大家想必都在家出不了们,远看已经到了回城里上班的日子,但是因为一只蝙蝠的原因导致我们无法回到工作岗位,大家可能有的在家远程办公,有些在家躺着看书,有的是在家打游戏;在这个特殊无聊的日志,我果断从无聊的被窝中 开启了流量共享wifi 来进行.net core 3.1 源代码的解读和学习,并且把学习到的东西分享给大家。

疑问

刚刚接触ASP.NET CORE 项目的同学可能会有如下疑问:

  • ASP.NET CORE 项目的启动过程是怎么样的?
  • 为什么ASP.NET CORE项目可以在控制台中运行启动后变成了一个网站程序?

现在我这里使用.NETCORE 3.1 最新稳定发布版本来进行以上问题的解析,带大家解决以上问题的疑惑,学习完大家可以对ASP.NETCORE 项目会有一个不一样的理解和领悟.


启动过程

刚刚接触ASP.NET core 的同学们估计都会觉得和之前的ASP.NET 设计大不一样,代码风格也有很大的变化,以前的ASP.NET 是全家桶框架模式,里面包含了所有的实现,你用的到的用不到的都集成在里面;然而ASP.NET CORE 框架做了大的改变,以最小化抽象设计,通过扩展方法完成易用性扩展.

解读过源代码的同学们都可以发现大多api都是最小化单元抽象接口方式进行设计,其他复杂的方法api都是通过扩展方法进行扩展提供,这也是.NET Core 高效易扩展的一大优势原因.

对于ASP.NET Core应用程序来说,我们要记住非常重要的一点是:其本质上是一个独立的控制台应用,它并不是必需在IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Core跨平台的基石)。ASP.NET Core应用程序拥有一个内置的Self-Hosted(自托管)Web Server(Web服务器),用来处理外部请求。

不管是托管还是自托管,都离不开Host(宿主)。在ASP.NET Core应用中通过配置并启动一个Host来完成应用程序的启动和其生命周期的管理。而Host的主要的职责就是Web Server的配置和Pilpeline(请求处理管道)的构建。

我们现在来创建一个ASP.NETCORE WEB 项目 步骤如下

文件-> 新建 -> 项目 -> 选择ASP.Net Core Web应用程序 -> 选择.NETCORE 3.1 框架 如图:

创建项目后我们从Program 类中可以看到以下代码:

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)//开启一个默认的通用主机Host建造者
.ConfigureWebHostDefaults(webBuilder =>
{
// 从前缀为“ASPNETCORE” 环境变量加载WEB主机配置
// 默认是Kestrel 设置为web 服务器并对其进行默认配置, 支持iis集成
//注册系统启动所使用的组件,比如配置的组件,容器的组件等
webBuilder.UseStartup<Startup>();
});
}

查看以上代码可以发现 Main 方法中代码很简单 ,清晰可见

CreateHostBuilder(args) :方法创建了一个IHostBuilder 抽象对象,创建过程包含CreateDefaultBuilder(args) :开启创建一个默认的通用宿主机Host建造者,再通过ConfigureWebHostDefaults()方法配置开启默认的Kestrel 为默认的Web服务器并对其进行默认配置,并集成对iis的集成

Build() :负责创建IHost,看过源代码的同学可以发现Build的过程 会配置各种东西,本身通过管道模式进行了一系列的默认或者自定义的配置以及服务注册的构建(下面会详细讲解)

Run() :启动IHost

所以,ASP.NET Core应用的启动本质上是启动作为宿主的Host对象。

其主要涉及到两个关键对象IHostBuilderIHost,它们的内部实现是ASP.NET Core应用的核心所在。下面我们就结合源码并梳理调用堆栈来一探究竟!

源代码详细图如下:

从上图中我们可以看出CreateDefaultBuilder()方法主要干了五件大事:
  • UseContentRoot:指定Web host使用的content root(内容根目录),比如Views。默认为当前应用程序根目录。
  • ConfigureHostConfiguration :启动时宿主机需要的环境变量等相关,支持命令行
  • ConfigureAppConfiguration:设置当前应用程序配置。主要是读取 appsettinggs.json 配置文件、开发环境中配置的UserSecrets、添加环境变量和命令行参数 。
  • ConfigureLogging:读取配置文件中的Logging节点,配置日志系统。
  • UseDefaultServiceProvider:设置默认的依赖注入容器。
从图中可以看出CreateDefaultBuilder 后调用了ConfigureWebHostDefaults 方法,该方法默认主要做了以下几个事情
  • UseStaticWebAssets:静态文件环境的配置启用
  • UseKestrel:开启Kestrel为默认的web 服务器.
  • ConfigureServices:服务中间件的注册,包含路由的中间件的注册
  • UseIIS:对iis 集成的支持
  • UseStartup:程序Startup 启动,该启动类中可以注册中间件、扩展第三方中间件,以及相关应用程序配置的处理等等操作
现在所有的配置都已经配置创建好了,接下来我们来看看Build 方法主要做了哪些不为人知的事情,先来看下源代码
/// <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>();
}

从代码中可以发现有一个_hostBuilt 的变量,细心的同学可以发现该变量主要是用于控制是否build 过,所以这里可以大胆猜测只能build 一次该Host;现在看下源代码解析图:

经过查看源代码得到的执行结构如上,因此我把代码改造成如下结构。

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)//开启一个默认的通用主机Host建造者
.ConfigureAppConfiguration(config => {
//注册应用程序内所使用的配置文件,比如数据库链接等等
Console.WriteLine("ConfigureAppConfiguration");
})
.ConfigureServices(service =>
{
//注册服务中间件等操作
Console.WriteLine("ConfigureServices");
})
.ConfigureHostConfiguration(builder => {
//启动时需要的组件配置等,比如监听的端口 url地址等
Console.WriteLine("ConfigureHostCOnfiguration");
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

Startup 代码如下:

public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
Console.WriteLine("Startup ");
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
Console.WriteLine("ConfigureServices");
services.AddControllersWithViews();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
Console.WriteLine("Configure");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

通过执行发现执行代码顺序正如我们源代码的执行顺序一致,执行顺序如下图:

为何通过控制台命令运行后自动启动了一个网站程序?

以前ASP.NET web项目是需要搭建在iis 中托管运行,但是ASP.NETCORE 项目可以直接通过命令行进行托管运行,运行后可以直接浏览器打开,你们有没有考虑过为什么?,细心的同学查看项目属性也会发现项目的输出类型也是控制台项目,如图:

查看这图,有没有发现很神奇,为什么输出类型竟然可以通过控制台命令行进行启动项目呢?

在上面的源代码分析过程中可以发现启动时会启动一个Kestrel 服务器(ConfigureWebHostDefaults方法中会调用UseKestrel),所以命令后启动一个控制台应用程序后相当于启动了一台web服务器;下面简要的概括下Kestrel 服务器的优势:

  • Kestrel:Kestrel 是个精简高效的 HttpServer,以包形式提供,自身不能单独运行。

内部封装了对 libuv 的调用,作为I/O底层,屏蔽各系统底层实现差异;有了Kestrel才能真正的实现跨平台.

好了,想必同学们到这里已经对上面 两个疑惑有了清晰的答案了。这里我抛出一个疑问,看了上面的代码解读,大家有没有发现ASP.NET CORE 和ASP.NET 有了很大的不同,这是什么样的设计改进呢?敬请期待下期我们一起来学习ASP.NET CORE 的牛逼的管道模型.

ASP.NET CORE 启动过程及源码解读的更多相关文章

  1. 各类最新Asp .Net Core 项目和示例源码

    1.网站地址:http://www.freeboygirl.com2.网站Asp .Net Core 资料http://www.freeboygirl.com/blog/tag/asp%20net%2 ...

  2. SpringBoot启动嵌入式tomcat源码解读

    一.SpringBoot自动拉起Tomcat SpringBoot框架是当前比较流行的java后端开发框架,与maven结合大大简化了开发人员项目搭建的步骤,我们知道SpringBoot的启动类启动后 ...

  3. spring mvc 启动过程及源码分析

    由于公司开源框架选用的spring+spring mvc + mybatis.使用这些框架,网上都有现成的案例:需要那些配置文件.每种类型的配置文件的节点该如何书写等等.如果只是需要项目能够跑起来,只 ...

  4. ASP.Net Core Configuration 理解与源码分析

    Configuration 在ASP.NET Core开发过程中起着很重要的作用,这篇博客主要是理解configuration的来源,以及各种不同类型的configuration source是如何被 ...

  5. Hyperledger Fabric的test-network启动过程Bash源码详解

    前言 在基于Debian搭建Hyperledger Fabric 2.4开发环境及运行简单案例中,我们已经完成了Fabric 2.4的环境搭建及fabric-samples/test-network官 ...

  6. Netty服务端启动过程相关源码分析

    1.Netty 是怎么创建服务端Channel的呢? 我们在使用ServerBootstrap.bind(端口)方法时,最终调用其父类AbstractBootstrap中的doBind方法,相关源码如 ...

  7. 从SpringBoot启动,阅读源码设计

    目录 一.背景说明 二.SpringBoot工程 三.应用上下文 四.资源加载 五.应用环境 六.Bean对象 七.Tomcat服务 八.事件模型 九.配置加载 十.数据库集成 十一.参考源码 服务启 ...

  8. Asp.Net Core AuthorizeAttribute 和AuthorizeFilter 跟进及源码解读

    一.前言 IdentityServer4已经分享了一些应用实战的文章,从架构到授权中心的落地应用,也伴随着对IdentityServer4掌握了一些使用规则,但是很多原理性东西还是一知半解,故我这里持 ...

  9. Taurus.MVC 支持Asp.Net Core 的过程

    前言: 这些天,似乎.NET Core相关的新闻和文章经常在我眼前晃~~~ 昨天,微软又发布了.Core 2.1,又愰了一下,差点没亮瞎我的眼睛. 好吧,大概是上天给我的暗示,毕竟 CYQ.Data  ...

随机推荐

  1. K-th K

    题目描述 You are given an integer sequence x of length N. Determine if there exists an integer sequence ...

  2. hibernate的乐观锁和悲观锁+事务

    hibernate实现数据库操作的乐观锁和悲观锁参看:https://blog.csdn.net/chang_ge/article/details/79695813https://www.cnblog ...

  3. 3.redis kyes命令

    Keys命令 1.1设置key的生存时间 Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁. EXPIRE key seconds         ...

  4. K3CLOUD表关联

    销售订单关联发货通知单 销售订单表 T_SAL_ORDER A T_SAL_ORDERENTRY B T_SAL_ORDERENTRY_LK C 发货通知单表 T_SAL_DELIVERYNOTICE ...

  5. JAVA单例模式的几种写法

    /** * 单例模式懒汉式(双重检锁线程安全.JDK1.5之后) */ public class Singleton { private static volatile Singleton singl ...

  6. emacs 入门第一课:Emacs里的基本概念

    Table of Contents 无聊的开场白 buffer(缓冲区) window(窗口)与frame Emacs的mode Emacs Lisp 函数function.命令command.键绑定 ...

  7. MOOC(7)- case依赖、读取json配置文件进行多个接口请求-模拟接口响应数据(18)

    这里是把传入的请求数据作为响应值返回 # -*- coding: utf-8 -*- # @Time : 2020/2/15 9:47 # @File : do_mock_18.py # @Autho ...

  8. 分布式Web服务器架构(通俗易通)

    最开始,由于某些想法,于是在互联网上搭建了一个网站,这个时候甚至有可能主机都是租借的,但由于这篇文章我们只关注架构的演变历程,因此就假设这个时候已经是托管了一台主机,并且有一定的带宽了,这个时候由于网 ...

  9. DP × KMP

    几道用到KMP的DP题: hdu 5763    hdu 3689    hdu 3336    codeforces 494B    codevs 3945 关于KMP的nx数组: 如果在本文中看见 ...

  10. sitemesh 使用方法

    一.简介 SiteMesh是一个网页布局和修饰的框架,利用它可以将网页的内容和页面结构分离,以达到页面结构共享的目的. 它能通过创建一个包装对象,也就是装饰来包裹的对象.尽管它是由Java语言来实现的 ...