【Netty】学习笔记(上)

概念

  1. 基于TCP、异步、事件驱动的网络框架;
  2. TCP/IP --> 原生JDK(Java的IO和网络编程) --> NIO --> Netty;

应用场景

  1. Netty是异步高性能的通信框架,往往被RPC框架使用;
  2. 阿里云分布式框架Dubbo;
  3. 游戏;
  4. 地图框架
  5. AVRO(RPC),使用Netty Service进行二次封装;
  6. Akka;
  7. Flink;
  8. Spark;

书籍

  1. 『Netty in Action』实战
  2. 『Netty权威指南』,知识点多,但其基于Netty5,目前已经过时不在维护;

I/O模型

  1. I/O模型决定性能
  2. Java BIO,同步阻塞,造成线程浪费或连接浪费;
  3. Java NIO,同步非阻塞,一个线程处理多个连接(维护一个Selector),并把连接注册到一个选择器中,多路复用模型,事件驱动;后期连接多了就不止一个线程而是多个线程以及多个Selector(BOSS);
  4. Java AIO(NIO2.0),引入异步通道,采用了Proactor模式,目前还没有广泛应用,JDK1.7引入,有效的请求才启动线程,特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用;

场景辨析

BIO

连接数较少,且架构较固定,对服务器资源要求较高,并发局限于应用,JDK 1.4以前的唯一选择;

NIO

连接数目较多连接较短的操作,比如聊天服务器、弹幕系统、服务器之间的通讯,JDK 1.4之后支持;

AIO

连接数目较多且连接较长(重操作)的架构,比如相册服务器,调用OS参与并发操作,JDK 7之后支持;

『Netty基于NIO』

BIO详解

传统的BIO可以通过线程池进行改善,实现多个客户连接服务器。

流程

  1. 启动ServerSocket
  2. 建立一个线程与每个连接进行通讯
  3. 客户发出请求后咨询服务器线程是否有响应,没有会等待或者判断被拒绝
  4. 如果有响应,客户端会等待请求结束后继续执行(阻塞)

NIO详解

同步非阻塞,JDK1.4之后,Channel、Buffer、Selector三个核心组件。面向缓冲区、或者面向块编程的,增加了处理的灵活性。

组件说明

Channel

类似于Socket,数据通道;

Selector

寻找不同的Channel的注册事见以及查看是否由Channel就绪;

Buffer

缓冲区

三大组件的关系

  1. 每个Channel对应一个Buffer
  2. 每个Selector对应一个线程,一个线程对应多个Channel(连接)
  3. 程序切换到哪个Channel由事件决定,Event是一个重要的概念
  4. Selector会根据不同的事件在不同的通道切换
  5. Buffer是一个内存块,底层是一个数组
  6. 数据的读写时是通过Buffer,与BIO不同的是,BIO的读写有分别对应的流,而NIO的Buffer是双向的,但需要通过flip进行切换
  7. Channel是双向的,可以返回底层操作系统的状况,比如Linux,底层的操作系统通道是双向的

Buffer详解

  1. Capacity,容量,创建后不可变
  2. Limit,当前终点,可变
  3. Position,位置,下一个要被读或者写的元素索引,起点
  4. Mark,Mark()调用后记录上一次读操作的位置,reset()后Postition就会恢复到Mark的位置,默认为-1
  5. clear(),标记恢复,但内容还在
  6. flip(),反转操作,读/写
  7. hasRemaining(),类似hasNext(),告知当前位置和限制之间是否有元素
  8. isReadOnly(),判断是否为只读缓冲区

ByteBuffer

  1. allocateDirect(int capacity)//创建直接缓冲区
  2. allocate(int capacity)//设置缓冲区的初始容量
  3. get( );//从当前位置position上get,get之后,position会自动+1
  4. get (int index);//从绝对位置get
  5. put (byte b);//从当前位置上添加,put之后,position会自动+1
  6. put (int index, byte b);//从绝对位置上put

Channel详解

基本介绍

  1. 双工,一个通道既可读,也可写;(stream则是单工的)
  2. 可实现异步读写数据;
  3. 可以从缓冲区读数据,也可以写数据;
  4. 是NIO中的一个接口;
  5. 常见的Channel类:FileChanne、DatagramChannel、ServerSocketChannel 和 SocketChannel;(ServerSocketChanne 类似 ServerSocket , SocketChannel 类似 Socket)
    • ServerSocketChannelImpl:ServerSocketChannel 的实现类
    • SocketChannelImpl:SocketChannel 的实现类
    • FileChanneImpl:FileChanne的实现类

