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调用,性能比较低. 和以前做过测试差异比较大,故而再次测 ...
随机推荐
- 关于db2处理特殊字段出现异常java.io.charConversionException
记录一下以前遇到的问题 在使用db2数据库处理数据的时候,碰到特殊字段,出现的问题 java.io.charConversionException 官方解决方法: db2.jcc.charsetDec ...
- C# 使用默认浏览器打开链接
public void OpenUrl(string link) { RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"http\she ...
- MongoDB with D3.js
MongoDB with D3.js I consider interactive data visualization to be the critical tool for exploration ...
- mysql远程连接/访问速度慢的解决方案
连接阿里云服务器上的数据库,速度很慢 账户密码正确 已关闭防火墙 修改 /etc/my.cnf,添加配置skip-name-resolve [mysqld] skip-name-resolve 在连接 ...
- C++第三课:类的使用(一)[个人见解]
说到C++语言的类,也称对象.在C++中首先得了解的三大特性:继承.封装.多态. 使用C++类,间接的反映出你所学习C++的深度,这章很重要,但小编未必能全部讲到,还望谅解. 类是C++语言中新添加的 ...
- [POJ1961]Period (KMP)
题意 求字符串s的最小循环元长度和循环次数 思路 s[1~i]满足循环元要len能整除i并且s[len+1~i]=s[1~i-len] 代码 #include<cstdio> #inclu ...
- QT—QTextEdit控件显示日志
功能:利用QTextEdit开发一个日志显示窗口.没有太多操作,需要实现的是日志自动向上滚动,总体的日志量可以控制在x行(比如300行)以内:其他的应用功能我后面继续添加 #include <Q ...
- Spring Cloud下微服务权限方案
背景从传统的单体应用转型Spring Cloud的朋友都在问我,Spring Cloud下的微服务权限怎么管?怎么设计比较合理?从大层面讲叫服务权限,往小处拆分,分别为三块:用户认证.用户权限.服务校 ...
- 我们来谈谈最近最热门的微信小程序
最近微信小程序真是火到不行,我们的经理大人也就此给我做了一定的培训.他讲的太好,我实在忍不住跟大家简单分享一下: 1.什么是微信小程序? 一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦 ...
- 快递100码json
{ "AOL澳通速递": "aolau", "A2U速递": "a2u", "AAE快递": &qu ...