zoukankan      html  css  js  c++  java
  • 【.Net Core】.Net Core 源码分析与深入理解

    研究原因:学习 .Net Core 两年有余,实际项目也使用了一年半,自己的技术已经到了瓶颈,需要有一个突破,我觉得首先研究架构师的设计思想,其次分析一下.Net Core的源码,这将会是一个很好的学习方式。

    源码版本: .Net Core 3.1.14   ,有aspnetcoreextensions 两部分

    下载地址:https://github.com/dotnet/aspnetcore  https://github.com/dotnet/extensions

    参考资料:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/startup?view=aspnetcore-3.1            微软官方文档

                      https://www.cnblogs.com/edison0621/p/11025310.html   .NET Core 3.0之深入源码理解Host(一)  艾心

    首先从 Program.cs 代码讲起,可以看到,以前使用 .Net Core 2.0 的时候 创建的是WebHostBuilder对象,而现在变为了HostBuilder

    public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    });

    Main 方法是项目的入口,但是实际上执行了三个函数,调用下面的CreateHostBuilder方法创建IHostBuilder对象,build一个实例,再run,运行。

    上半部分Main()

    主要就是 build() 和 run() 方法,这个很简单,我们先把后面解释一下。

    下半部分CreateHostBuilder()

    本篇介绍Program最复杂的就是这个创建IHostBuilder对象的过程,现在来逐一解析。

    其实这个CreateHostBuilder 就三步骤

    1.初始化一个IHostBuilder

    2.把IHostBuidler转化为IWebHostBuilder ( 2.0 是直接创建,这里绕了一个弯 )

    3.给IWebHostBuilder 配置一些信息 ( 例如后面可以配置 端口号,日志等)

    下半第一步,初始化一个 IHostBuilder 对象

    Host.CreateDefaultBuilder(args)  约等于 IHostBuilder builder = Host.CreateDefaultBuilder(args);

    点进去这个方法查看源头,指向CreateDefaultBuilder(string[] args)方法。

    在 ..extensions-3.1.14srcHostingHostingsrcHost.cs 中找到这个方法的源码如下

            public static IHostBuilder CreateDefaultBuilder(string[] args)
            {
                var builder = new HostBuilder();
    
                builder.UseContentRoot(Directory.GetCurrentDirectory());
                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;
                });
    
                return builder;
            }

    好,现在从此方法第一句 var builder = new HostBuilder(); 开始逐一分解

    指定Host要使用的根目录

     builder.UseContentRoot(Directory.GetCurrentDirectory());

    配置初始化环境变量,如appsettings.json等。

    builder.ConfigureHostConfiguration(config => ......
    
    builder.ConfigureAppConfiguration((hostingContext, config) => ......

    配置.Net Core 默认的Log

    .ConfigureLogging((hostingContext, logging) => ......

    配置开发环境模式下启用作用域验证

    .UseDefaultServiceProvider((context, options) =>
    {
        var isDevelopment = context.HostingEnvironment.IsDevelopment();
        options.ValidateScopes = isDevelopment;
        options.ValidateOnBuild = isDevelopment;
    });

    下半第二步:把IHostBuidler对象转化为IWebHostBuilder 

    这一步骤是通过Program.cs/CreateHostBuilder中的 .ConfigureWebHostDefaults()方法,点进去看看

     这个方法如果你按照上面写的目录去找,是找不到的。查找了很久,才发现这个方法其实是在

    .....aspnetcore-3.1.14srcDefaultBuildersrc 目录下,其实是调用在这里,很有欺骗性T.T,这里推荐一个搜文件的工具:searcheverything。

     打开这个方法,源码如下,非常简单。

        public static class GenericHostBuilderExtensions
        {
            public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
            {
                return builder.ConfigureWebHost(webHostBuilder =>
                {
                    WebHost.ConfigureWebDefaults(webHostBuilder);
    
                    configure(webHostBuilder);
                });
            }
        }

    看到这里豁然开朗,知道了前面的意思了,看起来似乎很难理解,其实上面的方法可以理解为

    var webHostBuilder = new GenericWebHostBuilder(builder);
    configure(webHostBuilder);

    下半第三步:给IWebHostBuilder 配置一些信息

    把 hostBuilder 转化为 webHostBudiler对像,然后调用参数传入的委托 configure(webHostBuilder) , 这个configure实际就是Program.cs里面的

    webBuilder.UseStartup<Startup>();

    当然,你还可以使用委托配置log,配置端口,自定义各种配置,然后都到源码里这个方法装配

    上半部分Main()

    好,现在下半部分CreateHostBuilder()方法已经完全执行完了,回到上半部分的 Main()方法。

    build方法在 ......extensions-3.1.14srcHostingHostingsrcHostBuilder 中

            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>();
            }

    build() 方法只会执行一次,创建各种配置过的对象。

    run 方法在 ......extensions-3.1.14srcHostingAbstractionssrcHostingAbstractionsHostExtensions 中

            public static void Run(this IHost host)
            {
                host.RunAsync().GetAwaiter().GetResult();
            }

    Run方法运行应用程序,阻止调用线程,使方法一直运行,提供服务,直到主机关闭

    hist.RunAsync() 中的 RunAsync()方法如下,host 的创建和销毁,可以通过设置CancellationToken 的值,使程序自动关闭

            public static async Task RunAsync(this IHost host, CancellationToken token = default)
            {
                try
                {
                    await host.StartAsync(token);
    
                    await host.WaitForShutdownAsync(token);
                }
                finally
                {
                    if (host is IAsyncDisposable asyncDisposable)
                    {
                        await asyncDisposable.DisposeAsync();
                    }
                    else
                    {
                        host.Dispose();
                    }
    
                }
            }

    总结:

    看起来非常复杂,但是经过细细分析,发现其实很简单,就是创建builder ,通过builder生成host ,最后执行host。

    其他思考:

    为什么.Net Core 3.1 与 2.X 比 生成 host 的过程 改的更加复杂???

    结合巨硬后来的各种操作比如.Net5 .Net6,我明白了,虽然需要的是IWebHostBuilder,但我还是要抽象一个IHostBuilder

    这样可以更灵活的配置,使.Net Core 不再只是为了 web服务,未来应该巨硬是想统一化生态园,WPF、Xamarin、Unity 等技术会渐渐整合起来.

    期待今年11月份的 .Net 6 LTS版本 ,C#设计的这么牛皮的语言不能让他没落,多输出内容,让.Net未来的生态变的更好吧。

  • 相关阅读:
    hdu1542线段树+离散化+扫描线
    Codeforces Round #373 (Div. 2)
    Codeforces Round #381 (Div. 2)
    Codeforces Round #352 (Div. 2)
    CodeForces
    poj3311 状压dp+floyd
    CodeForces 385 D.Bear and Floodlight 状压DP
    Codeforces Round #299 (Div. 2)D. Tavas and Malekas
    Tavas and Karafs 二分+结论
    ThikPHP3.1 常用方法(one)
  • 原文地址:https://www.cnblogs.com/simawenbo/p/14663622.html
Copyright © 2011-2022 走看看