- ASP.NET Core 6 基础入门系列(14) 项目发布与IIS部署
- ASP.NET Core 6 基础入门系列(13) Web 服务器介绍
- ASP.NET Core 6 基础入门系列(12) 项目的多种启动方式及问题
- ASP.NET Core 6 基础入门系列(11) 项目结构详解之项目入口Program.cs
- ASP.NET Core 6 基础入门系列(10) 项目结构详解之appsettings.json
- ASP.NET Core 6 基础入门系列(9) 项目结构详解之launchSettings.json
- ASP.NET Core 6 基础入门系列(8) 项目结构详解之MVC
- ASP.NET Core 6 基础入门系列(7) 项目结构详解之wwwroot
- ASP.NET Core 6 基础入门系列(6) 项目结构详解之依赖项
- ASP.NET Core 6 基础入门系列(5) 项目结构详解之项目文件管理
- ASP.NET Core 6 基础入门系列(4) 项目结构简介
- ASP.NET Core 6 基础入门系列(3) 新建 ASP.NET Core MVC 6.0 项目
- ASP.NET Core 6 基础入门系列(2) 开发环境准备
- ASP.NET Core 6 基础入门系列(1) ASP.NET Core 6 简介
在前一篇博客《ASP.NET Core 6 基础入门系列(14) ASP.NET Core 项目发布与IIS部署》中简单的介绍了Web项目的发布与IIS中部署的流程。ASP.NET Core 在 IIS下部署有 In-Process 和 Out-of-Process 两种部署模式。
ASP.NET Core Module (ANCM) 是插入 IIS 管道的本机 IIS 模块,能让 ASP.NET Core 应用程序通过 IIS 运行。 使用以下任一方式通过 IIS 运行 ASP.NET Core 应用:
- 在 IIS 工作进程 (
w3wp.exe
) 内托管 ASP.NET Core 应用,称为进程内托管模型 - 将 Web 请求转发到运行 Kestrel 服务器(dotnet.exe 进程)的后端 ASP.NET Core 应用,称为进程外托管模型
开发者需要对这两类托管模型进行权衡。 默认情况下使用的是进程内托管模型,因为这样可以得到更好的性能和诊断。
使用进程内托管,ASP.NET Core 应用运行在与 IIS 工作进程相同的进程(w3wp.exe)中(如果采用 IIS Express,则工作进程为 iisexpress.exe)。 进程内承载相较进程外承载提供更优的性能,因为请求并不通过环回适配器进行代理,环回适配器是一个网络接口,用于将传出的网络流量返回给同一计算机。 IIS 使用 Windows 进程激活服务 (WAS)处理进程管理。
下图说明了 IIS、ASP.NET Core 模块和进程内托管的应用之间的关系:
该模式下使用的服务器类型是 IISHttpServer。图中的 ASP.NET Core Module 会将原始的请求转发给这个服务器(IISHttpServer),并将 IISHttpServer 生成的响应转交给 IIS 服务器进行回复。
In-Process 是默认采用的部署模式,所以不需要为此进行任何设置。关于IIS下部署ASP.NET Core 项目,请参考我的博客《ASP.NET Core 6 基础入门系列(14) 项目发布与IIS部署》。
修改 Program.cs 文件,使用如下代码获取当前处理程序的名称
重新发布,再次访问,从下图的输出结果中可以看出 ASP.NET Core 应用实际上就是运行在IIS的工作进程(w3wp.exe)中。
查看发布目录,则会发现生成的程序集和配置文件如下图所示
应用既然部署在IIS中,那么具体的配置自然就定义在web.config中,查看该文件的内容如下图所示
(1)第6行中的 path="*" verb="*" 表明所有的请求都被映射到 AspNetCoreModuleV2 这个 module 上,这就是上面介绍的 ASP.NET Core Module。
(2)如果 Module 启动 ASP.NET Core 管道并与之交互,则由第8行的 <aspNetCore> 配置节中的 processPath 属性指定的应用程序来控制。
(3)第8行的 hostingModel="inprocess" 表示部署模式为进程内容托管。
IISHttpServer 的注册实现在 IWebHostBuilder 接口的 UseIIS() 扩展方法中。由于这个扩展方法并没有提供一个 Action<IISServerOptions> 委托参数对 IISServerOptions 配置选项进行设置,所以不得不采用原始的方式对它进行设置。由于 IHostBuilder 接口的 ConfigureWebHostDefaults 扩展方法内部会调用这个方法,所以我们并不需要为此做额外的工作。
(一)启用进程内托管
自 ASP.NET Core 3.0 起,默认情况下已为部署到 IIS 的所有应用启用进程内托管。若要显式配置进程内托管的应用,请在项目文件 (.csproj
) 中将 <AspNetCoreHostingModel>
属性的值设置为 InProcess
:
未使用 IIS 托管时,ASP.NET Core 项目模板默认使用 Kestrel 服务器。
(二)一般体系结构
请求的常规流程如下:
- 请求从 Web 到达内核模式 HTTP.sys 驱动程序。
- 驱动程序将本机请求路由到网站的配置端口上的 IIS,通常为 80 (HTTP) 或 443 (HTTPS)。
- ASP.NET Core Module 接收本机请求,并将其传递给 IIS HTTP 服务器 (
IISHttpServer
)。 (IIS HTTP 服务器是将请求从本机转换为托管的 IIS 进程内服务器实现)
在 IIS HTTP 服务器处理请求后:
- 请求被发送到 ASP.NET Core 中间件管道。
- 中间件管道处理该请求并将其作为
HttpContext
实例传递给应用的逻辑(如配置文件中配置的 processPath=".\DotNet6_Web_Study.exe" 对应的 DotNet6_Web_Study.dll)。 - 应用的响应通过 IIS HTTP 服务器传递回 IIS。
- IIS 将响应发送到发起请求的客户端。
CreateDefaultBuilder 添加 IServer 实例的方式是:调用 UseIIS() 方法来启动 CoreCLR 和将应用托管在 IIS 工作进程(w3wp.exe 或 iisexpress.exe)内。 性能测试表明,与在进程外托管应用并将请求代理传入 Kestrel 相比,在进程中托管 .NET Core 应用可提供明显更高的请求吞吐量。 作为单个文件可执行文件发布的应用无法由进程内托管模型加载。
(三)应用程序配置
要配置 IIS 选项,请在 Program.cs
中包括 IISServerOptions 的服务配置。
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);builder.Services.Configure<IISServerOptions>(options =>{options.AllowSynchronousIO = true;});
IISServerOptions中的所有属性及含义如下
ASP.NET Core 应用在 IIS 中还可以采用 Out-of-Process 模式进行部署。如下图所示
在这种部署模式下,采用 KestrelServer 的 ASP.NET Core 应用运行在独立的 dotnet.exe 进程中,ASP.NET Core Module 会负责进程管理。当IIS接收到目标应用的请求时,如果目标应用所在的进程并未启动,则 ASP.NET Core Module 还负责执行“dotnet”命令激活此进程,相当于充当了 WAS(Windows ActivationService)的作用。使用单独的进程还可以托管同一个应用池中的多个应用。
- 请求从 Web 到达内核模式 HTTP.sys 驱动程序。
- 驱动程序将请求路由到网站的配置端口上的 IIS。 配置的端口通常是 80 (HTTP) 或 443 (HTTPS)。
- 此模块将该请求转发到应用的随机端口上的 Kestrel。 随机端口不是 80 或 443。
在激活 ASP.NET Core 承载进程之前,ASP.NET Core Module 会选择一个可用的端口,该端口和当前应用的路径(该路径将作用 ASPNET Core 应用的 PathBase)被写入环境变量,对应环境变量名称分别为“ASPNETCORE_PORT”和“ASPNETCORE_ APPL_PATH”。以 Out-Of-Process 模式部署 ASP.NET Core 应会接收 IIS 转发的请求,为了能够过滤其他来源的请求,ASP.NET Core Module 会生成一个 Token 并写入环境变量“ASPNETCORE_TOKEN”。后续转发的请求会利用一个报头“MS-ASPNETCORE-TOKEN”传递此Token, ASP.NET Core 应用校验是否与之前生成的Token匹配。
ASPNET Core Module 还会利用环境变量传递其他设置,如认证方案被写入环境变量“ASPNETCORE _IIS_HTTPAUTH",另一个 “ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED环境变量用来设置 Web Socket的支持状态。由于这些环境变量名称的前缀都是“ASPNETCORE_”,所以它们都会作为默认配置源。KestrelServer 最终会绑定到基于该端口的本地址终节点(localhost)进行监听。由于监听地址是由 ASP.NET Core Module 控制的,所以它只需将请求转发到该地址,最终将接收到的响应交给IIS返回。由于这里涉及本地回环网(Loopback)的访问,其性能自然不如 In-Process 部署模式。
(一)启用进程外托管模型
编辑项目文件,修改配置如下
OutOfProcess 表示进程外托管。
(1)<AspNetCoreHostingModel> 的值不区分大小写,因此 inprocess 和 outofprocess 均为有效值。
(2)使用进程外托管应用时,使用 Kestrel 服务器,而不是 IIS HTTP 服务器 (
IISHttpServer
)。对于进程外托管,CreateDefaultBuilder
会调用 UseIISIntegration 来进行以下操作:
- 在 ASP.NET Core Module 后运行时,配置服务器应侦听的端口和基本路径。
- 配置主机以捕获启动错误。
要配置 IIS 选项,请在 ConfigureServices 中包括 IISOptions 的服务配置。
(二)进程名称
将项目重新发布,查看发布后的web.config文件
第8行的 hostingModel="outofprocess" 表示为进程外托管。
通过下面的代码获取进程名称
浏览器中访问站点,从下图的输出结果中可以看出 ASP.NET Core 应用实际上就是运行在应用程序本身的进程中
修改web.config内容如下
浏览器中访问站点,从下图的输出结果中可以看出 ASP.NET Core 应用实际上就是运行在 dotnet.exe 进程中
(三)验证环境变量是否存在
通过以下代码逻辑来验证:
重新发布程序,在浏览器中访问
在进程内托管时,将应用以下特征:
-
使用 IIS HTTP 服务器 (
IISHttpServer
),而不是 Kestrel 服务器。 对于进程内托管,CreateDefaultBuilder
会调用 UseIIS 来进行以下操作:- 注册
IISHttpServer
。 - 在 ASP.NET Core Module 后运行时,配置服务器应侦听的端口和基本路径。
- 配置主机以捕获启动错误。
- 注册
-
requestTimeout
属性不适用于进程内托管。 -
不支持在应用之间共享应用池。 每个应用使用一个应用池。
-
应用和已安装的运行时(x64 或 x86)的体系结构(位数)必须与应用池的体系结构匹配。 例如,为 32 位 (x86) 发布的应用必须已为其 IIS 应用程序池启用 32 位。 有关详细信息,请参阅创建 IIS 站点部分。
-
检测到客户端连接断开。 客户端断开连接时,将取消
HttpContext.RequestAborted
取消标记。 -
在进程内托管时,不会在内部调用 AuthenticateAsync 来初始化用户。 因此,默认情况下不激活每次身份验证后用于转换声明的 IClaimsTransformation 实现。 使用 IClaimsTransformation 实现转换声明时,请调用 AddAuthentication 以添加身份验证服务:
var builder = WebApplication.CreateBuilder(args);builder.Services.AddTransient<IClaimsTransformation, MyClaimsTransformation>(); builder.Services.AddAuthentication(IISServerDefaults.AuthenticationScheme);
- 不同的启动方式对应的服务器
参考文献:
- 《使用 IIS 在 Windows 上托管 ASP.NET Core》https://learn.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/?view=aspnetcore-6.0
- 《用于 IIS 的 ASP.NET Core 模块 (ANCM)》https://learn.microsoft.com/zh-cn/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-6.0