Kestrel.Transport.Sockets分析与使用
相信大家都清楚asp core有着非常出色的性能,它出色的性能也源于网络服务模块Kestrel;在techempower测试中Kestrel基础核心达到了700万级别的RPS吞吐能力,具备这样的能力那对应的Kestrel.Transport.Sockets也应有着不错的性能。接下来简单地分析一下Kestrel.Transport.Sockets的设计和使用,并进行简单的并发处理能力测试。
async/await
async/await的使用这几年时间里大放异彩,现有新功能的IO操作方式无一不支持它,毕竟可以同步的代码方式来实现异步处理功能,不管是开发,调试还是维护都带来的极大的便利性;既然这样Kestrel.Transport.Sockets也在基础的socket异步基础功能上引入了async/await设计,大大简化了上层应用编写的复杂度;下面看一下针对SocketAsyncEventArgs封装的Awaitable。
public class SocketAwaitableEventArgs : SocketAsyncEventArgs, ICriticalNotifyCompletion
{
private static readonly Action _callbackCompleted = () => { }; private readonly PipeScheduler _ioScheduler; private Action _callback; public SocketAwaitableEventArgs(PipeScheduler ioScheduler)
{
_ioScheduler = ioScheduler;
} public SocketAwaitableEventArgs GetAwaiter() => this;
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted); public int GetResult()
{
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted)); _callback = null; if (SocketError != SocketError.Success)
{
ThrowSocketException(SocketError);
} return BytesTransferred; void ThrowSocketException(SocketError e)
{
throw new SocketException((int)e);
}
} public void OnCompleted(Action continuation)
{
if (ReferenceEquals(_callback, _callbackCompleted) ||
ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
{
Task.Run(continuation);
}
} public void UnsafeOnCompleted(Action continuation)
{
OnCompleted(continuation);
} public void Complete()
{
OnCompleted(this);
} protected override void OnCompleted(SocketAsyncEventArgs _)
{
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted); if (continuation != null)
{
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
}
}
}
这个Awaitable的设计得非常好,它没没有引用新的对象,而是直接在SocketAsyncEventArgs的基础派生下来实现,这样在高并吞吐的情况可以更好地降低新对象的开销;这即能使用await的同时也无需增加对象的开销,不过PipeScheduler的调用设计竟然使用了匿名函数的方式带入,这样会增加了对象的开销;还有就是SocketAsyncEventArgs完成后还投递给一个线程调度去完成后面的工作,如果协议分析的工作量不大的情况个人感觉这个处理有些重了,不过使用都可以实现自己的PipeScheduler或直接改成执行continuation,最好是根据情况来配置最佳。
引入System.IO.Pipelines
在之前的文章已经说过Pipe,它是一个Buffer读写对象,其重要作用是可以把不连续的数据内存块连接起来处理起来,这样可以使普通开发人员避开Buffer的创建和回收的繁琐工作(毕竟这一块工作要做好还是有点难度的)。Pipe不紧紧提供了不连续数据Buffer的读写,它还具备一套await状态机制可以让使用人员从socket的receive和send工作分离出来。每个连接会分配两个Pipe对象,主要负责Socket的receive和send工作;其工作原理如下:

