詳解IIS在ASP.NET?Core下的兩種部署模式
KestrelServer最大的優(yōu)勢體現(xiàn)在它的跨平臺的能力,如果ASP.NET CORE應(yīng)用只需要部署在Windows環(huán)境下,IIS也是不錯的選擇。ASP.NET CORE應(yīng)用針對IIS具有兩種部署模式,它們都依賴于一個IIS針對ASP.NET CORE Core的擴展模塊。本文提供的示例演示已經(jīng)同步到《ASP.NET Core 6框架揭秘-實例演示版》)
一、ASP.NET CORE Core Module
IIS其實也是按照管道的方式來處理請求的,但是IIS管道和ASP.NET CORE中間件管道有本質(zhì)的不同。對于部署在IIS中的Web應(yīng)用來說,從最初接收到請求到最終將響應(yīng)發(fā)出去,這段處理流程被細(xì)分為一系列固定的步驟,每個都具有一個或者兩個(前置+后置)對應(yīng)的事件或者回調(diào)。我們可以利用自定義的Module注冊相應(yīng)的事件或回調(diào)在適當(dāng)?shù)臅r機接管請求,并按照自己希望的方式對它進(jìn)行處理。
IIS提供了一系列原生(Native)的Module,我們也可以使用任意.NET語言編寫托管的Module,整合IIS和ASP.NET CORE 的這個ASP.NET CORE Core Module就是一個原生的Module。它利用注冊的事件將請求從IIS管道中攔截下來,并轉(zhuǎn)發(fā)給ASP.NET CORE管道進(jìn)行處理。相應(yīng)的安裝包可以從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應(yīng)用運行在IIS的工作進(jìn)程w3wp.exe中(如果采用IIS Express,工作進(jìn)程為iisexpress.exe)。如圖18-7所示,ASP.NET CORE應(yīng)用在這種模式下使用的服務(wù)器類型是IISHttpServer,上述的ASP.NET CORE Core Module會將原始的請求轉(zhuǎn)發(fā)給這個服務(wù)器,并將后者生成響應(yīng)轉(zhuǎn)交給IIS服務(wù)器進(jìn)行回復(fù)。
圖1 In-Process部署模式
In-Process是默認(rèn)采用的部署模式,所以我們不需要為此做任何設(shè)置,接下來我們就來演示一下具體的部署方式。我們在IIS的默認(rèn)站點(Defaut Web Site)創(chuàng)建一個名為WebApp的應(yīng)用,并將映射的物理路徑設(shè)置為“C:\App”。然后我們創(chuàng)建一個空的ASP.NET CORE程序,并編寫了如下這個將當(dāng)前進(jìn)程名稱作為響應(yīng)內(nèi)容的演示程序。
using System.Diagnostics; var app = WebApplication.Create(args); app.Run(context => context.Response.WriteAsync(Process.GetCurrentProcess().ProcessName)); app.Run();
然后我們在Visual Studio的解決方案視圖右鍵選擇該項目,在彈出的菜單中選擇“發(fā)布(Publish)”選項,創(chuàng)建一個指向“C:\App”的Publish Profile,然后執(zhí)行這個Profile完成發(fā)布工作。應(yīng)用發(fā)布也可以執(zhí)行命令行“dotnet public”來完成。應(yīng)用部署好之后,我們利用瀏覽器采用地址“http://localhost/webapp”訪問部署好的應(yīng)用,從圖2所示的輸出結(jié)果可以看出ASP.NET CORE應(yīng)用實際上就運行在IIS的工作進(jìn)程中。
圖2 In-Process模式下的進(jìn)程名稱
如果我查看此時的部署目錄(“C:\App”),會發(fā)現(xiàn)生成的程序集和配置文件。應(yīng)用既然部署在IIS中,那么具體的配置自然定義在web.config中,如下所示的就是這個文件的內(nèi)容。我們會發(fā)現(xiàn)所有的請求(path="*" verb="*")都被映射到“AspNetCoreModuleV2”這個Module上,這就是上面介紹的ASP.NET CORE Core Module。至于這個Module如果啟動ASP.NET CORE管道并與之交互,則由后面的<aspNetCore>配置節(jié)來控制,可以看到它將表示部署模式的hostingModel屬性設(shè)置為“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,對應(yīng)的配置選項定義在IISServerOptions中。如果具有同步讀寫請求和響應(yīng)主體內(nèi)容的需要,我們需要將AllowSynchronousIO屬性(默認(rèn)為False)設(shè)置為True。如果將AutomaticAuthentication屬性返回True(默認(rèn)值),認(rèn)證用戶將自動賦值給HttpContext上下文的User屬性。我們可以利用MaxRequestBodyBufferSize(默認(rèn)為1,048,576)和MaxRequestBodySize屬性(默認(rèn)為30,000,000)設(shè)置接收請求主體的緩沖區(qū)的容量,和最大請求主體的字節(jié)數(shù)。
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的注冊實現(xiàn)在IWebHostBuilder接口如下這個UseIIS擴展方法中。由于這個方法并沒有提供一個Action<IISServerOptions>委托參數(shù)對IISServerOptions配置選項進(jìn)行設(shè)置,所以我們不得不采用原始的對它進(jìn)行設(shè)置。由于IHostBuider接口ConfigureWebHostDefaults擴展方法內(nèi)部會調(diào)用這個方法, 我們并不需要為此做額外的工作。
public static class WebHostBuilderIISExtensions { public static IWebHostBuilder UseIIS(this IWebHostBuilder hostBuilder); }
三、Out-of-Process部署模式
ASP.NET CORE應(yīng)用在IIS中還可以采用Out-of -Process模式進(jìn)行部署。如圖3所示,在這種部署下,采用KestrelServer的ASP.NET CORE應(yīng)用運行在獨立的dotnet.exe進(jìn)程中。當(dāng)IIS接受到針對目標(biāo)應(yīng)用的請求時,如果目標(biāo)應(yīng)用所在的進(jìn)程并未啟動,ASP.NET CORE Core Module還負(fù)責(zé)執(zhí)行dotnet命令激活此進(jìn)程,相當(dāng)于充當(dāng)了WAS(Windows Activation Service)的作用。
圖3 Out-of-Process部署模式
在激活A(yù)SP.NET CORE承載進(jìn)程之前,ASP.NET CORE Core Module會選擇一個可用的端口號,該端口號和當(dāng)前應(yīng)用的路徑(該路徑將作用ASP.NET CORE應(yīng)用的PathBase)被寫入環(huán)境變量,對應(yīng)的環(huán)境變量名稱分別為“ASPNETCORE_PORT”和“ASPNETCORE_APPL_PATH”。以O(shè)ut-of-Process模式部署的ASP.NET CORE應(yīng)用只會接收IIS轉(zhuǎn)發(fā)給它的請求,為了能夠過濾其它來源的請求,ASP.NET CORE Core Module會生成一個Token并寫入環(huán)境變量“ASPNETCORE_TOKEN”。后續(xù)轉(zhuǎn)發(fā)的請求會利用一個報頭“MS-ASPNETCORE-TOKEN”傳遞此Token,ASP.NET CORE應(yīng)用會校驗是否與之前生成的Token匹配。
ASP.NET CORE Core Module還會利用環(huán)境變量傳遞其他一些設(shè)置,認(rèn)證方案會寫入環(huán)境變量“ASPNETCORE_IIS_HTTPAUTH”,另一個“ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED”環(huán)境變量用來設(shè)置針對Web Socket的支持狀態(tài)。由于這些環(huán)境變量名稱的前綴都是“ASPNETCORE_”,所以它們會作為默認(rèn)配置源。KestrelServer最終會綁定到基于該端口的本地終結(jié)點(“localhost”)進(jìn)行監(jiān)聽。由于監(jiān)聽地址是由ASP.NET CORE Core Module控制的,所以它只需要將請求往該地址進(jìn)行轉(zhuǎn)發(fā),最終將接收到響應(yīng)交給IIS返回即可。由于這里涉及本地回環(huán)網(wǎng)絡(luò)(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的部署方式,現(xiàn)在我們直接修改配置文件web.config,按照上面的方式將<aspNetCore>配置節(jié)的hostingModel屬性設(shè)置為“outofprocess”,部署的應(yīng)用就自動切換到Out-of-Process。此時再次以相同的方式訪問部署的應(yīng)用,我們會發(fā)現(xiàn)瀏覽器上顯示的進(jìn)程名稱變成了“dotnet”。
圖4 Out-of-Process模式下的進(jìn)程名稱
部署模式可以直接定義在項目文件中,如果按照如下的方式將AspNetCoreHostingModel屬性設(shè)置為“OutOfProcess”,那么發(fā)布后生成的web.config中針對部署模式的設(shè)置將隨之改變。該屬性默認(rèn)值為“InProcess”,我們也可以顯式進(jìn)行設(shè)置。
<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>
為了進(jìn)一步驗證上述的這一系列環(huán)境變量是否存在,如下所示的演示程序會將以“ASPNETCORE_”為前綴的環(huán)境變量作為響應(yīng)內(nèi)容輸出來。除此之外,作為響應(yīng)輸出的還有進(jìn)程名稱、請求的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()); }
應(yīng)用重新發(fā)布之后,再次利用瀏覽器訪問后回得到如圖5所示的結(jié)果。我們可以從這里找到上述的環(huán)境變量,請求攜帶的“MS-ASPNETCORE-TOKEN”報頭正好與對應(yīng)環(huán)境變量的值一致,應(yīng)用在IIS中的虛擬目錄作為了應(yīng)用路徑被寫入環(huán)境變量并成為請求的PathBase。如果站點提供了HTTPS終結(jié)點,其端口還會寫入“SPNETCORE_ANCM_HTTPS_PORT”這個環(huán)境變量,這是為了實現(xiàn)針對HTTPS終結(jié)點的重定向而設(shè)計的。
圖5 Out-of-Process模式下環(huán)境變量
Out-of-Process部署的大部分實現(xiàn)都是由如下這個IISMiddleware中間件來完成的,IISOptions為對應(yīng)的配置選項。IISMiddleware中間件完成了針對“配對Token”的驗證過濾非IIS轉(zhuǎn)發(fā)的請求。如果IISOptions配置選項的ForwardClientCertificate屬性返回True(默認(rèn)值),此中間件會從請求報頭“MS-ASPNETCORE-CLIENTCERT”中提取客戶端證書,并將它保存到ITlsConnectionFeature特性中。該中間件還會將當(dāng)前Windows賬號對應(yīng)的WindowsPrincipal對象附加到HttpContext上下文的特性集合中,如果IISOptions配置選項的AutomaticAuthentication屬性返回True(默認(rèn)值),該對象會直接賦值給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根據(jù)請求激活工作進(jìn)程w3wp.exe。如果站點長時間未曾訪問,它還會自動關(guān)閉工作進(jìn)程。如果工作進(jìn)程都關(guān)閉了,承載ASP.NET CORE應(yīng)用的dotnet.exe進(jìn)程自然也應(yīng)該關(guān)閉。為了關(guān)閉應(yīng)用承載進(jìn)程,ASP.NET CORE Core Module會發(fā)送一個特殊的請求,該請求攜帶一個值為“shutdown”的“MS-ASPNETCORE-EVENT”報頭,IISMiddleware中間件在接收到該請求時會利用注入的IHostApplicationLifetime對象關(guān)閉當(dāng)前應(yīng)用。如果不支持WebSocket,該中間件還會將代表“可升級到雙向通信”的IHttpUpgradeFeature特性刪除。將應(yīng)用路徑設(shè)置為請求的PathBase也是由這個中間件完成的。由于IISMiddleware中間件所作的實際上是對HttpContext上下文進(jìn)行初始化的工作,所以它必須優(yōu)先執(zhí)行才有意義,為了將此中間件置于管道的前端,如下這個IISSetupFilter被定義出來完成對該中間件的注冊。
internal class IISSetupFilter : IStartupFilter { internal IISSetupFilter(string pairingToken, PathString pathBase, bool isWebsocketsSupported); public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next); }
IISSetupFilter最終是通過IWebHostBuilder接口如下這個UseIISIntegration擴展方法進(jìn)行注冊的。這個方法還負(fù)責(zé)從當(dāng)前配置和環(huán)境變量提取端口號,并完成監(jiān)聽地址的注冊。由于KestrelServer默認(rèn)會選擇注冊到服務(wù)器上的終結(jié)點,所以該方法會利用配置將IServerAddressesFeature特性的PreferHostingUrls屬性設(shè)置為True,這里設(shè)置的監(jiān)聽地址才會生效。這個方法還會根據(jù)當(dāng)前IIS站點的設(shè)置對IISOptions作相應(yīng)設(shè)置。由于IHostBuider接口ConfigureWebHostDefaults擴展方法內(nèi)部也會調(diào)用這個方法,我們并不需要為此做額外的工作。
public static class WebHostBuilderIISExtensions { public static IWebHostBuilder UseIISIntegration(this IWebHostBuilder hostBuilder); }
四、<aspnetcore>配置
不論是采用何種部署模式,相關(guān)的配置都定義在部署目錄下的web.config配置文件,它提供的針對ASP.NET CORE Core Module的映射使我們能夠?qū)SP.NET CORE應(yīng)用部署在IIS中。在web.config中,與ASP.NET CORE應(yīng)用部署相關(guān)的配置定義在<aspNetCore>配置節(jié)中。
<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>配置屬性,下表對這些配置進(jìn)行了簡單的說明。設(shè)置的文件可以采用絕對路徑和相對于部署目錄(通過 “.”表示)的相對路徑。
屬性 | 含 義 |
processPath | ASP.NET CORE應(yīng)用啟動命令所在路徑,必需。 |
arguments | ASP.NET CORE應(yīng)用啟動傳入的參數(shù),可選。 |
stdoutLogEnabled | 是否將stdout 和stderr輸出到 stdoutLogFile屬性指定的文件,默認(rèn)為False。 |
stdoutLogFile | 作為stdout 和stderr輸出的日志文件,默認(rèn)為“ aspnetcore-stdout”。 |
hostingModel | 部署模式,“inprocess/InProcess”或者“outofprocess/OutOfProcess”(默認(rèn)值)。 |
forwardWindowsAuthToken | 是否轉(zhuǎn)發(fā)Windows認(rèn)證令牌,默認(rèn)為True。 |
processesPerApplication | 承載ASP.NET CORE應(yīng)用的進(jìn)程( processPath)數(shù),默認(rèn)為1。該配置對In-Process模式無效。 |
rapidFailsPerMinute | ASP.NET CORE應(yīng)用承載進(jìn)程( processPath)每分鐘允許崩潰的次數(shù),默認(rèn)為10,超過此數(shù)量將不再試圖重新啟動它。 |
requestTimeout | 請求處理超時時間,默認(rèn)為2分鐘。 |
startupRetryCount | ASP.NET CORE應(yīng)用承載進(jìn)程啟動重試次數(shù),默認(rèn)為2次。 |
startupTimeLimit | ASP.NET CORE應(yīng)用承載進(jìn)程啟動超時時間(單位為秒),默認(rèn)為120秒。 |
environmentVariables | 設(shè)置環(huán)境變量。 |
handlerSettings | 為ASP.NET CORE Core Module提供額外的配置。 |
到此這篇關(guān)于IIS在ASP.NET Core下的兩種部署模式的文章就介紹到這了,更多相關(guān)ASP.NET Core部署模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
.net core高吞吐遠(yuǎn)程方法如何調(diào)用組件XRPC詳解
這篇文章主要給大家介紹了關(guān)于.net core高吞吐遠(yuǎn)程方法如何調(diào)用組件XRPC的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用.net core具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Web.config(應(yīng)用程序的配置信息)總結(jié)
Web.config文件是一個XML文本文件,它用來儲存 ASP.NET Web 應(yīng)用程序的配置信息(如最常用的設(shè)置ASP.NET Web 應(yīng)用程序的身份驗證方式),它可以出現(xiàn)在應(yīng)用程序的每一個目錄中,接下來詳細(xì)介紹一下配置情況,感興趣的朋友可以了解下2013-01-01ASP.NET獲取不到JS設(shè)置cookies的解決方法
有時候通過一個頁面設(shè)置的js,在其它的子頁面或父頁面就無法正常獲取,主要是因為路徑的問題, 設(shè)置到根目錄中即可。下面是具體的解決方法。2010-12-12asp.net 用繼承方法實現(xiàn)頁面判斷session
在做ASP項目的時候,判斷用戶是否登陸常用的方法是在每個頁面判斷session是否存在,無奈用java的時候過濾器就用的不熟。。。還是用繼承吧。汗。。。2009-09-09.NET Core 遷移躺坑記續(xù)集之Win下莫名其妙的超時
這篇文章主要介紹了.NET Core 遷移躺坑記續(xù)集之Win下莫名其妙的超時,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04asp.net 頁面逐步呈現(xiàn)的方法總結(jié)
分塊編碼 ( chunked encoding )就是讓 response 分塊編碼進(jìn)行傳輸。response 分塊編碼,可以先傳輸一部分不需要處理的 html 代碼到客戶端,等其他耗時代碼執(zhí)行完畢后再傳輸另外的 html 代碼。2010-06-06