聊聊如何设计千万级吞吐量的.Net Core网络通信!

  • 作者:大石头

  • 时间:2018-10-26 晚上 20:00

  • 地点:QQ群-1600800

  • 内容:网络通信,

  1. 网络库使用方式

  2. 网络库设计理念,高性能要点

介绍

  • 首先看下面这张很具有代表性的图,2018年5月份做的测试。当时单服务器得到 2256tps(Transactions Per Second,每秒事务数) 的吞吐率。这次测试只是说明一个问题,.Net可以做超高吞吐率的应用。
    640?wx_fmt=png
    640?wx_fmt=png

  • 当时测试相关记录和代码地址

    • 记录:https://www.cnblogs.com/nnhy/p/newlife_net_benchmark.html

    • 代码国外地址:https://github.com/nnhy/NewLife.Net.Tests

    • 代码国内地址:http://git.newlifex.com/Stone/NewLife.Net.Tests

1.1 开始网络编程

简单的网络程序示例

  • 相关使用介绍:https://www.cnblogs.com/nnhy/p/newlife_net_echo.html

  • 克隆上面的代码,运行EchoTest项目,打开编译的exe,打开两次,一个选1作为服务器,一个选2作为客户端
    640?wx_fmt=png
    640?wx_fmt=png

  • 在客户端连接服务器和给服务端发送数据的时候,分别触发StartOnReceive方法,连接之后服务端发送了Welcome 的消息,客户端发送5次“你好”。服务端回传收到的数据,打了一个日志,把收到的信息转成字符串输出到控制台。

  • NetServer是应用级网络服务器,支持tcp/udp/ipv4/ipv6。上面可以看到,同时监听了四个端口。

  • 码神工具也可以连接上来
    640?wx_fmt=png

解释

  • 对于网络会话来说,最关键的就是客户端连上来,以及收到数据包,这两部分,对应上面StartOnReceive两个方法
    640?wx_fmt=png
    640?wx_fmt=jpeg

服务端

  • 上面是最小的网络库例程,简单演示了服务端和客户端,连接和收发信息。网络应用分为NetServer/NetSession,服务端、会话,N个客户端连接服务器,就会有N个会话。来一个客户端连接,服务端就new一个新的NetSession,并执行Start,收到一个数据包,就执行OnReceive,连接断开,就执行OnDispose,这便是服务端的全部。

  • 客户端连接刚上来的时候,没有数据包等其它信息,所以这个时候没有参数。客户端发数据包过来,OnReceive函数在处理。

  • 服务端的创建,可以是很简单,看以下截图。这里为了测试方便,开了很多Log,实际使用的时候,根据需要注释。
    640?wx_fmt=png

  • 长连接、心跳第二节设计理念再讲。

客户端

  • 跟很多网络库不同,NewLife.Net除了服务端,还封装了客户端。客户端的核心,也就是Send函数和Received事件,同步发送,异步接收。
    640?wx_fmt=png

  • 因为是长连接,所以服务端随时可以向客户端发送数据包,客户端也可以收到。tcp在不做设置的时候,默认长连接2小时。

  • NetServer默认20分钟,在没有心跳的时候,20分钟没有数据包往来,服务端会干掉这个会话。

  • 虽然上面讲的NetServer和Client,都是tcp,但是换成其它协议也是可以的。这里的NetServer和NetUri.CreateRemote,同时支持Tcp/Udp/IPv4/IPv6等,CreateRemote内部,就是根据地址的不同,去new不同的客户端。所以我们写的代码,根本不在意用的是tcp还是udp,或者IPv6。有兴趣的可以看看源码

1.2 构建可靠网络服务

  • 相关博客

  • 要真正形成一个网络服务,那得稳定可靠。上面例程EchoTest只是简单演示,接下来看下一个例程EchoAgent。
    640?wx_fmt=png
    640?wx_fmt=png
    640?wx_fmt=png

安装运行

  • 这是一个标准的Windows服务,有了这个东西,我们就可以妥妥的注册到Windows里面去。这也是目前我们大量数据分析程序的必备。

  • 首先运行EchoAgent,按2,安装注册服务,用管理员身份运行。安装成功然后可以在服务里面找到刚刚安装的服务。
    640?wx_fmt=png
    640?wx_fmt=png
    640?wx_fmt=png
    640?wx_fmt=png

  • 安装完成可以在服务上找到,再次按2就是卸载,这个是XAgent提供的功能
    640?wx_fmt=png
    640?wx_fmt=png

  • 这时候按3,启动服务
    640?wx_fmt=png

