详解IIS在ASP.NET Core下的两种部署模式

目录
  • 一、ASP.NET CORE Core Module
  • 二、 In-Process部署模式
  • 三、Out-of-Process部署模式
  • 四、<aspnetcore>配置

KestrelServer最大的优势体现在它的跨平台的能力,如果ASP.NET CORE应用只需要部署在Windows环境下,IIS也是不错的选择。ASP.NET CORE应用针对IIS具有两种部署模式,它们都依赖于一个IIS针对ASP.NET CORE Core的扩展模块。本文提供的示例演示已经同步到《ASP.NET Core 6框架揭秘-实例演示版》)

一、ASP.NET CORE Core Module

IIS其实也是按照管道的方式来处理请求的,但是IIS管道和ASP.NET CORE中间件管道有本质的不同。对于部署在IIS中的Web应用来说,从最初接收到请求到最终将响应发出去,这段处理流程被细分为一系列固定的步骤,每个都具有一个或者两个(前置+后置)对应的事件或者回调。我们可以利用自定义的Module注册相应的事件或回调在适当的时机接管请求,并按照自己希望的方式对它进行处理。

IIS提供了一系列原生(Native)的Module,我们也可以使用任意.NET语言编写托管的Module,整合IIS和ASP.NET CORE 的这个ASP.NET CORE Core Module就是一个原生的Module。它利用注册的事件将请求从IIS管道中拦截下来,并转发给ASP.NET CORE管道进行处理。相应的安装包可以从https://dotnet.microsoft.com/permalink/dotnetcore-current-windows-runtime-bundle-installer下载。

二、 In-Process部署模式

ASP.NET CORE在IIS下有In-Process和Out-of-Process两种部署模式。In-Process模式下的ASP.NET CORE应用运行在IIS的工作进程w3wp.exe中(如果采用IIS Express,工作进程为iisexpress.exe)。如图18-7所示,ASP.NET CORE应用在这种模式下使用的服务器类型是IISHttpServer,上述的ASP.NET CORE Core Module会将原始的请求转发给这个服务器,并将后者生成响应转交给IIS服务器进行回复。

图1 In-Process部署模式

In-Process是默认采用的部署模式,所以我们不需要为此做任何设置,接下来我们就来演示一下具体的部署方式。我们在IIS的默认站点(Defaut Web Site)创建一个名为WebApp的应用,并将映射的物理路径设置为“C:\App”。然后我们创建一个空的ASP.NET CORE程序,并编写了如下这个将当前进程名称作为响应内容的演示程序。

using System.Diagnostics;
var app = WebApplication.Create(args);
app.Run(context => context.Response.WriteAsync(Process.GetCurrentProcess().ProcessName));
app.Run();

然后我们在Visual Studio的解决方案视图右键选择该项目,在弹出的菜单中选择“发布(Publish)”选项,创建一个指向“C:\App”的Publish Profile,然后执行这个Profile完成发布工作。应用发布也可以执行命令行“dotnet public”来完成。应用部署好之后,我们利用浏览器采用地址“http://localhost/webapp”访问部署好的应用,从图2所示的输出结果可以看出ASP.NET CORE应用实际上就运行在IIS的工作进程中。

图2 In-Process模式下的进程名称

如果我查看此时的部署目录(“C:\App”),会发现生成的程序集和配置文件。应用既然部署在IIS中,那么具体的配置自然定义在web.config中,如下所示的就是这个文件的内容。我们会发现所有的请求(path="*" verb="*")都被映射到“AspNetCoreModuleV2”这个Module上,这就是上面介绍的ASP.NET CORE Core Module。至于这个Module如果启动ASP.NET CORE管道并与之交互,则由后面的<aspNetCore>配置节来控制,可以看到它将表示部署模式的hostingModel属性设置为“inprocess”。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\App.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
    </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: 243DF55D-2E11-481F-AA7A-141C2A75792D-->

