相信大家都清楚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. jenkins里用ansible发布代码常见的问题

    1.stdout: Neither the JAVA_HOME nor the JRE_HOME environment variable is defined cd bin/vi catalina. ...

  2. eclipse自身导致的项目问题:上边提示需要移除无用包,下边类提示需要导入包。

  3. IndentityServer4

    官网: https://identityserver4.readthedocs.io/en/latest/index.html 比较好的中文博客: 晓晨Master: https://www.cnbl ...

  4. Makefile = ?= := 区别 $@,$^,$<

    = 是最基本的赋值:= 是覆盖之前的值?= 是如果没有被赋值过就赋予等号后面的值+= 是添加等号后面的值 1.“=” make会将整个makefile展开后,再决定变量的值.也就是说,变量的值将会是整 ...

  5. 查看Oracle中存储过程长时间被卡住的原因

    1:查V$DB_OBJECT_CACHE SELECT * FROM V$DB_OBJECT_CACHE WHERE name='CUX_OE_ORDER_RPT_PKG' AND LOCKS!='0 ...

  6. angular.module()

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

  7. vue将指定区域的表格数据或element-ui中el-table的数据单笔或多笔批量导出excel

    公司在后台管理系统开发中用到了 vue+element-ui 组合的框架,但随着需求的越来越复杂,前端的工作难度也呈几何倍数递增,工作量随之增大.这不,在项目中增加一个将列表数据导出为excel的需求 ...

  8. Java EE 导图

  9. jmeter学习笔记(一)

    1.添加JSON Path Extractor >>下载地址:http://jmeter-plugins.org/downloads/all/,下载 JMeterPlugins-Extra ...

  10. 测试连接失败,因为初始化提供程序时发生错误,[DBNMPNTW] ConnectionOpen (CreateFile())

    此主题相关图片如下:错误.jpg 今天发布的程序,在其它电脑上运行没问题,就是其中一台电脑上运程报这个错.系统是Win7的查了好久,最后解决 方法如下: 在报错的电脑上,单击"开始" ...