代码解释

  • 接下来看代码,服务启动的时候,执行StartWork。在这个时候实例化并启动NetServer,得到的效果就跟例程EchoTest一样,区别是一个是控制台一个是服务。停止服务时执行StopWork,我们可以在这里关闭NetServer。详细请看源码
    640?wx_fmt=png

  • 必须有这个东西,你的网络服务程序,才有可能达到产品级。linux上直接控制台,上nohup,当然还有很多其它办法。以后希望这个XAgent能够支持linux吧,这样就一劳永逸了

1.3 压测

  • 相关博客

  • 只需要记住一个两个数字,.net应用打出来2266万tps,流量峰值4.5Gbps

  • 两千万吞吐量的数字,当然,只能看不能用。因为服务端只是刚才的Echo而已,并没有带什么业务。实际工作中,带着业务和数据库,能跑到10万已经非常非常牛逼了。

  • 我们工作中的服务可以跑到100万,但是我不敢,怕它不小心就崩了。所以我们都是按照10万的上限来设计,不够就堆服务器好了,达到5万以上后,稳定性更重要

    网络编程的坑

  • 主要有粘包

  • 程序员中会网络编程的少,会解决粘包的更少!

1.4 网络编程的坎——粘包

  • 普遍情况,上万的程序员,会写网络程序的不到20%,会解决粘包问题的不到1%,如果大家会写网络程序,并且能解决粘包,那么至少已经达到了网络编程的中级水平。

什么是粘包

  • 举个栗子:客户端连续发了5个包,服务端就收到了一个大包。代码就不演示了,把第一个例程的这个睡眠去掉。
    640?wx_fmt=png
    640?wx_fmt=png

  • 客户端连续发了5个包,服务端就收到了一个大包。

原因

  • 很多人可能都听说Tcp是流式协议,但是很少人去问,什么叫流式吧?流式,就是它把数据像管道一样传输过去。

  • 刚才我们发了5个 “你好”,它负责把这10个字发到对方,至于发多少次,每次发几个字,不用我们操心,tcp底层自己处理。tcp负责把数据一个不丢的按顺序的发过去。所以,为了性能,它一般会把相近的数据包凑到一起发过去。对方收到一个大包,5个小包都粘在了一起,这就是最简单的粘包。

  • 这个特性由NoDelay设置决定。NoDelay默认是false,需要自己设置。如果设置了,就不会等待。但是不要想得那么美好,因为对方可能合包。

  • 局域网MTU(Maximum Transmission Unit,最大传输单元)是1500,处于ip tcp 头部等,大概1472多点的样子。

更复杂的粘包及解决方法

  • A 1000 字节 B 也是 1000字节,对方可能收到两个包,1400 + 600。对方可能收到两个包,1400 + 600。

  • 凡是以特殊符号开头或结尾来处理粘包的办法,都会有这样那样的缺陷,最终是给自己挖坑。所以,tcp粘包,绝大部分解决方案,偏向于指定数据包长度。这其中大部分使用4字节长度,长度+数据。对方收到的时候,根据长度判断后面数据足够了没有。

  • 这是粘包的处理代码:http://git.newlifex.com/NewLife/X/Blob/master/NewLife.Core/Net/Handlers/MessageCodec.cs
    640?wx_fmt=png

  • 每次判断长度,接收一个或多个包,如果接收不完,留下,存起来。等下一个包到来的时候,拼凑完整。

  • 虽然tcp确保数据不丢,但是难免我们自己失手,弄丢了一点点数据。为了避免祸害后面所有包,就需要进行特殊处理了。

  • 每个数据帧,自己把头部长度和数据体凑一起发送啊,tcp确保顺序。这里我们把超时时间设置为3~5秒,每次凑包,如果发现上次有残留,并且超时了,那么就扔了它,省得祸害后面。
    640?wx_fmt=png

  • 根据以上,粘包的关键解决办法,就是设定数据格式,可以看看我们的SRMP协议,1字节标识,1字节序号,2字节长度
    640?wx_fmt=png

  • 如果客户端发送太频繁,服务端tcp缓冲区阻塞,发送窗口会逐步缩小到0,不再接受客户端数据。

1.5 .NetCore版RPC框架

  • NetCore版RPC框架NewLife.ApiServer。
    640?wx_fmt=png

  • 先看看这个效果
    640?wx_fmt=png

