目录

背景

介绍

Redis协议

软件设计


  • 下载最新的存储库存档

本文是关于为Redis服务器创建异步客户端的系列文章中的第一篇,该客户端低分配,因此GC压力小,数据复制最少。这是使用技术完成的,这些技术使Kestrel成为TechEmpower 纯文本性能测试第13轮中记录的每秒原始请求数排名前十的Web服务器之一。

背景

前段时间,我开始编写一个异步的.NET Core Redis客户端。当时没有一个Redis客户端支持.NET Core,我想写一篇关于如何为一个简单的协议实现一个客户端的文章。

不幸的是,VS2015 RC1和RC2的变化表明该平台将在一段时间内不稳定,虽然我有一个相当完整的实现,但我把它放在架子上,直到.NET和Visual Studio世界变得更加稳定。

随着即将发布的VS2019、.NET 3.0以及CLI、NetStandard和工具的稳定,我认为是时候重新审视这个项目了。最让我对.NET Core产生兴趣的一件事是性能提升了多少,尤其是在Kestrel Web服务器性能方面。

.NET Core团队,尤其是David Fowler,利用他们学到的知识改进Kestrel,并创建了一组库,允许以很少或没有内存分配和最少数据复制的方式处理数据流。这是通过反转现有的 Stream范式来完成的,这样数据缓冲区就不是将数据缓冲区推入和拉出流,而是由低级 API管理并推送到应用程序。这些使用高效的内存缓冲池和结构来实现性能,使Kestrel成为可用的最快的Web服务器之一。

话虽如此,似乎Kestrel现在使用System.IO.Pipelines NuGet包,并且它也在SignalR 中使用。作为Kestrel项目的一部分,创建了许多基于低级流水线的库,以实现低分配、高性能的网络IO,以取代基于流的IO。有一个基于Socket的IO的实现。这可以在Nuget.org 的Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets上找到。

介绍

几年前,我们查看了我们的网页响应时间的性能,发现它严重不足。对于每个请求,我们都会对常见请求的数据进行数据库请求,并对内容进行复杂且昂贵的清理和格式化。

我们开始了一个项目,使用缓存各种信息和视图模型来提高站点的性能。这种缓存需要分布式,以便我们网络场中的所有服务器都与最新数据保持一致和最新。在评估了几个选项后,我们决定使用Redis,因为它的速度、成本、广泛采用、好评,以及其数据结构和API的强大功能。

由此产生的性能提升超出了我们的预期,需要几秒甚至几十秒的页面在不到一秒的时间内返回,通常不到500毫秒,大大降低了我们SQL Server的CPU负载。通过添加后台事件处理和优化一些昂贵且大量使用的算法,已经实现了进一步的性能改进,但我怀疑我们能做的任何事情都不能产生我们使用Redis获得的改进。

我们当前的实现使用ServiceStack Redis Client V3。我们也有一个使用StackExchange.Redis客户端的实现,但我也遇到了一些问题。我不得不研究代码以解决许多问题,并且像任何程序员一样,决定我可以做得更好,或者至少是不同的。这主要是由于C#语言的改进,例如扩展方法。这允许我创建一个小客户端,它只是向Redis服务器发送和从Redis服务器接收内容。实际的命令是使用扩展方法实现的。这消除了服务堆栈实现中的巨大类和StackExchange实现中的一些代码重复,允许每个类具有更大的单一职责。

这两个库中有许多很棒的想法,例如StackExchange客户端中的ConnectionMultiplexer 允许共享单个套接字,而不必在每次需要访问Redis服务器时创建新的套接字连接。将在本系列文章的后面部分实现这一点。

此实现的目标是

  • 简单
  • 性能
  • 效率
  • 稳健性
  • 完成单元测试

Redis协议

客户端使用Redis序列化协议 (RESP) 与Redis服务器通信,详见Redis 协议规范。正如规范所述:

Redis客户端使用名为RESP的协议与Redis服务器通信。(Redis序列化协议)。虽然该协议是专为Redis设计的,但它也可用于其他客户端-服务器软件项目。

RESP是以下各项之间的折衷:

  • 实现简单。
  • 解析速度快。
  • 人类可读。

RESP可以序列化不同的数据类型,如整数、字符串、数组。还有一种特定的错误类型。请求以字符串数组的形式从客户端发送到Redis服务器,这些字符串表示要执行的命令的参数。Redis使用特定于命令的数据类型进行回复。

RESP是二进制安全的,不需要处理从一个进程传输到另一个进程的批量数据,因为它使用前缀长度来传输批量数据。

如果您需要澄清有关我正在做什么的任何事情,我将把它留给读者参考规范,而不是详细介绍协议。它小巧、简单且易于理解。当我解释使用它们的代码时,我会解释具体的协议细节。

软件设计

基于管道的套接字传输的神奇之处在于它为一对管道公开了PipeReaders和PipeWriters。一个管道(OutputPipe)将数据从应用程序传输到传输程序,而另一个管道(InputPipe)将数据从传输程序传输到应用程序。

Connection公开了一个IDuplexPipe Application,它有一个Input PipeReader和一个Output PipeWriter。Input设置为InputPipe.Reader,而Output设置为OutputPipe.Writer。该连接有两个任务,一个从Socket读取数据并将其写入InputPipe,第二个任务从OutputPipe读取数据并将其写入Socket。

