网络编程与高效IO
2021-02-25 18:18:15 15 举报
AI智能生成
网络IO编程,高效IO, Netty原理, 网络协议
作者其他创作
大纲/内容
网络协议
网络模型
TCP和UDP的区别
UDP
1. 无连接协议, 无需握手
2. 除了1对1单播通信, 还可以1对多广播通信
3. 面向报文
TCP
1. 面向连接, <font color="#c41230">见面3次握手, 断开4次挥手</font>
3次握手
1. C端发起<font color="#c41230">syn</font>请求
2. S端回复<font color="#c41230">ack+syn</font>请求
3. C端回复<font color="#c41230">ack</font>请求, 这时C端的TCP通道estalished
4. S端收到B端回复, 这时S端的TCP通道estalished
<font color="#0076b3">为什么S-C需要握手3次, 而不是2次?</font>
4次挥手
1. C端发起fin请求
2. S端回复ack请求
3. S端信息业务继续发送中
<font color="#0076b3">为什么S-C需要挥手4次, 而不是3次?</font>
4. S端发送fin请求
5. C端回复ack请求, S端通道CLOSED, C端等待2MSL后CLOSED
2. 仅支持1对1单播
3. 面向<font color="#c41230">字节流</font>
4. 可靠传输
序列号
检验和
确认应答信号
重发控制
窗口控制
拥塞控制
对比
TCP中拆包黏包现象
1、将消息分为<font color="#c41230">头部和消息体</font>,<font color="#c41230">在头部中保存有当前整个消息的长度</font>
2、可以在数据包之间<font color="#c41230">设置边界</font>,如添加特殊符号
3. 消息定长
网络IO
IO
两个阶段
1. 等待数据准备阶段 - 等待数据从socket中到达(recvfrom函数),到达后需要复制到内核的缓冲区中
2. 从内核的缓冲区复制到进程的缓冲区,复制到进程的缓冲区才算读取完毕
IO演进
阻塞IO
一个线程只能<font color="#c41230">同时处理一个</font>socket通道
<font color="#c41230">等待数据准备阶段]</font> 和 <font color="#c41230">[线程read]</font> 都会会阻塞线程
缺点
每个请求都需要开启线程,并且阻塞不能干其他事情,比较耗费资源<br>
非阻塞IO
一个线程能<font color="#c41230">同时处理多个</font>socket通道
socket请求的<font color="#c41230">[等待数据准备阶段]</font>,进程不会阻塞,会快速返回结果(返回error的话,会过一段时间再发送recvfrom请求, 期间可以做其他事情).
缺点
当有大量客户端连接时,每次判断fd状态都需要从用户态切换到内核态,<br>即10k个连接需要循环10k次系统调用,费时费力
多路复用
select O(n)
在非阻塞IO基础上,改进10k次系统调用为1次系统调用
缺点
每次系统调用都需要<font color="#c41230">传10k个fd</font>
实际上是<font color="#c41230">由用户循环10k次改为了内核循环10k次</font>,同样是O(n)复杂度
32位机器上,源码中写死了最多接受的fd为1024个,容易成为瓶颈
poll O(n)
在select基础上,改进了1024个的限制
缺点
除1024缺陷外,其余都在
epoll O(1)
在poll基础上实现: <br>1. 在内核开辟一个缓存区,将监听的<font color="#c41230">fd注册到缓存区</font>中, 并且记录下fd监听的事件类型<br>2. 当fd的监听事件发生了, 便把fd加入对应的事件<font color="#c41230">就绪列表</font><br>3. 轮询就绪队列, 触发回调通知用户进程
10k个fd不再循环10k次,而实现了<font color="#c41230">事件机制</font>
缺点
每次读和写的时候,都是由用户线程自己从内核buffer读
异步IO
在epoll基础上,改进读写的时候由内核完成,用户线程<font color="#c41230">只需要提供读写完成后的回调方法</font>即可
目前linux系统还没有成熟的商用异步IO
C10K问题
http://www.kegel.com/c10k.html
netty
netty是一款基于NIO开发的网络通信框架.
优势
使用简单
功能强大
定制能力强
性能高
IO 线程模型
<font color="#c41230">主从Reactor多线程模型</font>
<font color="#c41230">内存零拷贝</font>
1. netty接收和发送ByteBuffe采用<font color="#c41230">堆外直接内存</font>, 避免从堆内存中复制到直接内存的步骤
2. 提供了<font color="#c41230">组合ByteBuffer对象</font>, 避免传统对象拼接时的内存拷贝
3. Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题
内存池设计
串形化处理读写
高性能序列化协议
主要组件
Channel
EventLoop
ChannelFuture
ChannelHandler
ChannelPipeline
Reactor线程模型
<font color="#0076b3">Reactor模型基于异步通知模式, 即有事情发生了, 我就通知你去处理</font>
Reactor
负责消息监听及事件响应,将事件分发给绑定了该事件的Handler处理<br>
假如是<font color="#c41230">连接事件</font>, 则交给<font color="#c41230">acceptor</font>处理
假如是<font color="#c41230">读写事件</font>, 则交给<font color="#c41230">handler</font>处理
Acceptor
<font color="#c41230">Handler的一种,绑定了connect事件.</font><br>注册channel到Reactor的selector中, 并创建对应的handlerPipe<br>
Handler
<font color="#c41230">事件处理器,绑定了某类事件.</font><br>完成channel的消息读取, 完成业务逻辑, 完成消息的发送<br>
单Reactor单线程模型
<font color="#c41230">1个主线程</font>
<font color="#c41230">Reactor和Handlers在同一个线程内</font>
1个Reactor
负责消息的监听, 及消息处理的分发<br>dispatch<br>
N个Handler
负责事件的响应处理<br>accept、read、decode、process、encode、send<br>
缺点
不能利用多核CPU
高并发时性能上无法支撑
一旦reactor线程意外跑飞或者进入死循环,会导致整个系统通信模块不可用
应用: redis
单Reactor多线程模型
<font color="#c41230">1个主线程+1个worker线程池</font>
1个Reactor<font color="#c41230">(主线程内)</font>
负责消息的监听, 及消息处理的分发<br>dispatch<br>
N个Handler<font color="#c41230">(主线程内)</font>
负责响应事件,不做具体的业务处理<br>read+send
worker线程池
具体的业务处理<br>decode+compute+encode
特点
1. 相对于单reactor单线程, 将具体的业务处理交给worker线程池处理, 提高了cpu的利用率
2. 目前的问题是, 在handler读取时仍会阻塞线程
主从Reactor多线程模型
Nginx, Netty, Memcached 都是应用该模式
<font color="#c41230">MainReactor线程池+SubReactor线程池+worker线程池</font>
MainReactor
监听连接事件,收到事件后,通过Acceptor处理连接事件<br>accept
SubReactor
连接事件处理完后, MainReactor将连接移交给SubReactor监听<br>接下来的各种事件处理由SubReactor去完成<br>read+send
worker
具体的业务处理<br>decode+compute+encode<br>
特点
<font color="#c41230">1. MainReactor只负责连接事件的处理+移交</font>
2. SubReactor负责事件的读写响应及消息处理的分发
3. worker线程负责具体的业务处理
优点
响应快,不必为单个同步事件所阻塞,虽然Reactor本身依然是同步的
可以最大程度的避免复杂的多线程的同步问题,并且避免了多线程/进程的切换开销<br>
扩展性好,可以方便的通过增加Reactor实例个数来充分利用CPU资源<br>
复用性好,<font color="#c41230">Reactor模型本身与处理逻辑无关</font>,具有很高的复用性<br>
小结
1. 单Reactor单线程,前台接待员和服务员是同一个人,全程为顾客服务
2. 单Reactor多线程,1个前台接待员,多个服务员,接待员只负责接待
3. 主从Reactor多线程,多个前台接待员,多个服务生
netty模型
NIOLoopGroup
NIOEventLoop表示<font color="#c41230">一个不断循环的执行处理任务的线程</font>, <br>每个NIOEventLoop都有一个Selector,用于监听绑定在其上的socket网络通讯<br>
NIOEventLoopGroup相当于一个事件循环组,<br>这个组中含有多个事件循环,每一个事件循环是NIOEventLoop<br>
NIOEventLoopGroup可以有多个线程,即可以含有多个NIOEventLoop
Netty抽象出两组线程池
<font color="#c41230">BossGroup和WorkerGroup类型都是NIOEventLoopGroup</font>
BossGroup:专门负责接收客户端的连接
1:轮询<font color="#c41230">Accept</font>事件
2:处理Accept事件,与Client建立连接,生成NIOSocketChannel,并将其注册到某个<br> Worker NIOEventLoop上的selector
3:再去处理任务队列的任务,即runAllTasks
WorkerGroup:专门负责网络的读写
1:轮询<font color="#c41230">read,write</font>事件
2:处理IO事件,即read write,在NIOSocketChannel处理
3:处理任务队列的任务,即runAllTasks
<font color="#c41230">每个Worker NIOEventLoop 要处理数据的话,是通过PipeLine(管道)执行的</font>
0 条评论
下一页