聊聊如何设计千万级吞吐量的.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,一经查实,立即删除!

相关文章

2020牛客国庆集训派对day2 AKU NEGARAKU

来源:牛客网: 题目描述 1st Academy is an international leadership training academy based in Kuala Lumpur. Every year, the company trains thousands of people to be supplied to companies around the world. To be fair amongst all the tra…

P5540-[BalkanOI2011]timeismoney|最小乘积生成树【最小生成树,凸壳】

正题 题目链接:https://www.luogu.com.cn/problem/P5540 题目大意 给出nnn个点mmm条边边权是一个二元组(ai,bi)(a_i,b_i)(ai​,bi​),求出一棵生成树最小化 (∑e∈Tae)(∑e∈Tbe)(\sum_{e\in T}a_e)\times(\sum_{e\in T}b_e)(e∈T∑​ae​)(e∈T∑​be​) 的情况下…

【数论】能量采集(P1447)

正题 P1447 题目大意 给出n,m,求2∑i1n∑j1m(gcd(i,j)−1)nm2\times \sum_{i1}^n\sum_{j1}^m(gcd(i,j)-1)n\times m2i1∑n​j1∑m​(gcd(i,j)−1)nm 解题思路 2∑i1n∑j1m(gcd(i,j)−1)nm2∑i1n∑j1mgcd(i,j)−nm2\times \sum_{i1}^n\sum_{j1}^m(gcd(i,j)-1)n\tim…

codeforces1440 E. Greedy Shopping

昨天晚上做完4题还有30分钟,感觉太晚了就没继续写,不过看了下E题感觉是一个线段树题目,今天中午看了看发现就是一个线段树上递归的询问问题,不过我之前没写过但是靠着日益强大的乱写能力竟然水出来了~~ E. Greedy Shopping 不难…

MonkeyFest2018 微软最有价值专家讲座

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

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

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

P5369-[PKUSC2018]最大前缀和【状压dp】

正题 题目链接:https://www.luogu.com.cn/problem/P5369 题目大意 一个数列aaa的权值定义为max{∑i1kai}(k∈[1,n])max\{\sum_{i1}^ka_i\}(k\in[1,n])max{∑i1k​ai​}(k∈[1,n]) 给出nnn个数字,求它们所有排列的权值和 1≤n≤201\leq n\leq 201≤n≤20 解题思路 …

【数论】疯狂 LCM(P1891)

正题 P1891 题目大意 有T个询问,每个询问给出n,求∑i1nlcm(i,n)\sum_{i1}^nlcm(i,n)i1∑n​lcm(i,n) 解题思路 ∑i1nlcm(i,n)\sum_{i1}^nlcm(i,n)i1∑n​lcm(i,n) n∑i1ni/gcd(i,n)n\sum_{i1}^ni/gcd(i,n)ni1∑n​i/gcd(i,n) n∑d∣n1d∑i1ni[gcd(i,n)…

codeforces1440 D. Graph Subset Problem

D. Graph Subset Problem jiangly代码%%% 感谢大佬对jly代码的解释 先贪心找一下clique,如果某个点的度数是k-1,那就爆搜他的相邻节点组成clique,看看是不是完全子图。如果不是由于这个点的度数小于k(若完全子图中由此点只能是c…

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 用法之前从未见过,在网上看了一些资料后也是云里雾里不知其解,很是困扰。今天在程序员节的大好日子里终于想通了,于是写下这篇…

Wannafly挑战赛23F-计数【原根,矩阵树定理,拉格朗日插值】

正题 题目链接:https://ac.nowcoder.com/acm/contest/161/F 题目大意 给出nnn个点的一张图,求它的所有生成树中权值和为kkk的倍数的个数。输出答案对ppp取模 1≤n,k≤100,1≤m≤104,p∈[2,109]∩Pri1\leq n,k\leq 100,1\leq m\leq 10^4,p\in[2,10^9]\cap Pri1≤n,…

【数论】Crash的数字表格 / JZPTAB(P1829)

正题 P1829 题目大意 给出n,m,求∑i1n∑j1mlcm(i,j)\sum_{i1}^n\sum_{j1}^mlcm(i,j)i1∑n​j1∑m​lcm(i,j) 解题思路 ∑i1n∑j1mlcm(i,j)\sum_{i1}^n\sum_{j1}^mlcm(i,j)i1∑n​j1∑m​lcm(i,j) ∑d1nd∑i1n/d∑j1m/di∗j[gcd(i,j)1]\sum_{d1}^nd\sum_{i1}^{n/d}…

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…

CF1137F-Matches Are Not a Child‘s Play【LCT】

正题 题目链接:https://www.luogu.com.cn/problem/CF1137F 题目大意 给出nnn个点的一棵树&#xff0c;第iii个点权值为iii。 一棵树的删除序列定义为每次删除编号最小的叶子并将其加入序列末尾。 要求支持 修改一个点的权值为一个比目前所有权值都要大的一个值询问一个点在…

【数论】数表(P3312)

正题 P3312 题目大意 给出n,m,a&#xff0c;求∑i1n∑j1mσ(gcd(i,j))[σ(gcd(i,j))≤a]\sum_{i1}^n\sum_{j1}^m\sigma(gcd(i,j))[\sigma(gcd(i,j))\leq a]i1∑n​j1∑m​σ(gcd(i,j))[σ(gcd(i,j))≤a] 解题思路 先不考虑a的条件限制 ∑i1n∑j1mσ(gcd(i,j))\sum_{i1}^n\su…

codeforces1452 E. Two Editorials

E. Two Editorials 官方题解 首先将每个参赛者按照区间中点排序&#xff0c;那么前一段参赛者听第一个人&#xff0c;而后一段听第二个人的。 预处理数组su[j]表示j→mj\to mj→m这些参赛者能被“覆盖”最多“贡献” 只需要枚举第一个人演讲的区间&#xff0c;再考虑前多少参赛…

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

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