In-Process模式会注册如下这个IISHttpServer,对应的配置选项定义在IISServerOptions中。如果具有同步读写请求和响应主体内容的需要,我们需要将AllowSynchronousIO属性(默认为False)设置为True。如果将AutomaticAuthentication属性返回True(默认值),认证用户将自动赋值给HttpContext上下文的User属性。我们可以利用MaxRequestBodyBufferSize(默认为1,048,576)和MaxRequestBodySize属性(默认为30,000,000)设置接收请求主体的缓冲区的容量,和最大请求主体的字节数。

internal class IISHttpServer : IServer, IDisposable
{
    public IFeatureCollection Features { get; }
    public IISHttpServer(
       IISNativeApplication nativeApplication,
       IHostApplicationLifetime applicationLifetime,
       IAuthenticationSchemeProvider authentication, IOptions<IISServerOptions> options,
       ILogger<IISHttpServer> logger);
    public unsafe Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken);
    public Task StopAsync(CancellationToken cancellationToken);
}
public class IISServerOptions
{
    public bool 	AllowSynchronousIO { get; set; }
    public bool 	AutomaticAuthentication { get; set; }
    public string? 	AuthenticationDisplayName { get; set; }
    public int 	        MaxRequestBodyBufferSize { get; set; }
    public long? 	MaxRequestBodySize { get; set; }
}

针对IISHttpServer的注册实现在IWebHostBuilder接口如下这个UseIIS扩展方法中。由于这个方法并没有提供一个Action<IISServerOptions>委托参数对IISServerOptions配置选项进行设置,所以我们不得不采用原始的对它进行设置。由于IHostBuider接口ConfigureWebHostDefaults扩展方法内部会调用这个方法, 我们并不需要为此做额外的工作。

public static class WebHostBuilderIISExtensions
{
    public static IWebHostBuilder UseIIS(this IWebHostBuilder hostBuilder);
}

三、Out-of-Process部署模式

ASP.NET CORE应用在IIS中还可以采用Out-of -Process模式进行部署。如图3所示,在这种部署下,采用KestrelServer的ASP.NET CORE应用运行在独立的dotnet.exe进程中。当IIS接受到针对目标应用的请求时,如果目标应用所在的进程并未启动,ASP.NET CORE Core Module还负责执行dotnet命令激活此进程,相当于充当了WAS(Windows Activation Service)的作用。

图3 Out-of-Process部署模式

在激活ASP.NET CORE承载进程之前,ASP.NET CORE Core Module会选择一个可用的端口号,该端口号和当前应用的路径(该路径将作用ASP.NET CORE应用的PathBase)被写入环境变量,对应的环境变量名称分别为“ASPNETCORE_PORT”和“ASPNETCORE_APPL_PATH”。以Out-of-Process模式部署的ASP.NET CORE应用只会接收IIS转发给它的请求,为了能够过滤其它来源的请求,ASP.NET CORE Core Module会生成一个Token并写入环境变量“ASPNETCORE_TOKEN”。后续转发的请求会利用一个报头“MS-ASPNETCORE-TOKEN”传递此Token,ASP.NET CORE应用会校验是否与之前生成的Token匹配。

ASP.NET CORE Core Module还会利用环境变量传递其他一些设置,认证方案会写入环境变量“ASPNETCORE_IIS_HTTPAUTH”,另一个“ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED”环境变量用来设置针对Web Socket的支持状态。由于这些环境变量名称的前缀都是“ASPNETCORE_”,所以它们会作为默认配置源。KestrelServer最终会绑定到基于该端口的本地终结点(“localhost”)进行监听。由于监听地址是由ASP.NET CORE Core Module控制的,所以它只需要将请求往该地址进行转发,最终将接收到响应交给IIS返回即可。由于这里涉及本地回环网络(Loopback)的访问,其性能自然不如In-Process部署模式。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2"resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\App.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="outofprocess" />
    </system.webServer>
  </location>
</configuration>

