netty基础01-NIO和BIO

参考博客:

https://www.jianshu.com/p/b9f3f6a16911

https://juejin.im/post/6844903728718462990

https://tech.meituan.com/2016/11/04/nio.html(美团技术团队博客)

参考书籍:《Netty实战》

1.几个基本概念

  • NIO 代表非阻塞 I/O(Non-blocking I/O)

  • BIO (blocking I/O)阻塞I/O

  • AIO (Aysnc I/O) 异步I/O

2. 看一下NIO和BIO代码实现

BIO

注: 1.servesocket的accept()方法会一直阻塞到创建一个连接;2.readLine()方法也是阻塞的,直到有回车符结尾字符

  • 1.在获取连接的时候,线程处于阻塞状态;

  • 2.一个线程处理一个请求和响应,新的请求创建新的线程

  • 3.显而易见的问题,需要开支更多的线程,这对服务器CPU和内存限制较多

    • 线程的创建销毁成本

    • 线程的切换成本

    • 对内存的消耗,每个线程栈分配的内存64K-1M

NIO

  interface ChannelHandler{
      void channelReadable(Channel channel);
      void channelWritable(Channel channel);
   }
   class Channel{
     Socket socket;
     Event event;//读,写或者连接
   }

   //IO线程主循环:
   class IoThread extends Thread{
   public void run(){
   Channel channel;
   while(channel=Selector.select()){//选择就绪的事件和对应的连接
      if(channel.event==accept){
         registerNewChannelHandler(channel);//如果是新连接,则注册一个新的读写处理器
      }
      if(channel.event==write){
         getChannelHandler(channel).channelWritable(channel);//如果可以写,则执行写事件
      }
      if(channel.event==read){
          getChannelHandler(channel).channelReadable(channel);//如果可以读,则执行读事件
      }
    }
   }
   Map<Channel,ChannelHandler> handlerMap;//所有channel的对应事件处理器
  }

NIO具备的优点:

  • 通过单线程选择器轮询,遍历socket

  • socket连接成功,通知thread处理任务

1.几种I/O

Java的Selector对于Linux系统来说,有一个致命限制:同一个channel的select不能被并发的调用。因此,如果有多个I/O线程,必须保证:一个socket只能属于一个IoThread,而一个IoThread可以管理多个socket。

  • 最新的AIO(Async I/O)里面会更进一步:不但等待就绪是非阻塞的,就连数据从网卡到内存的过程也是异步的。

  • 在Linux系统上,AIO的底层实现仍使用EPOLL,与NIO相同。这也是为什么Netty使用NIO

  • IO多路复用

    • 类似与非阻塞,只不过轮询不是由用户线程去执行,而是由内核去轮询,内核监听程序监听到数据准备好后,调用内核函数复制数据到用户态

    • IO多路复用至少有两次系统调用,如果只有一个代理对象,性能上是不如前面的IO模型的,但是由于它可以同时监听很多套接字,所以性能比前两者高

3.I/O线程模型

  • select:同步阻塞状态,线性扫描所有监听的文件描述符,不管他们是不是活跃的。有最大数量限制(32位系统1024,64位系统2048)

  • poll:同select,不过数据结构不同,需要分配一个pollfd结构数组,维护在内核中。它没有大小限制,不过需要很多复制操作

  • epoll:用于代替poll和select,没有大小限制。使用一个文件描述符管理多个文件描述符,使用红黑树存储。同时用事件驱动代替了轮询。epoll_ctl中注册的文件描述符在事件触发的时候会通过回调机制激活该文件描述符。epoll_wait便会收到通知。最后,epoll还采用了mmap虚拟内存映射技术减少用户态和内核态数据传输的开销

  • 每次调用 select()或 poll()时,程序都必须传递一个表示所有需要被检查的文件描述符的数据结构到内核,内核检查过描述符后,修改这个数据结构并返回给程序。当检查大量文件描述符时,从用户空间和内核空间来回拷贝这个数据结构将占用较大量的 CPU 时间。

    • 对于 select()来说,必须在每次调用前初始化这个数据结构。

    • 对于 poll()来说,随着待检查的文件描述符数量的增加,传递给内核的数据结构大小也会随之增加。

    • select()或 poll()调用完成后,应用进程必须检查返回的数据结构中的每个元素,以此查明哪个文件描述符处于就绪态了。

4.NIO的优势

  • 事件驱动模型

    • 范式:将任务放在队列中,通过一个线程检测队列中事件状态,来对状态的事件进行回调。
  • 避免多线程

  • 单线程处理多任务

  • 非阻塞I/O,I/O读写不再阻塞,而是返回0

  • 基于block的传输,通常比基于流的传输更高效

  • 更高级的IO函数,zero-copy

  • IO多路复用大大提高了Java网络应用的可伸缩性和实用性

posted @ 2020-08-11 23:34  PerfectLi  阅读(140)  评论(0编辑  收藏  举报