[2core]web服务器配置

迁移问题

相对来说本文的内容有点多,主要包含基于IISExpress调试配置、Kestrel配置、IIS配置、IISIntegation配置、HTTPS配置、基于控制器API。ASP.NET 4.x及以前版本的框架是基于IIS部署的,而ASP.NET Core则是独立部署运行,并不依赖IIS,所以需要把依赖IIS的功能剥离出来独自管理,因此出现了不少知识点需要记录。

IISExpress调试配置

当你使用Visual Studio2019+版本创建一个ASP.NET Core项目后,可能要做的第一件事儿就是运行起来看看效果,此时VS编译好项目后,创建一个承载项目运行的进程,然后打开浏览器,访问默认的服务地址得到输出结果。作为一名程序员,你可能首先关注的是浏览器地址栏里的地址构成,然后再去程序代码中查找配置。

使用VS调试ASP.NET Core项目,默认使用的是IISExpress服务器,项目的启动设置位于“启动项目 》Properties 》 launchSettings.json”文件里,我们可以通过更改launchSettings.json文中里的选项控制VS+IISExpress+ASP.NET Core项目的调试运行过程,比如更换默认分配的端口号,添加https链接方式等。

{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:12488",
      "sslPort": 0
    }
  },
  "profiles": {
    "jks.core.test.webserver": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "index",
      "applicationUrl": "http://localhost:9000;https://localhost:9001",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "index",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Kestrel配置

Kestrel服务器是ASP.NET Core实现跨平台的关键因素,没有它的出现无从谈起跨平台(Windows/Linux/Unix/MacOS)。 对于Kestrel的配置可以通过编码方式写在程序中,但是作为一名优秀的程序员一定会将其配置写入配置文件,然后通过控制配置文件实现对Kestrel服务器的动态设置(配置信息位于appsettings.json文件里)。由于ASP.NET Core毕竟是巨硬家的东西,IIS又那么强大,不可能弃IIS不用,所以ASP.NET Core项目的部署方式分为以下多种:
1)Kestrel部署:独立部署运行,可运行任何.NET支持的操作系统上,即实现跨平台。
2)HttpSys部署:由于支持使用操作系统内核功能,效率最高,依赖Windows平台。
3)IIS部署(InProcess):与ASP.NET 4.x时期的项目相同,效率比较高,依赖Windows和IIS。
4)IISIntegation部署(OutOfProcess):IIS更像代理服务器,将接收的请求转发给Kestrel处理。
5)其他服务器:Nginx、Caddy、BFE和Apache等,实现过程也类似于IISIntegation模式的代理方式。

1.为了方便使用和管理配置被划分为两部分:Kestrel配置与KestrelSettings配置

{
  //Web服务器配置
  "WebServer": {
    //服务器类型(IISExpress,Kestrel,IIS,IISIntegation),IIS注意与web.config配合
    "WSType": "IISExpress",
    "Kestrel": {
      "Endpoints": {
        "HttpEndPoint": {
          "Url": "http://*:9000",
          "Protocols": "Http1AndHttp2AndHttp3"
        },
        "HttpsEndPoint": {
          "Url": "https://*:9001",
          "ClientCertificateMode": "AllowCertificate",
          "Certificate": {
            "AllowInvalid": false,
            "Path": "Keys\\test.p12",
            "Password": "1234567890"
          }
        }
      }
    },
    "KestrelSettings": {
      "AddServerHeader": true,
      "AllowResponseHeaderCompression": true,
      "AllowSynchronousIO": false,
      "AllowAlternateSchemes": false,
      "DisableStringReuse": false,
      "KestrelLimits": {
        "KeepAliveTimeout": 130,
        "RequestHeadersTimeout": 30,
        "MaxConcurrentConnections": null,
        "MaxConcurrentUpgradedConnections": null,
        "MaxRequestBufferSize": 1048576,
        "MaxRequestHeaderCount": 100,
        "MaxRequestHeadersTotalSize": 32768,
        "MaxRequestLineSize": 8192,
        "MaxRequestBodySize": 30000000,
        "MaxResponseBufferSize": 65536
      },
      "KestrelLimits2": {
        "MaxStreamsPerConnection": 100,
        "HeaderTableSize": 4096,
        "MaxFrameSize": 16384,
        "MaxRequestHeaderFieldSize": 16384,
        "InitialConnectionWindowSize": 131072,
        "InitialStreamWindowSize": 98304
      },
      "KestrelLimits3": {
        "MaxRequestHeaderFieldSize": 16384
      }
    }
  }
}