我们在上面演示了In-Process的部署方式,现在我们直接修改配置文件web.config,按照上面的方式将<aspNetCore>配置节的hostingModel属性设置为“outofprocess”,部署的应用就自动切换到Out-of-Process。此时再次以相同的方式访问部署的应用,我们会发现浏览器上显示的进程名称变成了“dotnet”。

图4 Out-of-Process模式下的进程名称

部署模式可以直接定义在项目文件中,如果按照如下的方式将AspNetCoreHostingModel属性设置为“OutOfProcess”,那么发布后生成的web.config中针对部署模式的设置将随之改变。该属性默认值为“InProcess”,我们也可以显式进行设置。

<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
        <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
   </PropertyGroup>
</Project>

为了进一步验证上述的这一系列环境变量是否存在,如下所示的演示程序会将以“ASPNETCORE_”为前缀的环境变量作为响应内容输出来。除此之外,作为响应输出的还有进程名称、请求的PathBase和“MS-ASPNETCORE-TOKEN”报头。

using System.Diagnostics;
using System.Text;
var app = WebApplication.Create(args);
app.Run(HandleAsync);
app.Run();
Task HandleAsync(HttpContext httpContext)
{
    var request = httpContext.Request;
    var configuration = httpContext.RequestServices.GetRequiredService<IConfiguration>();
    var builder = new StringBuilder();
    builder.AppendLine($"Process: {Process.GetCurrentProcess().ProcessName}");
    builder.AppendLine($"MS-ASPNETCORE-TOKEN: {request.Headers["MS-ASPNETCORE-TOKEN"]}");
    builder.AppendLine($"PathBase: {request.PathBase}");
    builder.AppendLine("Environment Variables");
    foreach (string key in Environment.GetEnvironmentVariables().Keys)
    {
        if (key.StartsWith("ASPNETCORE_"))
        {
            builder.AppendLine($"\t{key}={Environment.GetEnvironmentVariable(key)}");
        }
    }
    return httpContext.Response.WriteAsync(builder.ToString());
}

应用重新发布之后,再次利用浏览器访问后回得到如图5所示的结果。我们可以从这里找到上述的环境变量,请求携带的“MS-ASPNETCORE-TOKEN”报头正好与对应环境变量的值一致,应用在IIS中的虚拟目录作为了应用路径被写入环境变量并成为请求的PathBase。如果站点提供了HTTPS终结点,其端口还会写入“SPNETCORE_ANCM_HTTPS_PORT”这个环境变量,这是为了实现针对HTTPS终结点的重定向而设计的。

图5 Out-of-Process模式下环境变量

Out-of-Process部署的大部分实现都是由如下这个IISMiddleware中间件来完成的,IISOptions为对应的配置选项。IISMiddleware中间件完成了针对“配对Token”的验证过滤非IIS转发的请求。如果IISOptions配置选项的ForwardClientCertificate属性返回True(默认值),此中间件会从请求报头“MS-ASPNETCORE-CLIENTCERT”中提取客户端证书,并将它保存到ITlsConnectionFeature特性中。该中间件还会将当前Windows账号对应的WindowsPrincipal对象附加到HttpContext上下文的特性集合中,如果IISOptions配置选项的AutomaticAuthentication属性返回True(默认值),该对象会直接赋值给HttpContext上下文的User属性。

public class IISMiddleware
{
    public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory,
        IOptions<IISOptions> options, string pairingToken,
        IAuthenticationSchemeProvider authentication,
        IHostApplicationLifetime applicationLifetime);
    public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory,
        IOptions<IISOptions> options, string pairingToken, bool isWebsocketsSupported,
        IAuthenticationSchemeProvider authentication,
        IHostApplicationLifetime applicationLifetime);
    public Task Invoke(HttpContext httpContext);
    public Task Invoke(HttpContext httpContext)
}
public class IISOptions
{
    public bool AutomaticAuthentication { get; set; }
    public string? AuthenticationDisplayName { get; set; }
    public bool ForwardClientCertificate { get; set; }
}

