原文:深度理解IIS下部署ASP.NET Core2.1 Web应用拓扑图

IIS部署ASP.NET Core2.1 应用拓扑图

我们看到相比Asp.Net, 出现了3个新的组件:ASP.NET Core Module、Kestrel、dotnet.exe, 后面我们会理清楚这三个组件的作用和组件之间的交互原理。

 

引入Kestrel的原因

进程内HTTP服务器,与老牌web服务器解耦,实现跨平台部署

IIS、Nginx、Apache等老牌web服务器有他们自己的启动进程和环境;为了实现跨平台部署,需要与这些web服务器的功能解耦,网络通信是一个比较好的选择。

作为进程内Http服务器,ASP.NET Core 保持独立运作一个Http服务器的能力,由这些老牌web服务器充当反向代理服务器将请求转发给进程内Http服务器,这样保持了开发过程中配置Startup、Program文件的纯粹性 和部署时候的跨平台能力。

客观上Kestrel还是作为Http服务器,功能还比不上老牌的web服务器

Kestrel自诞生之日起还有一些网络安全方面的缺陷,这些缺陷包括一个合适的timeouts,Size limits,和并发数量等

 
也就是说从主观和客观上都要求生产部署使用反向代理服务器
 

在内网部署和开发环境中我们完全可以使用Kestrel来充当web服务器。

Kestrel的底层实现细节:Kestrel 要做到跨平台HTTP服务器,需要脱离底层系统细节实现跨平台IO,在Asp.NetCore2.1 版本之前使用libuv(一个高性能跨平台IO库),2.1 版本之后采用的托管Sockets,这是一个巨大的变化,有兴趣的可以搜索一下。

 

引入ASP.NET Core Module

反向代理服务器的作用是将请求转发给内网的Http服务器,IIS上使用ASP.NET Core Module组件将请求转发到Kestrel Http服务器; 注意该组件只在IIS上有效。

从整个拓扑图上看,请求首先到达内核态Http.sys Driver,该驱动将请求路由到IIS上指定网站;

然后Asp.Net Core Module将请求转发给Kestrel服务器。

 

作为企业级服务ASP.NET Core Module需要完成:

  • 进程管理: 控制webApp启动进程内Kestrel服务器在某端口上启动,并监听转发请求

  • 故障恢复: 控制webapp在1min内崩溃重启的次数

  • 请求转发

  • 启动日志记录: webapp启动失败,可通过配置将日志输出到指定目录

  • 请求头信息转发:代理服务器需要保证传递 源IP地址、源Scheme、原始Host请求头等信息

  • 转发windiws认证token

以上能力,可以参考https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.1
给出的AspNetCore Module配置参数

自然可以猜想ASP.NET Core Module与IISIntegration()关系很是密切:

  1. Web启动的时候,ASP.NET Core Module会通过环境变量指定kestrel监听的端口;

  2. IIS Integration方法则配置服务器在http://localhost:{指定端口}上监听。

  3. 同时开始检查请求是否来自AspNet Core Module(非ASPNE TCore Module转发的请求会被拒绝)

另外由于反向代理服务器的存在,webapp可能会丢失原始请求信息; 比如

-  源IP地址

-  scheme: 反向代理服务器和Kestrel之间通过Http交互

-  可能被代理服务器修改的原始Host请求头

