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中引用它。

关键字:

50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信