管道使用一组内存块来提供和重用缓冲区来存储数据。这与Streams范式不同,在这种范式中,用户负责分配和管理用于读取和写入Stream的数据缓冲区。结果是管道传输需要很少或不需要缓冲区分配和垃圾收集来从Socket读取和写入。事实上,在大多数情况下,很少需要将数据从一个缓冲区复制到另一个缓冲区,直到需要进行这种复制才能从接收到的数据中反序列化某个对象。

这意味着我们的Redis协议处理程序需要做两件事:

  • 将Redis命令序列化为写入PipeWriter的字节
  • 从PipeWReader读取字节并将它们反序列化为Redis响应

因此,从两个管道创建测试传输层很简单。被测代码连接到应用程序端,测试读取和写入传输端,允许测试预期的功能,而实际上不需要为单元测试设置Redis实例。

当然,在某些时候需要与真实的Redis服务器进行实际通信以验证单元测试的假设。为此,我将使用Redis Docker容器。

https://www.codeproject.com/Articles/5274503/Creating-a-Redis-Client-using-the-NET-System-IO-Pi

使用.NET System.IO.Pipelines和Kestrel套接字库创建Redis客户端相关推荐

  1. System.IO.Pipelines: .NET高性能IO

    本文翻译自dotnet团队博客文章:https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-perfor ...

  2. 【.Net实用方法总结】 整理并总结.NET 中的 System.IO.Pipelines(管道)

  3. .Net 文件流 System.IO之Stream

    转自 :http://www.cnblogs.com/yukaizhao/archive/2011/07/28/stream.html Stream在msdn的定义:提供字节序列的一般性视图(prov ...

  4. 如何使用 System.IO 和 Visual C# 读取文本文件

    本文分步介绍了如何从文本文件 (.txt) 检索信息,然后使用 ArrayList 类向用户显示该信息. 回到顶端 要求 Microsoft Visual C# 2005 或 Microsoft Vi ...

  5. 使用System.IO.Packaging.Package进行文件压缩所产生的问题

    最近在项目中需要进行文件压缩,即将打包好的压缩文件提供给用户,用户进行下载. 获知微软提供了一个System.IO.Packaging.Package的类,从而可以进行打包,那么我就进行了使用.谁知道 ...

  6. 如何将struct System.Byte byte []转换为C#中的System.IO.Stream对象?

    如何将struct System.Byte byte[]转换为C#中的System.IO.Stream对象? #1楼 查看MemoryStream类. #2楼 您正在寻找MemoryStream.Wr ...

  7. 原来在UNITY中使用system.io下的所有函数都可以用相对路径 : Assets/xx

    原来在UNITY中使用system.io下的所有函数都可以用相对路径 : Assets/xx 代码如下图,这样就不用在绝对路径和相对路径之间不断转换了. 想要得到绝对路径时就傅 Application ...

  8. 安卓上为什么不能用system.io.file读取streammingAssets目录下的文件

    安卓上为什么不能用system.io.file读取streammingAssets目录下的文件 首先,看文档: Streaming Assets Most assets in Unity are co ...

  9. System.IO 的三个抽象类

    System.IO 命名空间 抽象类有:TextWriter,TextReader,Stream TextWriter抽象类 方法提供以下功能: 写入文本流或者后面跟着结束符 清理当前编写器的缓冲区 ...

最新文章

  1. SAP中国招聘内部顾问,工作职责是做客户项目,ABAP开发
  2. IO与NIO –中断,超时和缓冲区
  3. Linux bunzip2命令:bz2格式的解压缩命令
  4. sql 存储过程 并发测试_SQL单元测试模拟存储过程
  5. 【linux高级程序设计】(第九章)进程间通信-管道 3
  6. http之content-type
  7. Mac网易云音乐ncm格式转mp3
  8. java常量池存放什么_java常量池存放在哪里
  9. 使用 Google 的 zx 库在 Node 中编写 Shell 脚本技巧你会了吗
  10. 什么是做空和做多以及什么是做空期权波动率
  11. mac日历显示国家节假日及补班日期
  12. 跨境电商知识篇:平台与广告投放站之间的关系
  13. 【Pytorch基础教程34】EGES召回模型
  14. 在线小说网站的api和源码(有源码)
  15. java计算机毕业设计软考刷题系统源码+mysql数据库+系统+lw文档+部署
  16. java中最多小数位_在Java中最多2位小数?
  17. 百度震撼推出开发平台及分析
  18. BMP文件格式详解(BMP file format)
  19. 从最大似然到EM算法浅解 http://blog.csdn.net/zouxy09/article/details/8537620
  20. Gwallet路演柬埔寨站:区块链是游戏行业的巨大机会

热门文章

  1. 全国计算机一级考试试题大题,2016年全国计算机一级考试试题汇集
  2. 写python代码的心得体会_写python代码的一点感想
  3. linux上安装osg_ubuntu 环境 安装OSG
  4. python ddos 检测系统_python 检查是否存在ddos攻击
  5. 设计软件哪里找?图片素材哪里找?
  6. UI素材干货模板|网页“按钮”组件,教你要如何设计!
  7. keil5函数 默认返回值_python实用技巧——获取部分返回值的4种方式
  8. C++ 判断进程是否退出 代码封装
  9. GetCommandLineW()作用
  10. HeadFirst设计模式之观察者模式学习