2.定义KestrelSettings配置对应的Options类

public class KestrelSettingOptions
    {
        public bool AddServerHeader { get; set; } = true;
        public bool AllowResponseHeaderCompression { get; set; } = true;
        public bool AllowAlternateSchemes { get; set; } = false;
        public bool DisableStringReuse { get; set; } = false;

        public KestrelLimitOptions KestrelLimits { get; set; } = new KestrelLimitOptions();
        public KestrelLimit2Options KestrelLimits2 { get; set; } = new KestrelLimit2Options();
        public KestrelLimit3Options KestrelLimits3 { get; set; } = new KestrelLimit3Options();
    }

    public class KestrelLimitOptions
    {
        public long? MaxConcurrentConnections { get; set; } = null;
        public long? MaxConcurrentUpgradedConnections { get; set; } = null;
        public int KeepAliveTimeout { get; set; } = 130;
        public long? MaxRequestBufferSize { get; set; } = 1048576;
        public int MaxRequestHeaderCount { get; set; } = 100;
        public int MaxRequestHeadersTotalSize { get; set; } = 32768;
        public int MaxRequestLineSize { get; set; } = 8192;
        public long? MaxRequestBodySize { get; set; } = 30000000;
        public int RequestHeadersTimeout { get; set; } = 30;
        public long? MaxResponseBufferSize { get; set; } = 65536;
    }
    public class KestrelLimit2Options
    {
        public int MaxStreamsPerConnection { get; set; } = 100;
        public int HeaderTableSize { get; set; } = 4096;
        public int MaxFrameSize { get; set; } = 16384;
        public int MaxRequestHeaderFieldSize { get; set; } = 16384;
        public int InitialConnectionWindowSize { get; set; } = 131072;
        public int InitialStreamWindowSize { get; set; } = 98304;
    }
    public class KestrelLimit3Options
    {
        public int MaxRequestHeaderFieldSize { get; set; } = 16384;
    }

3.实现Kestrel服务器的功能

