ASP.NET Core跨平台技术内幕
ASP.NET Core設(shè)計初衷是開源跨平臺、高性能Web服務(wù)器,其中跨平臺特性較早期ASP.NET是一個顯著的飛躍,.NET現(xiàn)可以理直氣壯與JAVA同臺競技,而ASP.NET Core的高性能特性更是成為致勝法寶。
ASP.NET Core 2.1+為IIS托管新增In-Process模型并作為默認(rèn)選項(使用IISHttpServer替代了Kestrel,dotnet程序由IIS網(wǎng)站進(jìn)程w3wp.exe內(nèi)部托管)。
為展示ASP.NET Core跨平臺特性,本文重點著墨經(jīng)典的Out-Process托管模型。
宏觀設(shè)計
為解耦平臺web服務(wù)器差異,程序內(nèi)置Http服務(wù)組件Kestrel,由web服務(wù)器轉(zhuǎn)發(fā)請求到Kestrel。
老牌web服務(wù)器定位成反向代理服務(wù)器,轉(zhuǎn)發(fā)請求到ASP.NET Core程序(分別由IIS ASP.NET Core Module和Nginx負(fù)責(zé))
常規(guī)代理服務(wù)器,只用于代理內(nèi)部主機(jī)對外網(wǎng)的連接需求,一般不支持外部對內(nèi)部網(wǎng)絡(luò)的訪問請求;?當(dāng)一個代理服務(wù)器能夠代理外部網(wǎng)絡(luò)的主機(jī),訪問內(nèi)部網(wǎng)絡(luò),這種代理服務(wù)器被稱為反向代理服務(wù)器?。
平臺web代理服務(wù)器、ASP.NET Core程序(dotnet.exe) 均為獨立進(jìn)程,平臺自行決定互動細(xì)節(jié),只需確保平臺web服務(wù)器與Kestrel形成Http通信。
Kestrel
與老牌web服務(wù)器解耦,實現(xiàn)跨平臺部署。
Kestrel使ASP.NET Core具備了基本web服務(wù)器的能力,在內(nèi)網(wǎng)部署和開發(fā)環(huán)境完全可使用dotnet.exe自宿模式運(yùn)行。
Kestrel定位是Http服務(wù)組件,實力還比不上老牌web服務(wù)器,在timeout機(jī)制、web緩存、響應(yīng)壓縮等不占優(yōu)勢,在安全性等方面還有缺陷。
因此在生產(chǎn)環(huán)境中必須使用老牌web服務(wù)器反向代理請求。
跨平臺管控程序,轉(zhuǎn)發(fā)請求
要實現(xiàn)企業(yè)級穩(wěn)定部署:
*nix平臺
將ASP.NET Core程序以dotnet.exe自宿模式運(yùn)行,并配置為系統(tǒng)守護(hù)進(jìn)程(管控應(yīng)用),再由Nginx轉(zhuǎn)發(fā)請求。
以下使用systemd創(chuàng)建進(jìn)程服務(wù)文件 /etc/systemd/system/kestrel-eqidproxyserver.service
[Unit] Description=EqidProxyServer deploy on centos[Service] WorkingDirectory=/var/www/eqidproxyserver/eqidproxyServer ExecStart=/usr/bin/dotnet /var/www/eqidproxyserver/eqidproxyServer/EqidProxyServer.dll Restart=always # Restart service after 10 seconds if the dotnet service crashes: RestartSec=10 TimeoutStopSec=90 KillSignal=SIGINT SyslogIdentifier=dotnet-example User=root Environment=ASPNETCORE_ENVIRONMENT=Production Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false[Install] WantedBy=multi-user.target //?啟用服務(wù),在localhost:5000端口偵聽請求 sudo systemctl enable kestrel-eqidproxyserver.service安裝Nginx,并配置Nginx轉(zhuǎn)發(fā)請求到localhost:5000:
server {listen 80;server_name default_website;root /usr/share/nginx/html;# Load configuration files for the default server block.include /etc/nginx/default.d/*.conf;location / {proxy_pass http://localhost:5000;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection keep-alive;proxy_set_header Host $host;proxy_cache_bypass $http_upgrade;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;}}Windows平臺
[ 管控應(yīng)用、轉(zhuǎn)發(fā)請求] 由ASP.NET Core Module(插入在IIS Pipeline中的原生組件,下面簡稱ACM)一手操辦,w3wp.exe、dotnet.exe的互動關(guān)系是通過父子進(jìn)程維系。
下圖腳本力證dotnet.exe進(jìn)程是w3wp.exe創(chuàng)建出來的子進(jìn)程:
得益此關(guān)系,ACM在創(chuàng)建dotnet.exe子進(jìn)程時能指定環(huán)境變量,約定donet.exe接收(IIS轉(zhuǎn)發(fā)的請求)的偵聽端口。
實際源碼看ACM為子進(jìn)程設(shè)定三個重要的環(huán)境變量:
ASPNETCORE_PORT 約定 Kestrel將會在此端口上監(jiān)聽
ASPNETCORE_APPL_PATH
ASPNETCORE_TOKEN ? 約定 攜帶該Token的請求為合法的轉(zhuǎn)發(fā)請求
與ACM夫唱婦隨的是UseIISIntegration()擴(kuò)展方法,完成如下工作:
① 啟動Kestrel服務(wù)在http://localhost:{ASPNETCORE_PORT}上監(jiān)聽
② 根據(jù) {ASPNETCORE_TOKEN}?檢查請求是否來自ACM轉(zhuǎn)發(fā)
ACM轉(zhuǎn)發(fā)的請求,會攜帶名為MS-ASPNETCORE-TOKEN:******的Request Header,以便dotnet.exe對比研判。
③ 利用ForwardedHeaderMiddleware中間件保存原始請求信息
linux平臺部署需要手動啟用ForwardedHeader?middleware https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1
源碼快速驗證:
namespace Microsoft.AspNetCore.Hosting {public static class WebHostBuilderIISExtensions{// These are defined as ASPNETCORE_ environment variables by IIS's AspNetCoreModule.private static readonly string ServerPort = "PORT";private static readonly string ServerPath = "APPL_PATH";private static readonly string PairingToken = "TOKEN";private static readonly string IISAuth = "IIS_HTTPAUTH";private static readonly string IISWebSockets = "IIS_WEBSOCKETS_SUPPORTED";/// <summary>/// Configures the port and base path the server should listen on when running behind AspNetCoreModule./// The app will also be configured to capture startup errors.public static IWebHostBuilder UseIISIntegration(this IWebHostBuilder hostBuilder){var port = hostBuilder.GetSetting(ServerPort) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPort}");var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}");var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}");var iisAuth = hostBuilder.GetSetting(IISAuth) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISAuth}");var websocketsSupported = hostBuilder.GetSetting(IISWebSockets) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISWebSockets}");bool isWebSocketsSupported;if (!bool.TryParse(websocketsSupported, out isWebSocketsSupported)){// If the websocket support variable is not set, we will always fallback to assuming websockets are enabled.isWebSocketsSupported = (Environment.OSVersion.Version >= new Version(6, 2));}if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken)){// Set flag to prevent double service configurationhostBuilder.UseSetting(nameof(UseIISIntegration), true.ToString());var enableAuth = false;if (string.IsNullOrEmpty(iisAuth)){// back compat with older ANCM versionsenableAuth = true;}else{// Lightup a new ANCM variable that tells us if auth is enabled.foreach (var authType in iisAuth.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)){if (!string.Equals(authType, "anonymous", StringComparison.OrdinalIgnoreCase)){enableAuth = true;break;}}}var address = "http://127.0.0.1:" + port;hostBuilder.CaptureStartupErrors(true);hostBuilder.ConfigureServices(services =>{// Delay register the url so users don't accidentally overwrite it.hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, address);hostBuilder.PreferHostingUrls(true);services.AddSingleton<IServerIntegratedAuth>(_ => new ServerIntegratedAuth(){IsEnabled = enableAuth,AuthenticationScheme = IISDefaults.AuthenticationScheme});services.AddSingleton<IStartupFilter>(new IISSetupFilter(pairingToken, new PathString(path), isWebSocketsSupported));services.Configure<ForwardedHeadersOptions>(options =>{options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;});services.Configure<IISOptions>(options =>{options.ForwardWindowsAuthentication = enableAuth;});services.AddAuthenticationCore();});}return hostBuilder;}} }總結(jié)
?ASP.NET Core跨平臺的核心在于 程序內(nèi)置Kestrel HTTP通信組件,解耦web服務(wù)器差異;依平臺特性約定Http通信細(xì)節(jié)。
本文從框架設(shè)計初衷、進(jìn)程模型、組件交互驗證我對ASP.NET Core跨平臺特性的理解。
+ CentOS上部署ASP.NET Core完整版請參考:https://www.cnblogs.com/JulianHuang/p/10455644.html
往期精選
AspNetCore結(jié)合Redis實踐消息隊列
TPL Dataflow組件應(yīng)對高并發(fā),低延遲要求
AspNetCore應(yīng)用注意這一點,CTO會對你刮目相看
實例解讀Docker Swarm
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core跨平台技术内幕的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据丢失引起宕机怎么办?
- 下一篇: .NETCore3.1中的Json互操作