.NET Core 3.0之深入源码理解Kestrel的集成与应用(一)
写在前面
ASP.NET Core 的 Web 服务器默认采用Kestrel,这是一个跨平台、轻量级的Web服务器。
在开始之前,先回顾一下.NET Core 3.0默认的main()方法模板中,我们会调用Host.CreateDefaultBuilder方法,该方法的主要功能是配置应用主机及设置主机的属性,设置Kestrel 服务器配置为 Web 服务器,另外还包括日志功能、应用配置加载等等,此处不做展开。
作为一个轻量级的Web Server,它并没有IIS、Apache那些大而全的功能,但它依然可以单独运行,也可以搭配IIS、Apache等反向代理服务器结合使用。
本文将从源码角度讨论ASP.NET Core应用在Kestrel的相关知识点。
Kestrel
Kestrel的存在意义
了解这个问题,首先需要强调的是.NET Core应用的目标就是跨平台,既然要跨平台那么就需要适用各个平台上的Web服务器,各个服务器的启动、配置等等都是不尽相同的,如果每个服务器提供一套实现出来,如果未来出现了一个新的Web Server,然后又要增加新的实现,这会导致.NET Core应用的适用性滞后,也会很消耗人力,无法很好的达到跨平台的目标。
我们可以把Kestrel视作一个中间件,一个适配的功能,它抽象了各个服务器的特性,使得各个应用只需要调用同样的接口,即可最大限度的在各个平台上运行。
运行方式
.NET Core 3.0下,Kestrel的集成已经相当成熟了,也提供了相应的自定义配置,以使得Kestrel的使用更加具有灵活性和可配性。它可以独立运行,也可以与反向代理服务器结合使用。
Kestrel本身是不支持多个应用共享同一个端口的,但是我们可以通过反向代理服务器来实现统一对外的相同的端口的共享。
以下是其单独运行示意图:
以下是其结合反向代理使用示意图:
Microsoft.AspNetCore.Server.Kestrel.Core
该类库是Kestrel的核心类库,里面包含了该功能的多个逻辑实现,以下简称改类库为Kestrel.Core。
Kestrel适配逻辑
如前文所说,Kestrel起到了抽象服务器的功能,那么在适配其他服务器的过程中,必然涉及到的是,输入、输出、数据交互方式以及Trace功能。在Kestrel.Core中,该功能主要由AdaptedPipeline类来实现,该类继承自IDuplexPipe,并通过构造函数获取到了Pipe对象。IDuplexPipe和Pipe均位于System.IO.Pipelines命名空间下,详细信息可以点击查看。
AdaptedPipeline有两个公共方法:
RunAsync():用于读取(读取后会有Flush操作)和写入数据,并分别装载到Task中
CompleteAsync():完成读取和写入操作,并取消基础流的读取
另外还包括四个公共属性,如下所示:
- 1: public RawStream TransportStream { get; }
- 2:
- 3: public Pipe Input { get; }
- 4:
- 5: public Pipe Output { get; }
- 6:
- 7: public IKestrelTrace Log { get; }
它定义了可从中读取并写入数据的双工管道的对象。IDuplexPipe有两个属性,System.IO.Pipelines.PipeReader Input { get; }和System.IO.Pipelines.PipeReader Output { get; }。AdaptedPipeline还通过构造函数获取到了Pipe对象。
RawStream类继承自Stream,并重写了Stream的关键属性及方法,主要目标是提供适合于Kestrel读写数据方式的内部封装。
LoggingStream类也同样继承自Stream,和RawStream不同的是,里面增加操作过程的日志记录,主要用于记录在连接适配过程中的信息,不过需要启用日志才能把日志信息记录下来,以下是其对外的使用方式:
- 1: public static class ListenOptionsConnectionLoggingExtensions
- 2: {
- 3: /// <summary>
- 4: /// Emits verbose logs for bytes read from and written to the connection.
- 5: /// </summary>
- 6: /// <returns>
- 7: /// The <see cref="ListenOptions"/>.
- 8: /// </returns>
- 9: public static ListenOptions UseConnectionLogging(this ListenOptions listenOptions)
- 10: {
- 11: return listenOptions.UseConnectionLogging(loggerName: null);
- 12: }
- 13:
- 14: /// <summary>
- 15: /// Emits verbose logs for bytes read from and written to the connection.
- 16: /// </summary>
- 17: /// <returns>
- 18: /// The <see cref="ListenOptions"/>.
- 19: /// </returns>
- 20: public static ListenOptions UseConnectionLogging(this ListenOptions listenOptions, string loggerName)
- 21: {
- 22: var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<ILoggerFactory>();
- 23: var logger = loggerName == null ? loggerFactory.CreateLogger<LoggingConnectionAdapter>() : loggerFactory.CreateLogger(loggerName);
- 24: listenOptions.ConnectionAdapters.Add(new LoggingConnectionAdapter(logger));
- 25: return listenOptions;
- 26: }
- 27: }
Kestrel特性抽象
该模块下的 Kestrel特性,比较重要的有连接超时设置(包括设置超时时间、重置超时时间以及取消超时限制。这个特性使得我们的连接变得更加可控,比如,在某些特殊场景下,特殊条件下,我们需要取消超时限制或者动态重置超时时间),TLS应用程序协议功能,基于Http2.0的StreamId记录功能,用于停止连接计数的功能。
以下是连接超时接口的源代码:
- 1: /// <summary>
- 2: /// Feature for efficiently handling connection timeouts.
- 3: /// </summary>
- 4: public interface IConnectionTimeoutFeature
- 5: {
- 6: /// <summary>
- 7: /// Close the connection after the specified positive finite <see cref="TimeSpan"/>
- 8: /// unless the timeout is canceled or reset. This will fail if there is an ongoing timeout.
- 9: /// </summary>
- 10: void SetTimeout(TimeSpan timeSpan);
- 11:
- 12: /// <summary>
- 13: /// Close the connection after the specified positive finite <see cref="TimeSpan"/>
- 14: /// unless the timeout is canceled or reset. This will cancel any ongoing timeouts.
- 15: /// </summary>
- 16: void ResetTimeout(TimeSpan timeSpan);
- 17:
- 18: /// <summary>
- 19: /// Prevent the connection from closing after a timeout specified by <see cref="SetTimeout(TimeSpan)"/>
- 20: /// or <see cref="ResetTimeout(TimeSpan)"/>.
- 21: /// </summary>
- 22: void CancelTimeout();
- 23: }
Kestrel选项及限制功能
Kestrel的选项控制包括监听、Kestrel服务器、HTTPS连接适配。
1、监听选项功能在ListenOptions中实现,该类继承自IConnectionBuilder,ListenOptions的主要作用是描述Kestrel中已经打开的套接字,包括Unix域套接字路径、文件描述符、ipendpoint。ListenOptions内部会维护一个只读的List<Func<ConnectionDelegate, ConnectionDelegate>>()对象,并通过Use()方法加载新的Func<ConnectionDelegate, ConnectionDelegate>对象,然后通过Build方式返回最后加入的Func<ConnectionDelegate, ConnectionDelegate对象,源码如下所示:
- 1: public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware)
- 2: {
- 3: _middleware.Add(middleware);
- 4: return this;
- 5: }
- 6:
- 7: public ConnectionDelegate Build()
- 8: {
- 9: ConnectionDelegate app = context =>
- 10: {
- 11: return Task.CompletedTask;
- 12: };
- 13:
- 14: for (int i = _middleware.Count - 1; i >= 0; i--)
- 15: {
- 16: var component = _middleware[i];
- 17: app = component(app);
- 18: }
- 19:
- 20: return app;
- 21: }
需要注意的是ListenOptions在该类库内部还有两个子类,AnyIPListenOptions和LocalhostListenOptions,以用于特定场景的监听使用。
2、Kestrel服务器选项是在KestrelServerOptions中实现的,该类用于提供Kestrel特定功能的编程级别配置,该类内部会维护ListenOptions的列表对象,该类将ListenOptions的功能进一步展开,并加入了HTTPS、证书的默认配置与应用,这个类比较大,本文就不贴出源码了,有兴趣的同学可以自己去翻阅。
3、HTTPS连接适配选项在HttpsConnectionAdapterOptions实现,这个类用于设置Kestrel如何处理HTTPS连接,这里引入和证书功能、SSL协议、HTTP协议、超时功能,同时这里还可以自定义HTTPS连接的时候的证书处理模式(AllowCertificate、RequireCertificate等),以下是HttpsConnectionAdapterOptions的构造函数:
- 1: public HttpsConnectionAdapterOptions()
- 2: {
- 3: ClientCertificateMode = ClientCertificateMode.NoCertificate;
- 4: SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
- 5: HandshakeTimeout = TimeSpan.FromSeconds(10);
- 6: }
可以看到,在默认情况下,是无证书模式,其SSL协议包括Tls12 和Tls11以及指定允许进行TLS/SSL握手的最大时间是十秒钟。
4、Kestrel的限制功能在KestrelServerLimits实现,主要包括:
- 保持活动状态超时
- 客户端最大连接数(默认情况下,最大连接数不受限制 (NULL))
- 请求正文最大大小(默认的请求正文最大大小为 30,000,000 字节,大约 28.6 MB)
- 请求正文最小数据速率(默认的最小速率为 240 字节/秒,包含 5 秒的宽限期)
- 请求标头超时(默认值为 30 秒)
- 每个连接的最大流(默认值为 100)
- 标题表大小(默认值为 4096)
- 最大帧大小(默认值为 2^14)
- 最大请求标头大小(默认值为 8,192)
- 初始连接窗口大小(默认值为 128 KB)
- 初始流窗口大小(默认值为 96 KB)
代码如下所示:
- 1: .ConfigureKestrel((context, options) =>
- 2: {
- 3: options.Limits.MaxConcurrentConnections = 100;
- 4: options.Limits.MaxConcurrentUpgradedConnections = 100;
- 5: options.Limits.MaxRequestBodySize = 10 * 1024;
- 6: options.Limits.MinRequestBodyDataRate =
- 7: new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
- 8: options.Limits.MinResponseDataRate =
- 9: new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
- 10: options.Listen(IPAddress.Loopback, 5000);
- 11: options.Listen(IPAddress.Loopback, 5001, listenOptions =>
- 12: {
- 13: listenOptions.UseHttps("testCert.pfx", "testPassword");
- 14: });
- 15: options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
- 16: options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
- 17: options.Limits.Http2.MaxStreamsPerConnection = 100;
- 18: options.Limits.Http2.HeaderTableSize = 4096;
- 19: options.Limits.Http2.MaxFrameSize = 16384;
- 20: options.Limits.Http2.MaxRequestHeaderFieldSize = 8192;
- 21: options.Limits.Http2.InitialConnectionWindowSize = 131072;
- 22: options.Limits.Http2.InitialStreamWindowSize = 98304;
- 23: });
- 1: // Matches the non-configurable default response buffer size for Kestrel in 1.0.0
- 2: private long? _maxResponseBufferSize = 64 * 1024;
- 3:
- 4: // Matches the default client_max_body_size in nginx.
- 5: // Also large enough that most requests should be under the limit.
- 6: private long? _maxRequestBufferSize = 1024 * 1024;
- 7:
- 8: // Matches the default large_client_header_buffers in nginx.
- 9: private int _maxRequestLineSize = 8 * 1024;
- 10:
- 11: // Matches the default large_client_header_buffers in nginx.
- 12: private int _maxRequestHeadersTotalSize = 32 * 1024;
- 13:
- 14: // Matches the default maxAllowedContentLength in IIS (~28.6 MB)
- 15: // https://www.iis.net/configreference/system.webserver/security/requestfiltering/requestlimits#005
- 16: private long? _maxRequestBodySize = 30000000;
- 17:
- 18: // Matches the default LimitRequestFields in Apache httpd.
- 19: private int _maxRequestHeaderCount = 100;
- 20:
- 21: // Matches the default http.sys connectionTimeout.
- 22: private TimeSpan _keepAliveTimeout = TimeSpan.FromMinutes(2);
- 23:
- 24: private TimeSpan _requestHeadersTimeout = TimeSpan.FromSeconds(30);
- 25:
- 26: // Unlimited connections are allowed by default.
- 27: private long? _maxConcurrentConnections = null;
- 28: private long? _maxConcurrentUpgradedConnections = null;
.NET Core 3.0之深入源码理解Kestrel的集成与应用(一)的更多相关文章
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)
前言 前一篇文章主要介绍了.NET Core继承Kestrel的目的.运行方式以及相关的使用,接下来将进一步从源码角度探讨.NET Core 3.0中关于Kestrel的其他内容,该部分内容,我们 ...
- .NET Core 3.0之深入源码理解Startup的注册及运行
原文:.NET Core 3.0之深入源码理解Startup的注册及运行 写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...
- .NET Core 3.0之深入源码理解Configuration(一)
Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并 ...
- .NET Core 3.0之深入源码理解HttpClientFactory(二)
写在前面 上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是 ...
- .NET Core 3.0之深入源码理解Host(二)
写在前面 停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了.本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Pro ...
- .NET Core 3.0之深入源码理解ObjectPool(一)
写在前面 对象池是一种比较常用的提高系统性能的软件设计模式,它维护了一系列相关对象列表的容器对象,这些对象可以随时重复使用,对象池节省了频繁创建对象的开销. 它使用取用/归还的操作模式,并重复执行这些 ...
- .NET Core 3.0之深入源码理解HealthCheck(一)
写在前面 我们的系统可能因为正在部署.服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态.一般情况下,我们会提供公开 ...
- .NET Core 3.0之深入源码理解Host(一)
写在前面 ASP .NET Core中的通用主机构建器是在v2.1中引入的,应用在启动时构建主机,主机作为一个对象用于封装应用资源以及应用程序启动和生存期管理.其主要功能包括配置初始化(包括加载配置以 ...
- .NET Core 3.0之深入源码理解Configuration(三)
写在前面 上一篇文章讨论了文件型配置的基本内容,本篇内容讨论JSON型配置的实现方式,理解了这一种配置类型的实现方式,那么其他类型的配置实现方式基本可以触类旁通.看过了上一篇文章的朋友,应该看得出 ...
随机推荐
- message contains no documents code:13066 mongdb数据库报的错误
message contains no documents code:13066stackoverflow上面的回答是: What version of the C# driver are you ...
- google地图API的简单使用
<div id="contact_container" style="width:700px;height:600px;"></div> ...
- 一篇简单易懂的原理文章,让你把JVM玩弄与手掌之中
jvm原理 Java虚拟机是整个java平台的基石,是java技术实现硬件无关和操作系统无关的关键环节,是java语言生成极小体积的编译代码的运行平台,是保护用户机器免受恶意代码侵袭的保护屏障.JVM ...
- 接口测试——fiddler对soapui请求返回信息抓取
原文:接口测试——fiddler对soapui请求返回信息抓取 背景:接口测试的时候,需要对接口的请求和返回信息进行查阅或者修改请求信息,可利用fiddler抓包工具对soapui的请求数据进行抓取或 ...
- Cocos2d-x layout (两)
相对于对照布局 Size widgetSize = Director::getInstance()->getWinSize(); Text* alert = Text::create(" ...
- 键盘各键对应的编码值(key code)
原文:键盘各键对应的编码值(key code) 来源:http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes ...
- Java数组List换算方法
1.List转换到一个数组.(这里List它是实体是ArrayList) 调用ArrayList的toArray方法. toArray public <T> T[] toArray(T[] ...
- js 小野人跟着鼠标移动
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...
- liunx 查看php 安装的扩展
/usr/local/php5/bin/php -i |less 查看配置文件在哪里,编译参数 /usr/local/php5/bin/php -m |less 查看php加载的模块
- Linux下C语言RPC(远程过程调用)编程实例
在查看libc6-dev软件包提供的工具(用 dpkg -L libc6-dev 命令)的时候,发现此软件包提供了一个有用的工具rpcgen命令.通过rpcgen的man手册看到此工具的作用是把RPC ...