Net Core网络通信

https://www.cnblogs.com/xxred/p/9859893.html

聊聊如何设计千万级吞吐量的.Net Core网络通信!
作者:大石头
时间:2018-10-26 晚上 20:00
地点:QQ群-1600800
内容:网络通信,
网络库使用方式
网络库设计理念,高性能要点
介绍
首先看下面这张很具有代表性的图,2018年5月份做的测试。当时单服务器得到 2256tps(Transactions Per Second,每秒事务数) 的吞吐率。这次测试只是说明一个问题,.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作为客户端

在客户端连接服务器和给服务端发送数据的时候,分别触发Start和OnReceive方法,连接之后服务端发送了Welcome 的消息,客户端发送5次“你好”。服务端回传收到的数据,打了一个日志,把收到的信息转成字符串输出到控制台。
NetServer是应用级网络服务器,支持tcp/udp/ipv4/ipv6。上面可以看到,同时监听了四个端口。
码神工具也可以连接上来

解释
对于网络会话来说,最关键的就是客户端连上来,以及收到数据包,这两部分,对应上面Start和OnReceive两个方法

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

长连接、心跳第二节设计理念再讲。
客户端
跟很多网络库不同,NewLife.Net除了服务端,还封装了客户端。客户端的核心,也就是Send函数和Received事件,同步发送,异步接收。

因为是长连接,所以服务端随时可以向客户端发送数据包,客户端也可以收到。tcp在不做设置的时候,默认长连接2小时。
NetServer默认20分钟,在没有心跳的时候,20分钟没有数据包往来,服务端会干掉这个会话。
虽然上面讲的NetServer和Client,都是tcp,但是换成其它协议也是可以的。这里的NetServer和NetUri.CreateRemote,同时支持Tcp/Udp/IPv4/IPv6等,CreateRemote内部,就是根据地址的不同,去new不同的客户端。所以我们写的代码,根本不在意用的是tcp还是udp,或者IPv6。有兴趣的可以看看源码
1.2 构建可靠网络服务
相关博客
要真正形成一个网络服务,那得稳定可靠。上面例程EchoTest只是简单演示,接下来看下一个例程EchoAgent。

安装运行
这是一个标准的Windows服务,有了这个东西,我们就可以妥妥的注册到Windows里面去。这也是目前我们大量数据分析程序的必备。
首先运行EchoAgent,按2,安装注册服务,用管理员身份运行。安装成功然后可以在服务里面找到刚刚安装的服务。

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

这时候按3,启动服务

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

必须有这个东西,你的网络服务程序,才有可能达到产品级。linux上直接控制台,上nohup,当然还有很多其它办法。以后希望这个XAgent能够支持linux吧,这样就一劳永逸了
1.3 压测
相关博客
只需要记住一个两个数字,.net应用打出来2266万tps,流量峰值4.5Gbps
两千万吞吐量的数字,当然,只能看不能用。因为服务端只是刚才的Echo而已,并没有带什么业务。实际工作中,带着业务和数据库,能跑到10万已经非常非常牛逼了。
我们工作中的服务可以跑到100万,但是我不敢,怕它不小心就崩了。所以我们都是按照10万的上限来设计,不够就堆服务器好了,达到5万以上后,稳定性更重要

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

1.4 网络编程的坎——粘包
普遍情况,上万的程序员,会写网络程序的不到20%,会解决粘包问题的不到1%,如果大家会写网络程序,并且能解决粘包,那么至少已经达到了网络编程的中级水平。
什么是粘包
举个栗子:客户端连续发了5个包,服务端就收到了一个大包。代码就不演示了,把第一个例程的这个睡眠去掉。

客户端连续发了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

