Net Core网络通信
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网络通信的更多相关文章
- 聊聊如何设计千万级吞吐量的.Net Core网络通信!
聊聊如何设计千万级吞吐量的.Net Core网络通信! 作者:大石头 时间:2018-10-26 晚上 20:00 地点:QQ群-1600800 内容:网络通信, 网络库使用方式 网络库设计理念,高性 ...
- .Net Core 中的包、元包与框架(Packages, Metapackages and Frameworks)
包,元包与框架 本文翻译自 Packages, Metapackages and Frameworks. .Net Core 是一种由 NuGet 包组成的平台.一些产品体验受益于代码包的细粒度定义, ...
- Spark Streaming揭秘 Day35 Spark core思考
Spark Streaming揭秘 Day35 Spark core思考 Spark上的子框架,都是后来加上去的.都是在Spark core上完成的,所有框架一切的实现最终还是由Spark core来 ...
- ASP.NET Core 四种释放 IDisposable 对象的方法
本文翻译自<Four ways to dispose IDisposables in ASP.NET Core>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! IDispos ...
- [翻译] .NET Core 2.1 Preview 1 发布
[翻译] .NET Core 2.1 Preview 1 发布 原文: Announcing .NET Core 2.1 Preview 1 今天,我们宣布发布 .NET Core 2.1 Previ ...
- .NET Core 2.1 Preview 2发布 - April 10, 2018
我们今天宣布发布 .NET Core 2.1 Preview 2.这也是我们在接下来的两到三个月内接近最终发布的版本,该版本现已准备好进行广泛的测试.我们希望您有任何反馈意见. ASP.NET Cor ...
- .Net Core 学习笔记1——包、元包、框架
.Net Core 是由NuGet包(package)组成的平台. 一起使用的多个包的集合:元包(Metapackage) package 包 (对应以前的程序集概念) Framework 框架 as ...
- NET Core微服务之路:基于Ocelot的API网关Relay实现--RPC篇
前言 我们都知道,API网关是工作在应用层上网关程序,为何要这样设计呢,而不是将网关程序直接工作在传输层.或者网络层等等更底层的环境呢?让我们先来简单的了解一下TCP/IP的五层模型. (图片 ...
- .NET Core跨平台的奥秘[下篇]:全新的布局
从本质上讲,按照CLI规范设计的.NET从其出生的那一刻就具有跨平台的基因,这与Java别无二致.由于采用了统一的中间语言,微软只需要针对不同的平台设计不同的虚拟机(运行时)就能弥合不同操作系统与处理 ...
随机推荐
- Tomcat 源码分析(转)
本文转自:http://blog.csdn.net/haitao111313/article/category/1179996 Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件 ...
- python继承,判断类型,多态
1.python中继承 如果已经定义了Person类,需要定义新的Student和Teacher类时,可以直接从Person类继承: class Person(object): def __init_ ...
- 20145109 《Java程序设计》第八周学习总结
Chapter 15 API java.util.logging package The constructor of Logger class is protected. If Logger ins ...
- socket IPC(本地套接字 domain)
1.简介 socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络socket也可用于同一台主机的进程间通讯(通 ...
- cocoa应用程序中NSStatusItem的使用
mac上的应用程序除了左上方会有菜单之外,在屏幕的右上方也会有一个图标样的菜单,这个类似于windows上右下角的system tray. 本文讲述如何给自己的应用程序添加一个system tray( ...
- IE6+以上清除浮动普遍方法总结
浮动,CSSfloat属性.学过的人应该知道这个属性,平时用的应该也是很多的.特别是在N栏布局中. 但是我们会经常遇到这样一种情况,前面的元素浮动之后会影响后面的元素,后面的元素需要用清除浮动来消灭前 ...
- scala学习手记31 - Trait
不知道大家对java的接口是如何理解的.在我刚接触到接口这个概念的时候,我将接口理解为一系列规则的集合,认为接口是对类的行为的规范.现在想来,将接口理解为是对类的规范多少有些偏颇,更恰当些的观点应该是 ...
- Kubernetes 在知乎上的应用
从 Mesos 到 Kubernetes 之前的调度框架是基于 Mesos 自研的.采用的语言是 Python.运行了大概两年多的时间了,也一直比较稳定.但随着业务的增长,现有的框架的问题逐渐暴露. ...
- spring3: AOP 之切面实例化模型 ——跟我学spring3
所谓切面实例化模型指何时实例化切面. Spring AOP支持AspectJ的singleton.perthis.pertarget实例化模型(目前不支持percflow.percflowbelow ...
- 分享知识-快乐自己:Ajax 跨域请求处理
<%-- Created by IntelliJ IDEA. User: asus Date: 2019/1/24 Time: 15:57 To change this template use ...