• NetCore 启动地址配置详解


    背景

    程序在发布部署时候,设置环境ASPNETCORE_URLS不生效,也没在代码里使用UseUrls("xxxx"),启动一直是http://localhost:5000.最后测试发现只有在appsettings.json中配置urls才生效,网上找了半天资料也没看到有什么问题。

    最终翻看源代码,发现是在StartUp中的Configure替换了全局IConfiguration导致。

    平时开发大体知道程序启动时候端口启用顺序是
    UseUrls("xxx")> 环境变量 > 默认,具体是怎么确定使用哪个配置的,没找到资料,所有才有了本文。

    启动地址配置的几种方式介绍
    1. 环境变量ASPNETCORE_URLS
    #windows 
    set ASPNETCORE_URLS=http://localhost:6000
    #linux 
    export ASPNETCORE_URLS=http://localhost:6000
    
    1. UseUrls("http://localhost:6000")
    2. appsettings.json新增urls或者server.urls配置
    {
        "urls":"http://localhost:6000;http://localhost:6001",
        "server.urls":"http://localhost:6000;http://localhost:6001"
    }
    
    1. 使用系统默认
    说明

    程序启动过程中,一个配置key会重复使用,先放这里

    //WebHostDefaults.ServerUrlsKey如下
    public static readonly string ServerUrlsKey = "urls";
    
    Web项目启动地址配置说明

    今天是介绍启动方式,所以web启动流程不是重点。直接进入正题。

    Web启动最终是调用WebHost.StartAsync,源代码在这WebHost。其中有个方法EnsureServer来获取启动地址

    private static readonly string DeprecatedServerUrlsKey = "server.urls";
    
    //省略
    var urls = _config[WebHostDefaults.ServerUrlsKey] ?? _config[DeprecatedServerUrlsKey];
    

    是从全局IConfigration实例中获取启动地址。所以我的遇到问题这里就解决了。但环境变量UseUrls是如何解析并记载进来的呢?下面就开今天讲解。

    环境变量配置详解

    一般Web程序启动代码如下:

    Host.CreateDefaultBuilder(args)
       .ConfigureWebHostDefaults(webBuilder =>
       {
           webBuilder.UseStartup<Startup>();
       }).Build().Run();
    

    其中ConfigureWebHostDefaults的会用调用扩展方法ConfigureWebHost

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

    以上代码都是定义在Microsoft.Extensions.Hosting中。

    继续看ConfigureWebHost代码,这个方法就定义在Microsoft.AspNetCore.Hosting程序集中了。

    public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
    {
        //这里会加载环境变量
        var webhostBuilder = new GenericWebHostBuilder(builder);
        //这里会调用UseUrls等扩展方法
        configure(webhostBuilder);
        builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>());
        return builder;
    }
    

    GenericWebHostBuilder 构造函数里有如下代码,用来初始化配置,最终添加到全局
    IConfiguration实例中,也就是HostIConfiguration实例。

    builder.ConfigureServices((context, services) => services.AddHostedService());这个是web启动重点,有兴趣的可以看下

    //加入环境变量配置
    _config = new ConfigurationBuilder()
               .AddEnvironmentVariables(prefix: "ASPNETCORE_")
               .Build();
    //把配置加载到Host
    _builder.ConfigureHostConfiguration(config =>
    {
        config.AddConfiguration(_config);
    
        // We do this super early but still late enough that we can process the configuration
        // wired up by calls to UseSetting
        ExecuteHostingStartups();
    })
    

    AddEnvironmentVariables环境变量解析最终会使用EnvironmentVariablesConfigurationProvider,有兴趣的可以看下AddEnvironmentVariables源代码EnvironmentVariablesConfigurationProvider解析环境的方法如下。

    public override void Load()
    {
        Load(Environment.GetEnvironmentVariables());
    }
    
    internal void Load(IDictionary envVariables)
    {
        var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        //这里是筛选ASPNETCORE_开头的环境变量
        var filteredEnvVariables = envVariables
            .Cast<DictionaryEntry>()
            .SelectMany(AzureEnvToAppEnv)
            .Where(entry => ((string)entry.Key).StartsWith(_prefix, StringComparison.OrdinalIgnoreCase));
    
        foreach (var envVariable in filteredEnvVariables)
        {
            //这里会把前缀去掉加到配置里
            var key = ((string)envVariable.Key).Substring(_prefix.Length);
            data[key] = (string)envVariable.Value;
        }
    
        Data = data;
    }
    

    IConfiguration中的key是不区分大小写的,所有最终的效是在全局IConfiguration中新增一条key为urls的记录。
    而如果使用默认Host.CreateDefaultBuilder()appsettings.json中的配置会先加载。
    如果在appsettings.json中配置urls的话,环境变量也定义了,就会被环境变量的覆盖掉。

    UseUrls解析

    UseUrls解析最终会调用GenericWebHostBuilder中的UseSetting

    //UseUrls代码如下
    public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls)
    {
        if (urls == null)
        {
            throw new ArgumentNullException(nameof(urls));
        }
    
        return hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls));
    }
    
    //GenericWebHostBuilder中的UseSetting
    public IWebHostBuilder UseSetting(string key, string value)
    {
        _config[key] = value;
        return this;
    }
    

    由于这个方法是在 new GenericWebHostBuilder(builder);
    之后调用,就是 configure(webhostBuilder);,上面代码也有说明。所以IConfigurationurls如果有值,又会被覆盖掉。所以优先级最高的是UseUrls()

    默认地址

    假如以上3种配置都没有,就是地址为空,会使用默认策略。这里是源代码,下面是默认策略使用的地址

     /// <summary>
     /// The endpoint Kestrel will bind to if nothing else is specified.
     /// </summary>
     public static readonly string DefaultServerAddress = "http://localhost:5000";
    
     /// <summary>
     /// The endpoint Kestrel will bind to if nothing else is specified and a default certificate is available.
     /// </summary>
     public static readonly string DefaultServerHttpsAddress = "https://localhost:5001";
    
    结论
    1. 启动端口设置优先级如下:
      UseUrls("xxxx") > 环境变量 > appsetting.json配置urls>默认地址
    2. 不要随意替换全局的IConfiguration,如果不手动加入环境变量解析的话,会丢失一部分配置数据。
    3. 将自己的配置注入的全局,可以使用以下方式,这样就会把配置追加到全局的IConfiguration
     Host.CreateDefaultBuilder(args)
         .ConfigureWebHostDefaults(builder =>
         {
             builder.UseStartup<Startup>();
         }).ConfigureAppConfiguration(config =>
         {
             config.AddJsonFile("config.json", true, true);
         }).Build().Run();
    

    作者:cgyqu
    出处:https://www.cnblogs.com/cgyqu/p/12169014.html
    本站使用「署名 4.0 国际」创作共享协议,转载请在文章明显位置注明作者及出处。

  • 相关阅读:
    C++11:22委托构造函数和继承构造函数
    C++11:21通过智能指针管理第三方库分配的内存
    python 常识
    计算机基础
    XML
    flask请求上下文 及相关源码
    Flask框架
    Django orm 常用字段和参数
    docker 使用
    视图家族
  • 原文地址:https://www.cnblogs.com/cgyqu/p/12169014.html
Copyright © 2020-2023  润新知