<strike id="xxt9l"><dl id="xxt9l"><ruby id="xxt9l"></ruby></dl></strike>
<th id="xxt9l"></th>
<strike id="xxt9l"><video id="xxt9l"></video></strike><span id="xxt9l"></span>
<span id="xxt9l"></span>
<strike id="xxt9l"><video id="xxt9l"></video></strike> <th id="xxt9l"></th><th id="xxt9l"><video id="xxt9l"></video></th>
<span id="xxt9l"></span>
<span id="xxt9l"><video id="xxt9l"><strike id="xxt9l"></strike></video></span>
<strike id="xxt9l"></strike>
<span id="xxt9l"></span>
<span id="xxt9l"></span>
<th id="xxt9l"></th>
<span id="xxt9l"><video id="xxt9l"><strike id="xxt9l"></strike></video></span>
<th id="xxt9l"></th>
<del id="xxt9l"><i id="xxt9l"><del id="xxt9l"></del></i></del><th id="xxt9l"><video id="xxt9l"><strike id="xxt9l"></strike></video></th>
<span id="xxt9l"></span>
<span id="xxt9l"><video id="xxt9l"><ruby id="xxt9l"></ruby></video></span><th id="xxt9l"><video id="xxt9l"></video></th>
<th id="xxt9l"><video id="xxt9l"></video></th>
<span id="xxt9l"></span>
孙凯
聊聊如何设计千万级吞吐量的.Net Core网络通信!
来源:任达     发?#38469;?#38388;: 2019-05-29      浏?#26469;?#25968;:355

字号:

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

作者:大石头时间:2018-10-26 晚上 20:00地点:QQ群-1600800内容:网络通信,
    网络库使用方式网络库设计理念,高性能要点

介绍

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

当时测试相关记录和代码地址记录: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作为客户端在客户端连接服务器和给服务端发送数据的时候,分别触发StartOnReceive方法,连接之后服务端发送了Welcome 的消息,客户端发送5次“你好”。服务端回传收到的数据,打了一个日志,把收到的信息转成?#22336;?#20018;输出到控制台。NetServer是应用级网络服务器,支持tcp/udp/ipv4/ipv6。上面可以看到,同时监听了四个端口。码神工具也可以连接上来

解释

对于网络会话?#27492;擔?#26368;关键的就是客户端连上来,以及收到数据包,这两部分,对应上面StartOnReceive两个方法

服务端

上面是最小的网络库例程,简单演示了服务端和客户端,连接和收发信息。网络应用分为NetServer/NetSession,服务端、会话,N个客户端连接服务器,就会有N个会话。来一个客户端连接,服务?#21496;蚽ew一个新的NetSession,并执行Start,收到一个数据包,就执行OnReceive,连接断开,就执行OnDispose,这便是服务端的全部。客户端连接刚上来的时候,没有数据包等其它信息,所以这个时候没有参数。客户端发数据包过来,OnReceive函数在处理。服务端的创建,可以是很简单,看以下截图。这里为了测试方便,开了很多Log,实际使用的时候,根据需要注释。长连接、心跳第二节设计理念再讲。

客户端

跟很多网络库不同,NewLife.Net除了服务端,还封装了客户端。客户端的核?#27169;?#20063;就是Send函数和Received事件,同步发?#20572;?#24322;步接收。因为是长连接,所以服务端随时可以向客户端发送数据包,客户端也可以收到。tcp在不做设置的时候,默认长连接2小时。NetServer默认20分钟,在没有心跳的时候,20分?#29992;?#26377;数据包往来,服务端会干掉这个会话。虽然上面讲的NetServer和Client,?#38469;莟cp,但是换成其它协议也是可以的。这里的NetServer和NetUri.CreateRemote,同时支持Tcp/Udp/IPv4/IPv6等,CreateRemote内部,就是根据地址的不同,去new不同的客户端。所以我们写的代码,根本不在意用的是tcp还是udp,或者IPv6。有兴趣的可以看看源码

1.2 构建可靠网络服务

相关博客要真正形成一个网络服务,那得稳定可靠。上面例程EchoTest只是简单演示,接下来看下一个例程EchoAgent。

安装运行

这是一个标准的Windows服务,有了这个东西,我们就可以妥妥的注册到Windows里面去。这也是目前我们大量数据分析程序的必备。首先运行EchoAgent,按2,安?#30333;?#20876;服务,用管理员身份运行。安装成功然后可以在服务里面?#19994;?#21018;?#30651;?#35013;的服务。安装完成可以在服务上?#19994;劍?#20877;次按2就是?#23545;兀?#36825;个是XAgent提供的功能这时候按3,启动服务