这些工作由ForwardedHeader middleware完成,该中间件由UseIISIntegration方法默认开启。

 薅一下UseIISIntegration() 源码:
  1. //------------- 节选自Microsoft.AspNetCore.Hosting.WebHostBuilderIISExtensions---------------------
  2. public static class WebHostBuilderIISExtensions
  3. {
  4. // These are defined as ASPNETCORE_ environment variables by IIS's AspNetCoreModule.
  5. private static readonly string ServerPort = "PORT";
  6. private static readonly string ServerPath = "APPL_PATH";
  7. private static readonly string PairingToken = "TOKEN";
  8. private static readonly string IISAuth = "IIS_HTTPAUTH";
  9. private static readonly string IISWebSockets = "IIS_WEBSOCKETS_SUPPORTED";
  10.  
  11. /// <summary>
  12. /// Configures the port and base path the server should listen on when running behind AspNetCoreModule.
  13. /// The app will also be configured to capture startup errors.
  14. /// </summary>
  15. /// <param name="hostBuilder"></param>
  16. /// <returns></returns>
  17. public static IWebHostBuilder UseIISIntegration(this IWebHostBuilder hostBuilder)
  18. {
  19. if (hostBuilder == null)
  20. {
  21. throw new ArgumentNullException(nameof(hostBuilder));
  22. }
  23.  
  24. // Check if `UseIISIntegration` was called already
  25. if (hostBuilder.GetSetting(nameof(UseIISIntegration)) != null)
  26. {
  27. return hostBuilder;
  28. }
  29.  
  30. var port = hostBuilder.GetSetting(ServerPort) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPort}");
  31. var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}");
  32. var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}");
  33. var iisAuth = hostBuilder.GetSetting(IISAuth) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISAuth}");
  34. var websocketsSupported = hostBuilder.GetSetting(IISWebSockets) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISWebSockets}");
  35.  
  36. bool isWebSocketsSupported;
  37. if (!bool.TryParse(websocketsSupported, out isWebSocketsSupported))
  38. {
  39. // If the websocket support variable is not set, we will always fallback to assuming websockets are enabled.
  40. isWebSocketsSupported = (Environment.OSVersion.Version >= new Version(6, 2));
  41. }
  42.  
  43. if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken))
  44. {
  45. // Set flag to prevent double service configuration
  46. hostBuilder.UseSetting(nameof(UseIISIntegration), true.ToString());
  47.  
  48. var enableAuth = false;
  49. if (string.IsNullOrEmpty(iisAuth))
  50. {
  51. // back compat with older ANCM versions
  52. enableAuth = true;
  53. }
  54. else
  55. {
  56. // Lightup a new ANCM variable that tells us if auth is enabled.
  57. foreach (var authType in iisAuth.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
  58. {
  59. if (!string.Equals(authType, "anonymous", StringComparison.OrdinalIgnoreCase))
  60. {
  61. enableAuth = true;
  62. break;
  63. }
  64. }
  65. }
  66.  
  67. var address = "http://127.0.0.1:" + port;
  68. hostBuilder.CaptureStartupErrors(true);
  69.  
  70. hostBuilder.ConfigureServices(services =>
  71. {
  72. // Delay register the url so users don't accidently overwrite it.
  73. hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, address);
  74. hostBuilder.PreferHostingUrls(true);
  75. services.AddSingleton<IStartupFilter>(new IISSetupFilter(pairingToken, new PathString(path), isWebSocketsSupported));
  76. services.Configure<ForwardedHeadersOptions>(options =>
  77. {
  78. options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
  79. });
  80. services.Configure<IISOptions>(options =>
  81. {
  82. options.ForwardWindowsAuthentication = enableAuth;
  83. });
  84. services.AddAuthenticationCore();
  85. });
  86. }
  87.  
  88. return hostBuilder;
  89. }
  90. }

可以看到Web程序在配置启动时读取了5个由AspNET Core Module设置的环境变量。

着重理解Kestrel是怎么拒绝来自非AspNetCore Module转发的请求:

  1. AspNetCore Module 设置TOKEN=XXX环境变量

  2. WebApp启动时读取Token=XXX环境变量

  3. AspNetCore Module在转发请求时,会在Request里面加上一个 MS-ASPNETCORE-TOKEN:XXX的请求头

  4. IISMiddleware中间件将会起作用:携带了该请求头的转发请求被认为有效

  1. //---------------节选自Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware----------------------
  2. public async Task Invoke(HttpContext httpContext)
  3. {
  4. if (!string.Equals(_pairingToken, httpContext.Request.Headers[MSAspNetCoreToken], StringComparison.Ordinal))
  5. {
  6. _logger.LogError($"'{MSAspNetCoreToken}' does not match the expected pairing token '{_pairingToken}', request rejected.");
  7. httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
  8. return;
  9. }
  10. ......
  11. }
 20181205 更新,.NetCore2.2+ ASP.NETCore Module支持进程内托管模型,本文章内容针对AspNetCore2.1
 

FAQ:

1. 什么叫反向代理服务器Reverse Proxy Server?

通常的代理服务器,只用于代理内部网络对Internet的连接需求,客户机必须指定代理服务器将本来要直接发送到web服务器上的http请求发送到代理服务器中,普通的代理服务器不支持外部对内部网络的访问请求;

当一个代理服务器能够代理外部网络的主机,访问内部网络,这种代理服务器的方式称为反向代理服务器 。

在AspNet Core Web应用IIS、Ngnix转发请求时,内部的Kestrel和Applicaiton Code组成完整的内部网络,IIS、Nginx等web服务器在这里的角色就起到了反向代理作用。

 

2. 依照上图的拓扑图我们理所当然可以认为背靠在IIS后面的Kestrel应该是可以访问的,因为它也是一个web服务器,那么怎样直接访问背靠在AspNetCore Module后面的的Kestrel服务器?

按照上文的理论,背靠在AspNetCore Module后面的Web进程是依靠 Token来拒绝【非AspNetCore Module转发的请求】。

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.2

因此,将该PairToken拷贝到请求头,我们也是可以在Kestrel behind IIS的情况下,直接访问Kestrel(虽然这种情况不常见,但是对于我们理解拓扑图很有帮助)。

步骤如下:

  1. 找到dotnet进程: tasklist | findstr "dotnet"

  2. 找到该进程占用port : netstat -ano | findstr {pid}

  3. 利用输出的port: curl localhost:{port}, 会提示400 badrequest, 这与源码的返回一致

  4. 从error log 中拷贝出该MS-ASPNETCORE-TOKEN, 附在request header中便可以访问

--------------如果你觉得文章对你有价值,请点赞或者关注,支持原创势力,蟹蟹--------------~~。。~~--------------~~。。~~----------------