每次判断长度,接收一个或多个包,如果接收不完,留下,存起来。等下一个包到来的时候,拼凑完整。
虽然tcp确保数据不丢,但是难免我们自己失手,弄丢了一点点数据。为了避免祸害后面所有包,就需要进行特殊处理了。
每个数据帧,自己把头部长度和数据体凑一起发送啊,tcp确保顺序。这里我们把超时时间设置为3~5秒,每次凑包,如果发现上次有残留,并且超时了,那么就扔了它,省得祸害后面。

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

如果客户端发送太频繁,服务端tcp缓冲区阻塞,发送窗口会逐步缩小到0,不再接受客户端数据。
1.5 .NetCore版RPC框架
NetCore版RPC框架NewLife.ApiServer。

先看看这个效果

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

服务端
有没有发现,这个ApiServer跟前面的NetServer有点像?其实ApiServer内部就有一个NetServer

这么些行代码,就几个地方有价值,一个是注册了两个控制器。你可以直接理解为Mvc的控制器,只不过我们没有路由管理系统,直接手工注册。
第二个是指定编码器为Json,用Json传输参数和返回值。其实内部默认就是Json,可以不用指定
看看我们的控制器,特别像Mvc,只不过这里的Controller没有基类,各个Action返回值不是ActionResult,是的,ApiServer就是一个按照Mvc风格设置的RPC框架

返回复杂对象

做请求预处理,甚至拦截异常

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

客户端
客户端是ApiClient,这里的MyClient继承自ApiClient

这些就是我们刚才客户端远程调用的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 的方式。
网络库的几个精髓文件

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

所有对象,不管客户端服务端,都实现ISocket。然后客户端Client,服务端Server+Session。tcp+udp同时支持并不难,因为它们都基于Socket。
目前无状态无会话的通信架构,做不到高性能。我们就是依靠长连接以及合并小包,实现超高吞吐量
一般灵活性和高性能都是互相矛盾的
2.2 高性能设计要点
第一要点:同步发送,因为要做发送队列、拆分、合并,等等,异步发送大大增加了复杂度。大家如果将来遇到诡异的40ms延迟,非常可能就是tcp的nodelay作怪,可以设为true解决
第二要点:IOCP,高吞吐率的服务端,一定是异步接收,而不是多线程同步。当然,可以指定若干个线程去select,也就是Linux里面常见的poll,那个不在这里讨论,Windows极少人这么干,大量资料表明,iocp更厉害。

SAEA是.net/.netcore当下最流行的网络架构,我们可以通俗理解为,把这个缓冲区送给操作系统内核,待会有数据到来的时候,直接放在里面,这样子就减少了一次内核态到用户态的拷贝过程。
我们测试4.5Gbps,除以8,大概是 540M字节,这个拷贝成本极高
第三要点:零拷贝ZeroCopy,这也是netty的核心优势。iocp是为了减少内核态到用户态的拷贝,zerocopy进一步把这个数据交给用户层,不用拷贝了。
数据处理,我们采用了链式管道,

这些都是管道的编码器

第四要点:合并小包,NoDelay=false,允许tcp合并小包,MTU=1500,除了头部,一般是1472
第五要点:二进制序列化,消息报文尽可能短小,每个包1k,对于100Mbps,也就12M,理论上最多12000包,所以大量Json协议或者字符串协议,吞吐量都在1万上下
SRMP头部4字节,ApiServer的消息报文,一般二三十个字节,甚至十几个字节
第五要点:批量操作,User FindByID(int id); User[] FindByIDs(int[] ids);