代码分析

  • 我们看这部分代码,4次调用远程函数,成功获取结果,包括二进制高速调用、返回复杂对象、捕获远程异常,没错,这就是一个RPC。
    640?wx_fmt=png
    640?wx_fmt=png

服务端

  • 有没有发现,这个ApiServer跟前面的NetServer有点像?其实ApiServer内部就有一个NetServer
    640?wx_fmt=png

  • 这么些行代码,就几个地方有价值,一个是注册了两个控制器。你可以直接理解为Mvc的控制器,只不过我们没有路由管理系统,直接手工注册。

  • 第二个是指定编码器为Json,用Json传输参数和返回值。其实内部默认就是Json,可以不用指定

  • 看看我们的控制器,特别像Mvc,只不过这里的Controller没有基类,各个Action返回值不是ActionResult,是的,ApiServer就是一个按照Mvc风格设置的RPC框架
    640?wx_fmt=png

  • 返回复杂对象
    640?wx_fmt=png

  • 做请求预处理,甚至拦截异常
    640?wx_fmt=png

  • 像下面这样写RPC服务,然后把它注册到ApiServer上,客户端就可以在1234端口上请求这些接口服务啦
    640?wx_fmt=jpeg
    640?wx_fmt=jpeg

客户端

  • 客户端是ApiClient,这里的MyClient继承自ApiClient
    640?wx_fmt=png
    640?wx_fmt=png

  • 这些就是我们刚才客户端远程调用的stub代码啦,当然,我们没有自动生成stub,也没有要求客户端跟服务端共用接口之类。实际上,我们认为完全没有必要做接口约束,大部分项目的服务接口很少,并且要求灵活多变

  • stub就是类似于,刚才那个MyController实现IAbc接口,然后客户端根据服务端元数据自动在内存里面生成一个stub类并编译,这个类实现了IAbc接口。
    客户端直接操作接口,还以为在调用服务端 的函数呢

  • 其实stub代码内部,就是封装了 这里的InvokeAsync这些代码,等同于自动生成这些代码,包括gRPC、Thrift等都是这么干的

框架解析

  • 这个RPC框架,封包协议就是刚才的SRMP,负载数据也就是协议是json

  • 当需要高速传输的时候,参数用Byte[],它就会直接传输,不经json序列化,这是多年经验得到的灵活性与性能的最佳结合点

2.1 人人都有一个自己的高性能网络库

  • 网络库核心代码:http://git.newlifex.com/NewLife/X/Tree/master/NewLife.Core/Net

  • 我们一开始就是让Tcp/Udp可以混合使用,网络库设计于2005年,应该要比现如今绝大部分网络框架要老。服务端清一色采用 Server+Session 的方式。

  • 网络库的几个精髓文件
    640?wx_fmt=png

  • 其中比较重要的一个,里面实现了 Open/Close/Send/Receive 系列封装,Tcp/Udp略有不同,重载就好了。打开关闭比较简单,就不讲了
    640?wx_fmt=png

  • 所有对象,不管客户端服务端,都实现ISocket。然后客户端Client,服务端Server+Session。tcp+udp同时支持并不难,因为它们都基于Socket。

  • 目前无状态无会话的通信架构,做不到高性能。我们就是依靠长连接以及合并小包,实现超高吞吐量

  • 一般灵活性和高性能都是互相矛盾的

2.2 高性能设计要点

  • 第一要点:同步发送,因为要做发送队列、拆分、合并,等等,异步发送大大增加了复杂度。大家如果将来遇到诡异的40ms延迟,非常可能就是tcp的nodelay作怪,可以设为true解决

  • 第二要点:IOCP,高吞吐率的服务端,一定是异步接收,而不是多线程同步。当然,可以指定若干个线程去select,也就是Linux里面常见的poll,那个不在这里讨论,Windows极少人这么干,大量资料表明,iocp更厉害。
    640?wx_fmt=png
    640?wx_fmt=png

    • SAEA是.net/.netcore当下最流行的网络架构,我们可以通俗理解为,把这个缓冲区送给操作系统内核,待会有数据到来的时候,直接放在里面,这样子就减少了一次内核态到用户态的拷贝过程。

    • 我们测试4.5Gbps,除以8,大概是 540M字节,这个拷贝成本极高

  • 第三要点:零拷贝ZeroCopy,这也是netty的核心优势。iocp是为了减少内核态到用户态的拷贝,zerocopy进一步把这个数据交给用户层,不用拷贝了。

    • 数据处理,我们采用了链式管道,
      640?wx_fmt=png

    • 这些都是管道的编码器
      640?wx_fmt=png

  • 第四要点:合并小包,NoDelay=false,允许tcp合并小包,MTU=1500,除了头部,一般是1472

  • 第五要点:二进制序列化,消息报文尽可能短小,每个包1k,对于100Mbps,也就12M,理论上最多12000包,所以大量Json协议或者字符串协议,吞吐量都在1万上下

    • SRMP头部4字节,ApiServer的消息报文,一般二三十个字节,甚至十几个字节

  • 第五要点:批量操作User FindByID(int id); User[] FindByIDs(int[] ids);