public static void Config(ConfigureWebHostBuilder webhost)
        {
            switch (AppSetting.Instance.WebServerType)
            {
                case "iisexpress":
                    break;
                case "kestrel":
                    ConfigKestrel(webhost);
                    break;
                case "iisintegation":
                    ConfigIISIntegation(webhost);
                    break;
                default:
                    ConfigIIS(webhost);
                    break;
            }
        }

        private static void ConfigKestrel(ConfigureWebHostBuilder webhost)
        {
            webhost.UseKestrel((builderContext, options) =>
            {
                options.Configure(builderContext.Configuration.GetSection(ConstFiles.AppSettings_WebServerKestrel), reloadOnChange: true);
                var kso = builderContext.Configuration.GetSection(ConstFiles.AppSettings_WebServerKestrelSettings).Get<KestrelSettingOptions>();
                KestrelSettings(options, kso);
            });
        }
        private static void KestrelSettings(KestrelServerOptions opt, KestrelSettingOptions kso)
        {
            //settings
            opt.AddServerHeader = kso.AddServerHeader;
            opt.AllowAlternateSchemes = kso.AllowAlternateSchemes;
            opt.AllowResponseHeaderCompression = kso.AllowResponseHeaderCompression;
            opt.AllowSynchronousIO = kso.AllowSynchronousIO;
            opt.DisableStringReuse = kso.DisableStringReuse;

            //http1.x(Text)
            opt.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(kso.KestrelLimits.KeepAliveTimeout);
            opt.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(kso.KestrelLimits.RequestHeadersTimeout);
            opt.Limits.MaxConcurrentConnections = kso.KestrelLimits.MaxConcurrentConnections;
            opt.Limits.MaxConcurrentUpgradedConnections = kso.KestrelLimits.MaxConcurrentUpgradedConnections;
            opt.Limits.MaxRequestBodySize = kso.KestrelLimits.MaxRequestBodySize;
            opt.Limits.MaxRequestBufferSize = kso.KestrelLimits.MaxRequestBufferSize;
            opt.Limits.MaxRequestHeaderCount = kso.KestrelLimits.MaxRequestHeaderCount;
            opt.Limits.MaxRequestHeadersTotalSize = kso.KestrelLimits.MaxRequestHeadersTotalSize;
            opt.Limits.MaxRequestLineSize = kso.KestrelLimits.MaxRequestLineSize;
            opt.Limits.MaxResponseBufferSize = kso.KestrelLimits.MaxResponseBufferSize;
            //http2.x(Frame|Binary)
            opt.Limits.Http2.HeaderTableSize = kso.KestrelLimits2.HeaderTableSize;
            opt.Limits.Http2.InitialConnectionWindowSize = kso.KestrelLimits2.InitialConnectionWindowSize;
            opt.Limits.Http2.InitialStreamWindowSize = kso.KestrelLimits2.InitialStreamWindowSize;
            opt.Limits.Http2.MaxFrameSize = kso.KestrelLimits2.MaxFrameSize;
            opt.Limits.Http2.MaxRequestHeaderFieldSize = kso.KestrelLimits2.MaxRequestHeaderFieldSize;
            opt.Limits.Http2.MaxStreamsPerConnection = kso.KestrelLimits2.MaxStreamsPerConnection;
            //http3.x(QUIC|UDP)
            opt.Limits.Http3.MaxRequestHeaderFieldSize = kso.KestrelLimits3.MaxRequestHeaderFieldSize;
        }

        private static void ConfigIIS(ConfigureWebHostBuilder webhost)
        {
            webhost.UseIIS();
        }
        private static void ConfigIISIntegation(ConfigureWebHostBuilder webhost)
        {
            webhost.UseIISIntegration();
        }

IIS配置(InProcess)

从程序代码上说ASP.NET Core项目基于IIS部署运行的配置,就像上述代码中那么简单“webhost.UseIIS();”这么一行代码就可实现,然而实际上并非如此。通过对ASP.NET Core项目编译发布,你会看到生成的文件里有一个web.config文件,这个文件里<aspNetCore>节点是关键配置项,具体配置内容如下:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- 
        ASP.NET Core 应用不会使用 web.config 中的 ASP.NET 4.x 应用的配置部分进行配置:
        <system.web>
        <appSettings>
        <connectionStrings>
        <location>
    -->
    
    <!-- To customize the asp.net core module uncomment and edit the following section. 
         For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
    <system.webServer>
        <handlers>
            <remove name="aspNetCore"/>
            <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
        </handlers>
        <!--
            processPath:应用程序启动命令所在路径,必选 
            arguments:应用程序启动参数,可选
            stdoutLogEnabled:是否将stdout和stderr输出到stdoutLogFile属性指定的文件,默认值false
            stdoutLogFile:作为stdout和stderr输出的日志文件
            hostingModel:部署模式,默认inprocess。可选值“inprocess/InProcess”和“outofprocess/OutOfProcess”
            forwardWindowsAuthToken:是否转发Windows认证令牌,默认值true
            processesPerApplication:承载ASP.NET CORE应用的进程(processPath)数量,默认值1,不建议设置此参数。该配置对InProcess部署模式无效。
            rapidFailsPerMinute:ASP.NET CORE应用进程(processPath),每分钟允许崩溃的次数,默认值10,超过此值就不再尝试启动。该配置对InProcess部署模式无效。
            requestTimeout:请求处理超时时间,默认值00:02:00,最大值360:00:00。该配置对InProcess部署模式无效。
            shutdownTimeLimit:检测到 app_offline.htm 文件时,模块等待可执行文件正常关闭的持续时间(以秒为单位),默认值10秒。
            startupRetryCount:ASP.NET CORE应用进程启动重试次数,默认值2次。
            startupTimeLimit:ASP.NET CORE应用进程启动超时时间(单位为秒),默认120秒。
        -->
        <aspNetCore
            processPath="dotnet"
            arguments=".\jks.core.test.webserver.dll"
            stdoutLogEnabled="false"
            stdoutLogFile=".\logs\stdout"
            hostingModel="inprocess"
            forwardWindowsAuthToken="true"
            processesPerApplication="1"
            rapidFailsPerMinute="10"
            requestTimeout="00:02:00"
            shutdownTimeLimit="10"
            startupRetryCount="2"
            startupTimeLimit="120">
        </aspNetCore>
    </system.webServer>
