相信大家都清楚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分析与使用的更多相关文章

  1. golang http/transport 代码分析

    请结合源码阅读,本文只是总结一下,源码里有详细的注释.基于:go1.12.4 http.Client 表示一个http client端,用来处理HTTP相关的工作,例如cookies, redirec ...

  2. netcore高性能Web服务器Kestrel分析

    Kestrel是aspnetcore中的web服务器之一,其本身有跨平台,轻量级,高性能的特点 在 ryzen 1600 12核cpu 测试环境中,瞬间每秒处理请求数能达到2w5以上,与netty不相 ...

  3. 利用Windbg分析Magicodes.IE一次错误编写导致内存剧增

    由于这近一年时间一直忙于写书和工作,一直没有水文,但是近期有几位朋友使用我们的Magicodes.IE反馈在导出过程中内存暴涨...好吧,不管怎样,不能苦了我们朋友,接下来我们通过windbg来看一下 ...

  4. 解决ASP.NET Core MVC调试慢的问题

    最近在做的一个项目中,发现网页端同时发起了4个AJAX请求需要数秒才获取到响应,太慢了,当我把请求数降低为1个的时候,速度看起来就比较正常,增加到2个同时的请求后,速度就有些慢了,3个的话就明显慢了, ...

  5. 服务测试碰钉子Server GC

    如果发现你的dotnet core服务并发上不去,但cpu资源还比较充足那就要注意了!因为这很有可能是你没有设置一个运行项导致...,下面要提到的就是GC.Server这玩意,实际上项目编译中并没有这 ...

  6. ASP.NET Core MVC内置服务的使用

    ASP.NET Core中的依赖注入可以说是无处不在,其通过创建一个ServiceCollection对象并将服务注册信息以ServiceDescriptor对象的形式添加在其中,其次针对Servic ...

  7. .net core 拦截socket

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net ...

  8. Kestrel web server implementation in ASP.NET Core

    https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?tabs=aspnetcore1x&view ...

  9. dotnetcore http服务器研究(二)性能分析

    Asp.net core kestrel 服务器性能分析 因近来发现neocli 使用asp.net core kestrel 服务器提供rpc调用,性能比较低. 和以前做过测试差异比较大,故而再次测 ...

随机推荐

  1. linux中sogou输入法崩溃重启

    经常在linux中搜狗输入法用着用着就崩溃了,无法输入中文,又不想重启电脑,照着下面在终端输入命令可以重启输入法: 1.先关闭fcitx(小企鹅输入法,提供了良好的中文输入法环境) # killall ...

  2. CentOS7下使用yum安装MariaDB

    从CentOS 7开始,使用 MariaDB 替代默认的 MySQL.MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MyS ...

  3. hdu1201 java

    题意: 求某人从出生到18岁生日所经过的天数.如果这个人没有18岁生日,就输出-1. 思路: 通过毫秒值计算天数. 利用:来自https://www.cnblogs.com/xiohao/p/5294 ...

  4. vue解决加载闪烁问题

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>& ...

  5. 2018面向对象程序设计(java)课程学习进度条

    周次 (阅读/编写)代码行数 发布博文量/评论他人博文数量 课余学习时间 学习收获的最大程序阅读或编程任务 1 30-50 1/0 5 九九乘法表 2 60-80 1/0 6 实验一,实验二 3 12 ...

  6. MD5加密过时方法替换

    使用System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile进行MD5加密时,会出现已过时 /// < ...

  7. [Codeforces Round #516][Codeforces 1063C/1064E. Dwarves, Hats and Extrasensory Abilities]

    题目链接:1063C - Dwarves, Hats and Extrasensory Abilities/1064E - Dwarves, Hats and Extrasensory Abiliti ...

  8. web移动端开发技巧

    一.meta的使用 1.<meta name="viewport" content="width=device-width,initial-scale=1.0, m ...

  9. webpack配置非CMD规范的模块

    一.前言 webpack在配置多页面开发的时候 ,发现用 import 导入 Zepto 时,会报 Uncaught TypeError: Cannot read property 'createEl ...

  10. jsp中input获得后台传递的值

    1.后台赋值 req.setAttribute("openId",openId); 2.前台获取值 value="<%= request.getAttribute( ...