代码解释

接下来看代码,服务启动的时候,执行StartWork。在这个时候实例化并启动NetServer,得到的效果就跟例程EchoTest一样,区别是一个是控制台一个是服务。停止服务时执行StopWork,我们可以在这里关闭NetServer。详细请看源码必须有这个东西,你的网络服务程序,才有可能达到产品级。linux上直接控制台,上nohup,?#27604;?#36824;有很多其它办法。以后希望这个XAgent能够支持linux吧,这样就一劳永逸了

1.3 压测

相关博客只需要记住一个两个数字,.net应用打出来2266万tps,流量峰值4.5Gbps两千万吞吐量的数字,?#27604;唬?#21482;能看不能用。因为服务端只是刚才的Echo而已,并没有带什么业务。实际工作中,带着业务和数据库,能跑到10万已经非常非常牛逼了。

我们工作中的服务可以跑到100万,但是?#20063;?#25954;,怕它不小心就崩了。所以我们?#38469;前?#29031;10万的上限来设计,不够?#25237;?#26381;务器好了,达到5万以上后,稳定性更重要

网络编程的坑

主要有粘包

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

1.4 网络编程的坎——粘包

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

什么是粘包

举个栗子:客户端连续发了5个包,服务?#21496;?#25910;到了一个大包。代码就不演示了,把第一个例程的这个睡眠去掉。客户端连续发了5个包,服务?#21496;?#25910;到了一个大包。

原因

很多人可能都听说Tcp是流式协议,但是很少人去问,什么叫流式吧?流式,就是它把数据像管道一样传输过去。刚才我们发了5个 “你好?#20445;?#23427;负责把这10个?#22336;?#21040;对方,至于发多少次,每次发几个字,不用我们操?#27169;瑃cp底层自己处理。tcp负责把数据一个不丢的?#27492;?#24207;的发过去。所以,为了性能,它一般会把相近的数据包凑到一起发过去。对方收到一个大包,5个小包都粘在了一起,这就是最简单的粘包。这个特性由NoDelay设置决定。NoDelay默认是false,需要自己设置。如果设置了,就不会等待。但是不要想得那么美好,因为对方可能合包。?#38047;?#32593;MTU(Maximum Transmission Unit,最大传输单元)是1500,处于ip tcp 头部等,大概1472多点的样子。

更复杂的粘包及解决方法

A 1000 字节 B 也是 1000字节,对方可能收到两个包,1400 + 600。对方可能收到两个包,1400 + 600。凡是以特殊符号开头或结尾来处理粘包的办法,都会有这样那样的缺陷,最终是给自?#21644;?#22353;。所以,tcp粘包,绝大部分解决方案,偏向于指定数据包长度。这其中大部分使用4字节长度,长度+数据。对方收到的时候,根据长度判断后面数据足够了没有。这是粘包的处理代码:http://git.newlifex.com/NewLife/X/Blob/master/NewLife.Core/Net/Handlers/MessageCodec.cs每次判断长度,接收一个或多个包,如果接收不完,留下,存起来。等下一个包到来的时候,?#21019;?#23436;整。虽然tcp确保数据不丢,但是难免我们自己失手,弄丢了一点点数据。为了避免祸害后面所有包,?#25176;?#35201;进行特殊处理了。每个数据帧,自己把头部长?#32676;?#25968;据体凑一起发送啊,tcp确保顺序。这里我?#21069;?#36229;时时间设置为3~5秒,每次?#30651;?#22914;果发现上次有残留,并?#39029;?#26102;了,那么就扔了它,省得祸害后面。根据以上,粘包的关键解决办法,就是设定数据格式,可以看?#27425;?#20204;的SRMP协议,1字节标识,1字节序号,2字节长度如果客户端发?#21534;?#39057;?#20445;?#26381;务端tcp缓冲区阻塞,发送窗口会逐步缩小到0,不再接受客户端数据。

1.5 .NetCore版RPC框架

NetCore版RPC框架NewLife.ApiServer。先看看这个效果

代码分析

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

服务端

有没有发现,这个ApiServer跟前面的NetServer有点像?#31185;?#23454;ApiServer内部就?#24184;?#20010;NetServer这么些行代码,?#22270;?#20010;地方有价值,一个是注册了两个控制器。你可以直接理解为Mvc的控制器,只不过我们没有路由管理系统,直接手工注册。第二个是指定编码器为Json,用Json传输参数?#22836;?#22238;值。其?#30340;?#37096;默认就是Json,可以不用指定看?#27425;?#20204;的控制器,特别像Mvc,只不过这里的Controller没有基类,各个Action返回值不是ActionResult,是的,ApiServer就是一个按照Mvc风格设置的RPC框架返回复杂对象做请求预处理,甚至拦截异常像下面这样写RPC服务,然后把它注册到ApiServer上,客户?#21496;?#21487;以在1234端口上请求这些接口服务啦