</configuration>

特别提示:
1)由于ASP.NET Core已经从根本上独立于IIS,此时若想让IIS托管运行,IIS需要安装一个插件ASP.NET Core 模块 (ANCM)

IISIntegation配置(OutOfProcess)

InProcess部署模式属于进程内托管,OutOfProcess部署模式属于进程外托管,通过上述代码可以知晓,其实代码使用和配置文件基本相同,只是个别调用方法和参数配置需要修改,因此不再赘述,请参考上述内容。
 

Https配置

https配置相较于基于IIS的配置变化不小,在ASP.NET 4.x时期https的配置不会在程序代码中操作,现在ASP.NET Core需要在代码中实现。对于一切可以通过配置实现的功能一定要通过配置实现,因为修改程序代码总是一件麻烦事,尤其是在部署运行之后,所以对于Https的配置,也是通过配置的形式实现。
1.在appsettings.json文件中,定义Https的相关配置项

{
"WebServer": {
    //服务器类型(IISExpress,Kestrel,IIS,IISIntegation),IIS注意与web.config配合
    "WSType": "IISExpress",
    "Kestrel": {
      "Endpoints": {
        "HttpEndPoint": {
          "Url": "http://*:9000",
          "Protocols": "Http1AndHttp2AndHttp3"
        },
        "HttpsEndPoint": {
          "Url": "https://*:9001",
          "ClientCertificateMode": "AllowCertificate",
          "Certificate": {
            "AllowInvalid": false,
            "Path": "Keys\\test.p12",
            "Password": "1234567890"
          }
        }
      }
    }
  },
  "WebApplication": {
    "HttpsRedirection": true,
    "MapController": true
  }
}

2.依据配置信息决定是否将默认的http请求重定向到https请求

private static void ConfigHttps(WebApplication app)
        {
            if (AppSetting.Instance.WebApp.HttpsRedirection)
            {
                app.UseHttpsRedirection();
                app.UseHsts();
            }
        }

控制器API

控制器API就是传统的XXXController+Action方式定义的API服务,由于巨硬在ASP.NET Core中推出Minimal API,故而作此说明,但是鉴于Minimal API模式尚未成熟,在生产环境还是建议使用控制器API。在使用依赖注入(DI)时,都是先把服务对象添加到IServiceCollection集合中,然后再使用,所以控制器API也是如此,先通过IServiceCollection.AddControllers方法把你定义的控制器添加到服务集合,然后再调用WebApplication.MapControllers方法将控制器API类中的Action转化成一个个路由终结点,此时外部程序就可以发起请求了。

1.依据配置文件中的定义,判断是否采用了控制器API

  "WebApplication": {
    "HttpsRedirection": true,
    "MapController": true
  }

2.将控制器类添加到服务集合

private static void ConfigControllers(IServiceCollection services)
        {
            if (AppSetting.Instance.WebApp.MapController)
            {
                services.AddControllers();
            }
        }

3.将控制器类中的Action转化成路由终结点服务

private static void ConfigControllers(WebApplication app)
        {
            if (AppSetting.Instance.WebApp.MapController)
            {
                app.MapControllers();
            }
        }

总结

经过上述内容可知,web服务器配置变化是比较大的,而且未来还会继续变化,因此,需要重点关注这部分技术的发展和使用。

测试源码:https://gitee.com/kinbor/jks.core.test.webserver

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值