深度理解IIS下部署ASP.NET Core2.1 Web应用拓扑图的更多相关文章

  1. Web Server 在iis下部署asp网站在iis下

    Web Server 在iis下部署asp网站在iis下 一.参考地址: win7 http://jingyan.baidu.com/article/636f38bb1bbcadd6b846108b. ...

  2. 在IIs上部署asp.net core2.1项目

    转自:https://www.cnblogs.com/jasonduan/p/9193702.html 在IIS上部署你的ASP.NET Core 2.1项目   1.在控制面板→程序→启用或关闭Wi ...

  3. 【续集】在 IIS 中部署 ASP.NET 5 应用程序遭遇的问题

    dudu 的一篇博文:在 IIS 中部署 ASP.NET 5 应用程序遭遇的问题 针对 IIS 部署 ASP.NET 5 应用程序的问题,在上面博文中主要采用两种方式尝试: VS2015 的 Publ ...

  4. Windows + IIS 环境部署Asp.Net Core App

    环境:Windows Server 2012, IIS 8, Asp.Net Core 1.1. 不少人第一次在IIS中部署Asp.Net Core App的人都会遇到问题,会发现原来的部署方式无法运 ...

  5. Windows Server 2008 R2 + IIS 环境部署Asp.Net Core App

    Windows + IIS 环境部署Asp.Net Core App   环境:Windows Server 2012, IIS 8, Asp.Net Core 1.1. 不少人第一次在IIS中部署A ...

  6. 在IIS中部署Asp.Net网站

    在IIS中部署Asp.Net网站 1.添加IIS或者删除IIS,在控制面板=>程序和功能=>打开或关闭功能 启动iis,右键计算机=>管理=>服务和应用程序=>Inter ...

  7. 在IIS下部署Thinkphp项目,验证码不能显示的解决办法

    由于公司租用的是虚拟空间,而且用的是IIS服务器,所以部署PHP的时候就出现很多问题:比如昨天就碰到这个问题:在IIS下部署Thinkphp项目,验证码不能显示 这是生成验证码的方法: // 制作专门 ...

  8. Ubuntu 下部署asp.net运行环境

    在Ubuntu下部署asp.net运行环境,网上教程很多,基本都是编译Mono源码,然后安装jexus.但是可能是我最近RP不太好,编译Mono源码一直都是失败,无奈之下只好找另外的方法安装了. 网上 ...

  9. 在IIS下部署SSL证书实现HTTPS

    在IIS下部署SSL证书实现HTTPS   HTTPS是以安全为目标的HTTP通道,简单讲是HTTP的安全版.谷歌已经制定了一项长远的计划,它的最终目标是将所有通过HTTP协议呈现的网页标为“不安全” ...

随机推荐

  1. STL中向量vector笔记

    vector的本质是:数组的封装 特点:读取能在常数时间内完成 Vector成员函数 函数 表述 c.assign(beg,end) c.assign(n,elem) 将[beg; end)区间中的数 ...

  2. 记号(notation)的学习

    数学的记号(notation) 记号具体代表什么含义,取决于你的定义: 比如这样的 d⃗  一个向量,每个分量 d(i) 表示的是从初始结点 v 到当前节点 vi 的最短路径:也即这样的一个向量的每一 ...

  3. 36.Node.js 工具模块--OS模块系统操作

    转自:http://www.runoob.com/nodejs/nodejs-module-system.html Node.js os 模块提供了一些基本的系统操作函数.我们可以通过以下方式引入该模 ...

  4. 15.Node.js REPL(交互式解释器)

    转自:http://www.runoob.com/nodejs/nodejs-tutorial.html Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电 ...

  5. 错误 make: Nothing to be done for 'default'

    Makefile书写格式非常严格,all:<TAB缩进>make -C $(KDIR) M=$(PWD) $(EXTRA_CFLAGS) modulesdefault:<TAB缩进& ...

  6. react radio onchange事件点击无效

    记: 项目需求:   页面中radio默认选中        第一次进去页面   点击radio的时候不管怎样点击    都是选中 连onChange事件都没触发 进入页面  点击刷新   点击rad ...

  7. 20160218.CCPP体系具体解释(0028天)

    程序片段(01):加法.c 内容概要:字符串计算表达式 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <st ...

  8. 图像处理 Mine

    1)中值滤波:排序取中间值.作用:去噪点 1.1)均值滤波; 1.2)高斯模糊:执行高斯模糊,然后改混合模式,改成叠加.柔光或者深色.就能得到平滑而不模糊的效果. 2)腐蚀.膨胀:开运算(腐蚀后膨胀) ...

  9. CODEVS——T 1993 草地排水 USACO

    http://codevs.cn/problem/1993/  时间限制: 2 s  空间限制: 256000 KB  题目等级 : 钻石 Diamond 题解  查看运行结果     题目描述 De ...

  10. JS错误记录 - 按左右箭头div移动、一串div跟着鼠标移动

    本次练习错误总结: 1. div跟着用户操作而移动,首先必须要绝对定位,否则无法移动. 2. if条件语句里面是双等号,不是单等号(赋值). 3. 坐标值没有Right,只能offsetLeft 加减 ...