客户端

客户端是ApiClient,这里的MyClient继承自ApiClient这些就是我们刚才客户端远程调用的stub代码啦,?#27604;唬?#25105;们没有自动生成stub,也没?#24184;?#27714;客户端跟服务?#26031;?#29992;接口之类。实际上,我们认为完全没有必要做接口?#38469;?#22823;部分项目的服务接口很少,并且要求灵活多变stub就是类似于,刚才那个MyController实现IAbc接口,然后客户端根据服务端元数据自动在内存里面生成一个stub类并编译,这个类实现了IAbc接口。客户端直接操作接口,还以为在调用服务端 的函数呢其实stub代码内部,就是封装了 这里的InvokeAsync这些代码,等同于自动生成这些代码,包括gRPC、Thrift等?#38469;?#36825;么干的

框架解析

这个RPC框架,封包协议就是刚才的SRMP,负载数据也就是协议是json当需要高速传输的时候,参数用Byte[],它就会直接传输,不经json序列化,这是多年经验得到的灵活性与性能的最佳结合点

2.1 人人都?#24184;?#20010;自己的高性能网络库

网络库核心代码:http://git.newlifex.com/NewLife/X/Tree/master/NewLife.Core/Net我们一开?#23395;?#26159;让Tcp/Udp可以混合使用,网络库设计于2005年,应该要比现如今绝大部分网络框架要老。服务端清一色采用 Server+Session 的方式。网络库的几个精髓文件其中比较重要的一个,里面实现了 Open/Close/Send/Receive 系列封装,Tcp/Udp略有不同,重载就好了。打开关闭比较简单,就?#21796;?#20102;所有对象,不管客户端服务端,?#38469;?#29616;ISocket。然后客户端Client,服务端Server+Session。tcp+udp同时支持并不难,因为它们都基于Socket。目前无状态无会话的通信架构,做不到高性能。我们就是依靠长连接以及合并小包,实现超高吞吐量一般灵活性和高性能?#38469;?#20114;相矛盾的

2.2 高性能设计要点

第一要点:同步发送,因为要做发?#25237;恿小?#25286;分、合并,等等,异步发送大大增加了复杂度。大家如果将来遇到诡异的40ms延迟,非常可能就是tcp的nodelay作怪,可以设为true解决第二要点:IOCP,高吞吐率的服务端,一定是异步接收,而不是多线程同步。?#27604;唬?#21487;以指定若干个线程去select,也就是Linux里面常见的poll,那个不在这里?#33268;郟琖indows极少人这么干,大量资料表明,iocp更厉害。SAEA是.net/.netcore当下最流行的网络架构,我们可以通俗理解为,把这个缓冲区送给操作系统内核,待会有数据到来的时候,直接放在里面,这样子就减少了一次内核态到用户态的拷贝过程。我们测试4.5Gbps,除以8,大概是 540M字节,这个拷贝成本极高第三要点:零拷贝ZeroCopy,这也是netty的核心优势。iocp是为了减少内核态到用户态的拷贝,zerocopy进一步把这个数据交给用户层,不用拷贝了。数据处理,我们采用了链式管道,这些?#38469;?#31649;道的编码器第四要点:合并小包,NoDelay=false,?#24066;韙cp合并小包,MTU=1500,除了头部,一般是1472第五要点:二进制序列化,消息报文尽可能短小,每个包1k,对于100Mbps,也就12M,理论上最多12000包,所以大量Json协议或者?#22336;?#20018;协议,吞吐量都在1万上下SRMP头部4字节,ApiServer的消息报?#27169;?#19968;般二三十个字节,甚至十几个字节第五要点:批量操作User FindByID(int id); User[] FindByIDs(int[] ids);

最后

整理不全,大家凑合着看。中途?#35745;粒?#35821;音啥的还掉了,准备得不是很好,下周再来一次吧,选Redis

  • 相关内容:
无敌北京pk10计划软件手机版 第一比分网 澳洲彩票幸运5开奖结果查询 北京赛车pk10平刷王 真人游戏公司 《彩票助赢软件》官网 山东福彩20选5走势图 山东群英会时时彩网 贵州11选5精准预测 二肖中特期期准100%顶尖号码 混合过关错一场有奖吗 彩票系统app开发 北京快三一定牛走势图电脑版 广西福彩开奖公告 2元可以中2400万彩票 怎么开个彩票网站