最后

  • 整理不全,大家凑合着看。中途录屏,语音啥的还掉了,准备得不是很好,下周再来一次吧,选Redis

原文地址: https://www.cnblogs.com/xxred/p/9859893.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MonkeyFest2018 微软最有价值专家讲座

MonkeyFest2018微软最有价值专家讲座Monkey Fest 是一个一年一度由全球Microsoft Xamarin跨平台开发者发起的全球性社区活动,主要是推广在云、人工智能、大数据、移动开发等技术。本次活动同时在新加坡,美国,日本,加拿大&#xff…

2020牛客国庆集训派对day2 MATRIX MULTIPLICATION CALCULATOR

MATRIX MULTIPLICATION CALCULATOR 题意: 求两矩阵相乘 题解: 应该都学过把。。。矩阵相乘 矩阵相乘的前提是两个矩阵的列等于另一个矩阵的行 也就是cij∑aik*bkj 原理很简单注意格式,但是我遇到一个玄学问题。。。 就是卡格式了。。我人…

P3911 最小公倍数之和

最小公倍数之和 题目描述: 对于A1,A2…AN,求 ∑i1N∑i1Nlcm(Ai,Aj)\sum_{i1}^{N}\sum_{i1}^{N} lcm(Ai,Aj)∑i1N​∑i1N​lcm(Ai,Aj) 题解: 莫比乌斯反演,直接强推一波 推导过程我也是一知半解,大体如图…

终于明白了 C# 中 Task.Yield 的用途

最近在阅读 .NET Threadpool starvation, and how queuing makes it worse 这篇博文时发现文中代码中的一种 Task 用法之前从未见过,在网上看了一些资料后也是云里雾里不知其解,很是困扰。今天在程序员节的大好日子里终于想通了,于是写下这篇…

2018-2019 ACM-ICPC, Asia Seoul Regional Contest——A - Circuits

A - Circuits 不难发现x坐标根本没用&#xff0c;只需要存储y坐标。 题目所求的两条直线y1ay_1ay1​a&#xff0c;y2b(a<b)y_2b\ (a<b)y2​b (a<b) 我们枚举y2by_2by2​b这条线&#xff0c;这条线一定可以是矩形的边界&#xff0c;于是我们扫描矩形边界差分计算当前…

aspnet core 2.1中使用jwt从原理到精通一

原理jwt对所有语言都是通用的&#xff0c;只要知道秘钥&#xff0c;另一一种语言有可以对jwt的有效性进行判断;jwt的组成&#xff1b;Header部分Base64转化.Payload部分Base64转化.使用HS256方式根据秘钥对前面两部分进行加密后再Base64转化&#xff0c;其中使用的hs256加密是h…

2020牛客国庆集训派对day3 Points

Points 题目描述 Jack and Rose are playing games after working out so many difficult problems. They together drew a “Haizi” tree to show their collaboration. “Haizi” tree is the same as the tree defined in graph theory. Now Jack would like to count t…

.NET Core微服务之路:利用DotNetty实现一个简单的通信过程

上一篇我们已经全面的介绍过《基于gRPC服务发现与服务治理的方案》&#xff0c;我们先复习一下RPC的调用过程&#xff08;笔者会在这一节的几篇文章中反复的强调这个过程调用方案&#xff09;&#xff0c;看下图根据上面图&#xff0c;服务化原理可以分为3步&#xff1a;服务端…

[译]ASP.NET Core中使用MediatR实现命令和中介者模式

在本文中&#xff0c;我将解释命令模式&#xff0c;以及如何利用基于命令模式的第三方库来实现它们&#xff0c;以及如何在ASP.NET Core中使用它来解决我们的问题并使代码简洁。因此&#xff0c;我们将通过下面的主题来进行相关的讲解。什么是命令模式?命令模式的简单实例以及…

.NET in Browser - Blazor