基于Pipe使用者只需要关心应用协议处理处理即可,而这个处理会变得非常简单;只需要关注Pipe的Writer和Reader即可。虽然这样做带来了便利性,但经过Pipe多了两层状态通讯多多少少会有性能上的影响,但这些影响相对Buffer开销,GC和处理来说则还是有比较好的回报的。这里还是要重吐嘲一下MS,为什么Writer和Reader不按BinaryReader和BinaryWriter的基准作为设计,其实Pipe对普通使用者来说还是不怎友好的!
使用
Kestrel.Transport.Sockets的使用还真有点让人头痛,首先它没有完善的文档,还有设计集成度也比较高。要搞清楚怎么用对于新手来说还真不怎容易,出于研究它的设计和对比查看了一段时间源码才总结出来如何用;最终发现要用得好还需真要再做一层封装才能更好的用于实限应用中;下面讲解一下如何简单地使用它吧,首先你要在Nuget中引用它。
构建
Kestrel.Transport.Sockets的使用入口是SocketTransportFactory,只要能构建这个对象那接下工作就简单很多,首先看一下这个对象的构造函数
public SocketTransportFactory(IOptions<SocketTransportOptions> options, IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory);
三个参数都是接口……没有文档的情况还真有点头痛。ILoggerFactory引用Microsoft.Extensions.Logging可以得到,剩下两个简单地实现一下即可。
IOptions<SocketTransportOptions>
public class SocketOpetion : IOptions<SocketTransportOptions>
{
public SocketTransportOptions Value => new SocketTransportOptions();
}
IApplicationLifetime
public class ApplicationLifetime : IApplicationLifetime
{
public ApplicationLifetime() : this(new CancellationToken(), new CancellationToken(), new CancellationToken())
{ }
public ApplicationLifetime(CancellationToken started, CancellationToken stopping, CancellationToken stoped)
{ ApplicationStarted = started;
ApplicationStopping = stopping;
ApplicationStopped = stoped;
}
public CancellationToken ApplicationStarted { get; set; } public CancellationToken ApplicationStopping { get; set; } public CancellationToken ApplicationStopped { get; set; } public virtual void StopApplication()
{ }
}
创建服务
以上接口的实现都有了,接下来就可以创建SocketTransportFactory对象了
private static async void ListenSocket(int prot)
{
var loggerFactory = new LoggerFactory();
ApplicationLifetime applicationLifetime = new ApplicationLifetime();
var server = new SocketTransportFactory(new SocketOpetion(), applicationLifetime, loggerFactory);
await server.Create(new AnyEndPointInformation(prot), new Program()).BindAsync();
}
同样SocketTransportFactory的Create方法也需要两个接口参数,一个是监听类型和地址描述,一个连接调度器。这里只需要IP端口监听所以实现起来比较简单:
public class AnyEndPointInformation : IEndPointInformation
{
public AnyEndPointInformation(int port)
{
IPEndPoint = new IPEndPoint(IPAddress.Any, port);
} public ListenType Type => ListenType.IPEndPoint; public IPEndPoint IPEndPoint { get; set; } public string SocketPath => throw new NotImplementedException(); public ulong FileHandle => throw new NotImplementedException(); public FileHandleType HandleType { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public bool NoDelay => true;
}
接下来的工作就在IConnectionDispatcher接口的OnConnection方法下处理连接
public void OnConnection(TransportConnection connection)
{
Session session = new Session(connection);
Task.Run(session.StartRecive);
}
刚开始以为有了TransportConnection就可以进行数据接收和发送,但事情是我想得太简单了!其实TransportConnection并不具备数据处理能力,因为里面两路的Pipe是空的……使用者需要自己定义对应的Pipe并设置给它,以上代码的Session是需要自己实现的,名称随自己喜欢定义;实现接口IDuplexPipe,设置两路的Pipe对象,然后设置到TransportConnection.Application属性上。实现IDuplexPipe后就可以进行数据接收和发送功能了,以下是实现了一个简单的StartRecive后回发数据,有收有发才便于下面测试的工作。
public async Task StartRecive()
{
while (true)
{
var data = await Receiver.ReadAsync();
if (data.IsCompleted)
{
this.Dispose();
break;
}
var buffers = data.Buffer;
var end = buffers.End;
if (buffers.IsSingleSegment)
{
ReadOnlyMemory<byte> b = buffers.First;
var sbuf = Sender.GetMemory(b.Length);
b.CopyTo(sbuf);
Sender.Advance(b.Length);
}
else
{
foreach (var b in buffers)
{
var sbuf = Sender.GetMemory(b.Length);
b.CopyTo(sbuf);
Sender.Advance(b.Length);
}
}
var flush = await Sender.FlushAsync();
Receiver.AdvanceTo(end);
}
}
测试
既然研究它自然就会关心它的性能情况,针对以上最简单接收后返回的功能进行了一个压力测试。测试结果总体上来说还算不错,但算不上非常出色;最终测结果在一台E3 1230V2的PC机上测试结果是:10000连接,接近20万rps。

Kestrel.Transport.Sockets分析与使用的更多相关文章
- golang http/transport 代码分析
请结合源码阅读,本文只是总结一下,源码里有详细的注释.基于:go1.12.4 http.Client 表示一个http client端,用来处理HTTP相关的工作,例如cookies, redirec ...
- netcore高性能Web服务器Kestrel分析
Kestrel是aspnetcore中的web服务器之一,其本身有跨平台,轻量级,高性能的特点 在 ryzen 1600 12核cpu 测试环境中,瞬间每秒处理请求数能达到2w5以上,与netty不相 ...
- 利用Windbg分析Magicodes.IE一次错误编写导致内存剧增
由于这近一年时间一直忙于写书和工作,一直没有水文,但是近期有几位朋友使用我们的Magicodes.IE反馈在导出过程中内存暴涨...好吧,不管怎样,不能苦了我们朋友,接下来我们通过windbg来看一下 ...
- 解决ASP.NET Core MVC调试慢的问题
最近在做的一个项目中,发现网页端同时发起了4个AJAX请求需要数秒才获取到响应,太慢了,当我把请求数降低为1个的时候,速度看起来就比较正常,增加到2个同时的请求后,速度就有些慢了,3个的话就明显慢了, ...
- 服务测试碰钉子Server GC
如果发现你的dotnet core服务并发上不去,但cpu资源还比较充足那就要注意了!因为这很有可能是你没有设置一个运行项导致...,下面要提到的就是GC.Server这玩意,实际上项目编译中并没有这 ...
- ASP.NET Core MVC内置服务的使用
ASP.NET Core中的依赖注入可以说是无处不在,其通过创建一个ServiceCollection对象并将服务注册信息以ServiceDescriptor对象的形式添加在其中,其次针对Servic ...
- .net core 拦截socket
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net ...
- Kestrel web server implementation in ASP.NET Core
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?tabs=aspnetcore1x&view ...
- dotnetcore http服务器研究(二)性能分析
Asp.net core kestrel 服务器性能分析 因近来发现neocli 使用asp.net core kestrel 服务器提供rpc调用,性能比较低. 和以前做过测试差异比较大,故而再次测 ...
随机推荐
- 在Github上为项目添加多个用户
点击项目目录中的Settings 点击Collaborators 添加后,合作者会受到确认邮件,等待合作者确认后,合作者就可以提交了 添加组织 在Settings中找到Organizations 点击 ...
- Hive+Sqoop+Mysql整合
Hive+Sqoop+Mysql整合 在本文中,LZ随意想到了一个场景: 车,道路,监控,摄像头 即当一辆车在道路上面行驶的时候,道路上面的监控点里面的摄像头就会对车进行数据采集. 我们对采集的数据进 ...
- Linux 搭建Nginx+uWSGI+Django环境
安装环境 sudo apt-get install nginx sudo apt install python3 sudo apt install python3-pip 使用 sudo pip3 i ...
- aarch64的架构:unrecognized command line option '-mfpu=neon'
不用添加这个'-mfpu=neon'的编译器选项了,因为这个架构下neon是默认启动的. 参考: https://lists.linaro.org/pipermail/linaro-toolchain ...
- LoadRunner(二)——性能测试过程概述
参考学习感谢:<精通软件性能测试与LoadRunner实战> 性能测试过程概述 2.1 性能测试的基本过程 2.2 性能测试需求分析 2.3 性能测试计划 2.4 性能测试用例 2.5 测 ...
- 服务器Windows 登录 出现401 错误
Method 1: Disable the loopback checkThe first method is to disable the loopback check by setting th ...
- Data Center手册(1):架构
如图是数据中心的一个基本架构 最上层是Internet Edge,也叫Edge Router,也叫Border Router,它提供数据中心与Internet的连接. 连接多个网络供应商来提供冗余可靠 ...
- Python学习:类和实例
Python学习:类和实例 本文作者: 玄魂工作室--热热的蚂蚁 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规 ...
- [AI开发]Python+Tensorflow打造自己的计算机视觉API服务
"与其停留在概念理论层面,不如动手去实现一个简单demo ." ——鲁迅 没有源码都是耍流氓github 前言 目前提供AI开发相关API接口的公司有很多,国外如微软. ...
- JAVA小白开发环境配置(编译器为Idea)
JDK配置 1.首先到官网下载最新版JDK:Oracle官网下载 Accept License Agreement–>下载适合自己pc版本的jdk(此处以64位windows为例.x86是32位 ...