IIS利用WAS根据请求激活工作进程w3wp.exe。如果站点长时间未曾访问,它还会自动关闭工作进程。如果工作进程都关闭了,承载ASP.NET CORE应用的dotnet.exe进程自然也应该关闭。为了关闭应用承载进程,ASP.NET CORE Core Module会发送一个特殊的请求,该请求携带一个值为“shutdown”的“MS-ASPNETCORE-EVENT”报头,IISMiddleware中间件在接收到该请求时会利用注入的IHostApplicationLifetime对象关闭当前应用。如果不支持WebSocket,该中间件还会将代表“可升级到双向通信”的IHttpUpgradeFeature特性删除。将应用路径设置为请求的PathBase也是由这个中间件完成的。由于IISMiddleware中间件所作的实际上是对HttpContext上下文进行初始化的工作,所以它必须优先执行才有意义,为了将此中间件置于管道的前端,如下这个IISSetupFilter被定义出来完成对该中间件的注册。

internal class IISSetupFilter : IStartupFilter
{
    internal IISSetupFilter(string pairingToken, PathString pathBase, bool isWebsocketsSupported);
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next);
}

IISSetupFilter最终是通过IWebHostBuilder接口如下这个UseIISIntegration扩展方法进行注册的。这个方法还负责从当前配置和环境变量提取端口号,并完成监听地址的注册。由于KestrelServer默认会选择注册到服务器上的终结点,所以该方法会利用配置将IServerAddressesFeature特性的PreferHostingUrls属性设置为True,这里设置的监听地址才会生效。这个方法还会根据当前IIS站点的设置对IISOptions作相应设置。由于IHostBuider接口ConfigureWebHostDefaults扩展方法内部也会调用这个方法,我们并不需要为此做额外的工作。

public static class WebHostBuilderIISExtensions
{
    public static IWebHostBuilder UseIISIntegration(this IWebHostBuilder hostBuilder);
}

四、<aspnetcore>配置

不论是采用何种部署模式,相关的配置都定义在部署目录下的web.config配置文件,它提供的针对ASP.NET CORE Core Module的映射使我们能够将ASP.NET CORE应用部署在IIS中。在web.config中,与ASP.NET CORE应用部署相关的配置定义在<aspNetCore>配置节中。

<aspNetCore
    processPath 		= "dotnet"
    arguments 			= ".\App.dll"
    stdoutLogEnabled 		= "false"
    stdoutLogFile 		= ".\logs\stdout"
    hostingModel		= "outofprocess"
    forwardWindowsAuthToken	= "true"
    processesPerApplication	= "10"
    rapidFailsPerMinute	= "5"
    requestTimeout		= "00:02:00"
    shutdownTimeLimit		= "60"
    startupRetryCount		= "3"
    startupTimeLimit		= "60">
    <environmentVariables>
        <environmentVariable name = "ASPNETCORE_ENVIRONMENT" value = "Development"/>
    </environmentVariables>
    <handlerSettings>
        <handlerSetting name = "stackSize" value = "2097152" />
        <handlerSetting name = "debugFile" value = ".\logs\aspnetcore-debug.log" />
        <handlerSetting name = "debugLevel" value = "FILE,TRACE" />
    </handlerSettings>
</aspNetCore>

上面这段XML片段包含了完整的<aspNetCore>配置属性,下表对这些配置进行了简单的说明。设置的文件可以采用绝对路径和相对于部署目录(通过 “.”表示)的相对路径。


属性


含  义


processPath


ASP.NET CORE应用启动命令所在路径,必需。


arguments


ASP.NET CORE应用启动传入的参数,可选。


stdoutLogEnabled


是否将stdout 和stderr输出到 stdoutLogFile属性指定的文件,默认为False。


stdoutLogFile


作为stdout 和stderr输出的日志文件,默认为“ aspnetcore-stdout”。