什么是BlazorBlazor 是一个实验性的. NET web 框架, 使用 C# 和 HTML 在任何浏览器中不需要插件即可运行 WebAssembly 程序集。什么是WebAssemblyWebAssembly是一种新的适合于编译到Web的&#xff0c;可移植的&#xff0c;大小和加载时间高效的格式&#xff0c;是一种新的字节码…

在碰撞中成长 - 北京银行的DevOps实践之路

2018年10/27日&#xff0c;在上海召开的微软年度最大规模的技术盛会—微软2018技术暨生态大会上&#xff0c;北京银行渠道系统负责人&敏捷团队负责人周兵女士和大家一起分享了北京银行的DevOps 实践转型经验&#xff0c;得到了大会听众的热烈评价和共鸣&#xff0c;会后众多…

2020牛客国庆集训派对day4 Emergency Evacuation

Emergency Evacuation 题意&#xff1a; 有n个人在不同的位置上&#xff0c;在最后面有一个出口exit&#xff0c;所有人都要逃离出去&#xff08;走出出口&#xff09;&#xff0c;且每个格子最多容纳一个人&#xff0c;当有人挡在前面时&#xff0c;后面的人必须停留&#x…

【活动(广州)】MonkeyFest2018 微软最有价值专家讲座

MonkeyFest2018微软最有价值专家讲座Monkey Fest 是一个一年一度由全球Microsoft Xamarin跨平台开发者发起的全球性社区活动&#xff0c;主要是推广在云、人工智能、大数据、移动开发等技术。本次活动同时在新加坡&#xff0c;美国&#xff0c;日本&#xff0c;加拿大&#xff…

互联网公司为什么普遍996而不是666

根据skinshoe wu的遭遇&#xff0c;解释一下互联网行业的12小时工作制以及996。题目说的有点绝对&#xff0c;这里先澄清一下&#xff1a;有的公司是10106&#xff0c;9106&#xff0c;10126&#xff0c;995&#xff0c;甚至955&#xff0c;007的都有&#xff0c;我只说大多数&…

IdentityServer4之JWT签名(RSA加密证书)及验签

一、前言在IdentityServer4中有两种令牌&#xff0c;一个是JWT和Reference Token&#xff0c;在IDS4中默认用的是JWT&#xff0c;那么这两者有什么区别呢&#xff1f;二、JWT与Reference Token的区别1、JWT(不可撤回)  JWT是一个非常轻巧的规范&#xff0c;一般被用来在身份提…

.NET Core使用IO合并技巧轻松实现千万级消息推送

之前讲述过多路复用实现单服百万级别RPS吞吐,但在文中有一点是没有说的就是消息IO合并&#xff0c;如果缺少了消息IO合并即使怎样多路复用也很难达到百万级别的请求响毕竟所有应用层面的网络IO读写都是非常损耗性能的&#xff08;需要硬件配置很高的服务器&#xff09;。这一章…

天气情况图像分类练习赛 第三阶段(赛中感)

第三阶段也是实战阶段&#xff0c;不同于前两个阶段的填空而是实打实的预测分析 题目会给出8000张照片数据&#xff0c;其中6000作为训练集而另外2000张作位测试集&#xff0c;通过对6000张的训练来预测2000的结果&#xff0c;并将结果输出到csv文件中&#xff0c;提交检验成功…

一码阻塞,万码等待:ASP.NET Core 同步方法调用异步方法“死锁”的真相

在我们 2015 年开始的从 .NET Framework 向 .NET Core 迁移的工程中&#xff0c;遇到的最大的坑就是标题中所说的——同步方法中调用异步方法发生”死锁”。虽然在 .NET Framework 时代就知道不能在同步方法中调用异步方法&#xff0c;但我们却明知路有坑&#xff0c;偏向此路行…

take

take 题解参考 题目描述 Kanade has n boxes , the i-th box has p[i] probability to have an diamond of d[i] size. At the beginning , Kanade has a diamond of 0 size. She will open the boxes from 1-st to n-th. When she open a box,if there is a diamond in it an…

将传统 ASP.NET 应用迁移到 .NET Core

点击蓝字关注我现在越来越多的人在谈论. NET Core。诚然&#xff0c;.NET Core 是未来, 但是.NET Framework 仍在支持, 因为大量的应用程序无法在短时间内迁移。.NET Core 和 .NET Framework 就像电动汽车和汽油动力汽车。汽油车是成熟的&#xff0c;你可以毫无任何问题驾驶它&…