Net Core网络通信的更多相关文章

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

    聊聊如何设计千万级吞吐量的.Net Core网络通信! 作者:大石头 时间:2018-10-26 晚上 20:00 地点:QQ群-1600800 内容:网络通信, 网络库使用方式 网络库设计理念,高性 ...

  2. .Net Core 中的包、元包与框架(Packages, Metapackages and Frameworks)

    包,元包与框架 本文翻译自 Packages, Metapackages and Frameworks. .Net Core 是一种由 NuGet 包组成的平台.一些产品体验受益于代码包的细粒度定义, ...

  3. Spark Streaming揭秘 Day35 Spark core思考

    Spark Streaming揭秘 Day35 Spark core思考 Spark上的子框架,都是后来加上去的.都是在Spark core上完成的,所有框架一切的实现最终还是由Spark core来 ...

  4. ASP.NET Core 四种释放 IDisposable 对象的方法

    本文翻译自<Four ways to dispose IDisposables in ASP.NET Core>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! IDispos ...

  5. [翻译] .NET Core 2.1 Preview 1 发布

    [翻译] .NET Core 2.1 Preview 1 发布 原文: Announcing .NET Core 2.1 Preview 1 今天,我们宣布发布 .NET Core 2.1 Previ ...

  6. .NET Core 2.1 Preview 2发布 - April 10, 2018

    我们今天宣布发布 .NET Core 2.1 Preview 2.这也是我们在接下来的两到三个月内接近最终发布的版本,该版本现已准备好进行广泛的测试.我们希望您有任何反馈意见. ASP.NET Cor ...

  7. .Net Core 学习笔记1——包、元包、框架

    .Net Core 是由NuGet包(package)组成的平台. 一起使用的多个包的集合:元包(Metapackage) package 包 (对应以前的程序集概念) Framework 框架 as ...

  8. NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇

    前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型.     (图片 ...

  9. .NET Core跨平台的奥秘[下篇]:全新的布局

    从本质上讲,按照CLI规范设计的.NET从其出生的那一刻就具有跨平台的基因,这与Java别无二致.由于采用了统一的中间语言,微软只需要针对不同的平台设计不同的虚拟机(运行时)就能弥合不同操作系统与处理 ...

随机推荐

  1. Tomcat 源码分析(转)

    本文转自:http://blog.csdn.net/haitao111313/article/category/1179996 Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件 ...

  2. python继承,判断类型,多态

    1.python中继承 如果已经定义了Person类,需要定义新的Student和Teacher类时,可以直接从Person类继承: class Person(object): def __init_ ...

  3. 20145109 《Java程序设计》第八周学习总结

    Chapter 15 API java.util.logging package The constructor of Logger class is protected. If Logger ins ...

  4. socket IPC(本地套接字 domain)

    1.简介 socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络socket也可用于同一台主机的进程间通讯(通 ...

  5. cocoa应用程序中NSStatusItem的使用

    mac上的应用程序除了左上方会有菜单之外,在屏幕的右上方也会有一个图标样的菜单,这个类似于windows上右下角的system tray. 本文讲述如何给自己的应用程序添加一个system tray( ...

  6. IE6+以上清除浮动普遍方法总结

    浮动,CSSfloat属性.学过的人应该知道这个属性,平时用的应该也是很多的.特别是在N栏布局中. 但是我们会经常遇到这样一种情况,前面的元素浮动之后会影响后面的元素,后面的元素需要用清除浮动来消灭前 ...

  7. scala学习手记31 - Trait

    不知道大家对java的接口是如何理解的.在我刚接触到接口这个概念的时候,我将接口理解为一系列规则的集合,认为接口是对类的行为的规范.现在想来,将接口理解为是对类的规范多少有些偏颇,更恰当些的观点应该是 ...

  8. Kubernetes 在知乎上的应用

    从 Mesos 到 Kubernetes 之前的调度框架是基于 Mesos 自研的.采用的语言是 Python.运行了大概两年多的时间了,也一直比较稳定.但随着业务的增长,现有的框架的问题逐渐暴露. ...

  9. spring3: AOP 之切面实例化模型 ——跟我学spring3

    所谓切面实例化模型指何时实例化切面. Spring AOP支持AspectJ的singleton.perthis.pertarget实例化模型(目前不支持percflow.percflowbelow ...

  10. 分享知识-快乐自己:Ajax 跨域请求处理

    <%-- Created by IntelliJ IDEA. User: asus Date: 2019/1/24 Time: 15:57 To change this template use ...