hostingModel


部署模式,“inprocess/InProcess”或者“outofprocess/OutOfProcess”(默认值)。


forwardWindowsAuthToken


是否转发Windows认证令牌,默认为True。


processesPerApplication


承载ASP.NET CORE应用的进程( processPath)数,默认为1。该配置对In-Process模式无效。


rapidFailsPerMinute


ASP.NET CORE应用承载进程( processPath)每分钟允许崩溃的次数,默认为10,超过此数量将不再试图重新启动它。


requestTimeout


请求处理超时时间,默认为2分钟。


startupRetryCount


ASP.NET CORE应用承载进程启动重试次数,默认为2次。


startupTimeLimit


ASP.NET CORE应用承载进程启动超时时间(单位为秒),默认为120秒。


environmentVariables


设置环境变量。


handlerSettings


为ASP.NET CORE Core Module提供额外的配置。

到此这篇关于IIS在ASP.NET Core下的两种部署模式的文章就介绍到这了,更多相关ASP.NET Core部署模式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 在IIS上部署ASP.NET Core Web API的方法步骤

    对于本文,我想与您分享有关如何在IIS上部署ASP.NET Core Web API的指南.我将指导您安装Visual Studio 2019,.NET Core Runtime 3.0.我还确保我逐步引导您在服务器(Web IIS)中启用它,使用no受管代码选项创建新的应用程序池,创建ASP.NET Core Web API项目以及发布ASP.NET Core Web API. 了解并遵循正确的步骤来准备开发和部署环境后,在IIS上部署ASP.NET Core Web API就是一件容易的事.

  • IIS部署ASP.NET Core项目及常见问题总结

    部署准备工作 1.服务器开启添加IIS相关功能 1.1. 点击windows搜索到 “启用或关闭windows功能” 1.2 选择添加IIS的部分功能, 如下图所示 2.进入IIS,添加已经发布的网站文件 3.设置应用程序池无托管代码 4.运行你的网站 4.1. 默认运行下, 如出现500.19问题, 则说明网站目录权限不足, 需要给网站添加用户, 设置所有权限. 4.2.打开网站的文件, 添加用户设置权限, 然后刷新网站 5.按照以上的操作, 一般即可正常运行, 如出现不同的异常, 以下总结归

  • 详解IIS在ASP.NET Core下的两种部署模式

    目录 一.ASP.NET CORE Core Module 二. In-Process部署模式 三.Out-of-Process部署模式 四.<aspnetcore>配置 KestrelServer最大的优势体现在它的跨平台的能力,如果ASP.NET CORE应用只需要部署在Windows环境下,IIS也是不错的选择.ASP.NET CORE应用针对IIS具有两种部署模式,它们都依赖于一个IIS针对ASP.NET CORE Core的扩展模块.本文提供的示例演示已经同步到<ASP.NET

  • 详解如何在ASP.NET Core Web API中以三种方式返回数据

    在 ASP.NET Core 中有三种返回 数据 和 HTTP状态码 的方式,最简单的就是直接返回指定的类型实例,如下代码所示: [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random()

  • 详解如何在ASP.NET Core中编写高效的控制器

    通过遵循最佳实践,可以编写更好的控制器.所谓的"瘦"控制器(指代码更少.职责更少的控制器)更容易阅读和维护.而且,一旦你的控制器很瘦,可能就不需要对它们进行太多测试了.相反,你可以专注于测试业务逻辑和数据访问代码.瘦控制器的另一个优点是,它更容易维护控制器的多个版本. 这篇文章讨论了使控制器变胖的坏习惯,然后探索了使控制器变瘦和易于管理的方法.我列出编写控制器的最佳实践可能并不全面,但我已经讨论了最重要的一些,并在适当的情况下提供了相关的源代码.在接下来的几节中,我们将研究什么是胖控制

  • 详解如何在ASP.Net Core中实现健康检查

    健康检查 常用于判断一个应用程序能否对 request 请求进行响应,ASP.Net Core 2.2 中引入了 健康检查 中间件用于报告应用程序的健康状态. ASP.Net Core 中的 健康检查 落地做法是暴露一个可配置的 Http 端口,你可以使用 健康检查 去做一个最简单的活性检测,比如说:检查网络和系统的资源可用性,数据库资源是否可用,应用程序依赖的消息中间件或者 Azure cloud service 的可用性 等等,这篇文章我们就来讨论如何使用这个 健康检查中间件. 注册健康检查

  • 详解如何在ASP.NET Core中使用IHttpClientFactory

    利用IHttpClientFactory可以无缝创建HttpClient实例,避免手动管理它们的生命周期. 当使用ASP.Net Core开发应用程序时,可能经常需要通过HttpClient调用WebAPI的方法以检查终结点是否正常工作.要实现这一点,通常需要实例化HttpClient并使用该实例来调用你的方法.但是直接使用HttpClient也有一些缺点,主要与手动管理实例的生命周期有关. 你可以使用IHttpClientFactory创建HttpClient来避免这些问题.IHttpClie

  • 详解Springboot整合ActiveMQ(Queue和Topic两种模式)

    写在前面: 从2018年底开始学习SpringBoot,也用SpringBoot写过一些项目.这里对学习Springboot的一些知识总结记录一下.如果你也在学习SpringBoot,可以关注我,一起学习,一起进步. ActiveMQ简介 1.ActiveMQ简介 Apache ActiveMQ是Apache软件基金会所研发的开放源代码消息中间件:由于ActiveMQ是一个纯Java程序,因此只需要操作系统支持Java虚拟机,ActiveMQ便可执行. 2.ActiveMQ下载 下载地址:htt

  • 详解node服务器中打开html文件的两种方法

    本文介绍了详解node服务器中打开html文件的两种方法,分享给大家,具体如下: 方法1:利用 Express 托管静态文件,详情查看这里 方法2:使用fs模块提供的readFile方法打开文件,让其以text/html的形式输出. 代码: var express = require('express'); var fs=require("fs"); var app = express(); //方法1:通过express.static访问静态文件,这里访问的是ajax.html //

  • 详解IIS中的重写工具下关于操作重定向URL中的{R:N}与{C:N}使用介绍

    URL Rewrite(URL重写工具)作为IIS下较为常用的模块组件, 提供了重写.重定向.自定义响应.中止请求等功能.但是其相关的中文资料比较缺少,官方倒是有完整和详细的英文文档,之前我在项目中遇到需要设置重写/重定向URL操作规则时,对于范例中的{R:N}和{C:N}规则就理解的十分辛苦,因此写下本文分享下经验. 这里先附上官网的文档,其实文档链接在IIS的URL重写模块的右边菜单就有:URL Rewrite Module Configuration Reference(URL重写模块配置

  • 详解如何在ASP.NET Core中应用Entity Framework

    首先为大家提醒一点,.NET Core和经典.NET Framework的Library是不通用的,包括Entity Framework! 哪怎么办? 别急,微软为.NET Core发布了.NET Core版本的Entity Framework,具体配置方法与经典.NET Framework版本的稍有区别,下面的内容就为带领大家在ASP.NET Core中应用Entity Framework DB first. 注:目前部分工具处于Preview版本,正式版本可能会稍有区别. 前期准备: 1.推

  • 详解如何在ASP.NET Core中使用Route特性

    ASP.NET Core 中的 Route 中间件的职责在于将 request 匹配到各自 Route 处理程序上,Route 分两种:基于约定 和 基本特性 模式. 基于约定 模式的Route采用集中化的方式,而 基于特性 的方式允许你在 Action 或者 Controller 上单独定义,到底采用哪一种可以基于你自己的应用场景,本篇就来讨论如何使用 基于特性 模式. 创建 Controller 类 创建一个 DefaultController 类,新增如下代码. public class

随机推荐