Buffer和Channel使用细节

  1. put和get的类型要对应,否则会抛BufferUnderflowException异常,比如调换了get的顺序(类比序列化的过程!);
  2. 可以把普通的Buffer转成只读Buffer;
  3. MappedByteBuffer,可以让文件直接在内存(堆外内存,指虚拟机外的内存)中修改,而不需要额外拷贝一次;(RandomAccessFile是java提供的对文件类容进行访问的类。即可读也可写)
  4. NIO支持通过多个Buffer完成读写操作(Scattering和Gathering)

Selector详解

  1. selector能够检测注册的通道是否有事件的发生(单线程管理多个通道、一个Selector对应多个Channel)
  2. Selector是一个抽象类,open() 得到一个选择器对象,select(long) 监控所有注册的通道,当其中有IO操作可以进行时,将对应的 SelectionKey 加入到内部集合中并返回
  3. select()阻塞,select(long) 有超时时间
  4. selectKeys()是返回内部集合得所有Key,而select()是监听当中有事件发生所返回得事件selectkeys

流程

  1. 当客户端连接时,会通过ServerSocketChannel 得到 SocketChannel
  2. Selector 进行监听 select 方法, 返回有事件发生的通道的个数.
  3. 将socketChannel注册到Selector上, register(Selector sel, int ops), 一个selector上可以注册多个SocketChannel
  4. 注册后返回一个 SelectionKey, 会和该Selector 关联(集合)
  5. 进一步得到各个 SelectionKey (有事件发生)
  6. 在通过 SelectionKey 反向获取 SocketChannel , 方法 channel()
  7. 可以通过 得到的 channel , 完成业务处理

理解:selector是一个公共的轮询器,selectionKey是轮询中的每个item,他内部能映射到对应的channel,注册则是seletor.add(selectionkey),selector.bind(selectorkey);事件发生则是通过seletor获取selectionkey,然后再获取channel

SelectionKeys解释

channel和selector的注册关系

常用方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传下上传(iB0PttWDLscH-1606353783851)(C:\Users\hasee\AppData\Roaming\Typora\typora-user-images\image-20200811170332773.png)(C:\Users\hasee\AppData\Roaming\Typora\typora-user-images\image-20200811170332773.png)]

零拷贝

  1. 网络编程优化得关键
  2. 常见得零拷贝:mmap(内存映射)、sendFile

传统IO

Ps: DMA拷贝(direct memory access):直接内存拷贝,不使用CPU。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMhziTWE-1606353783854)(C:\Users\hasee\AppData\Roaming\Typora\typora-user-images\image-20200811171230047.png)]

  1. 把硬件的数据做一次DMA拷贝,数据进入内核kernel中
  2. 内核做一次CPU拷贝,数据进入用户buffer中
  3. 用户态下做一次CPU拷贝,数据进入socket buffer中
  4. 之后socket buffer做一次DMA拷贝,数据进入协议栈protocol engine中

传统IO经历了四次拷贝,三次用户态、内核态之间的切换

mmap优化

mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户控件的拷贝次数。

  • 示意图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMOSDM5q-1606353783856)(C:\Users\hasee\AppData\Roaming\Typora\typora-user-images\image-20200811171809699.png)]

三次拷贝,三次用户态、内核态之间的切换

sendFile

Linux 2.1 版本 提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换

  • 示意图:
    在这里插入图片描述

三次拷贝,两次用户态、内核态之间的切换

PS:所谓零拷贝指没有CPU拷贝,是从操作系统角度看的

Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket buffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝,实现真正的零拷贝,但socket buffer还存在的原因是 kernel buffer的部分数据还是需要存到socket buffer中,但可以忽略不计。

  • 示意图:在这里插入图片描述

两次拷贝,两次用户态、内核态切换

再次梳理

  1. 我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有 kernel buffer 有一份数据);
  2. 零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算;
mmap和sendFile的区别
  1. mmap 适合小数据量读写,sendFile 适合大文件传输;
  2. mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝;(Ps: 这里的上下文切换,开始的也算一次
  3. sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区);

transferTo在win中每次只能传递8m,所以要做分段处理

AIO详解

  • JDK 7 引入了 Asynchronous I/O,即 AIO。在进行 I/O 编程中,常用到两种模式:Reactor和 Proactor。Java 的 NIO 就是 Reactor,当有事件触发时,服务器端得到通知,进行相应的处理;
  • AIO 即 NIO2.0,叫做异步不阻塞的 IO。AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用;
  • 目前 AIO 还没有广泛应用,Netty 也是基于NIO, 而不是AIO;
  • 参考:<<Java新一代网络编程模型AIO原理及Linux系统AIO介绍>> http://www.52im.net/thread-306-1-1.html

总结

BIO、NIO、AIO对比:

BIONIOAIO
IO模型同步阻塞同步非阻塞(多路复用)异步非阻塞
编程难度简单复杂复杂
可靠性
吞吐量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小胡同的诗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值