Netty与NIO超详细讲解

作者:kaico2018 时间:2022-08-16 00:44:37 

Linux下的五种I/O模型

1)阻塞I/O(blocking I/O)

2)非阻塞I/O (nonblocking I/O)

3) I/O复用(select 和poll) (I/O multiplexing)

4)信号驱动I/O (signal driven I/O (SIGIO))

5)异步I/O (asynchronous I/O (the POSIX aio_functions))

前面四种都是同步io、第五种是异步IO;

阻塞式:当程序没有获取到数据的时候,整个应用可能会产生阻塞,放弃了CPU执行,无法去做其他事情。

非阻塞式:不管有没有获取到数据,都必须立马返回一个结果,如果没有获取到数据的情况下返回一个错误标记,根据错误的标记不断轮询。BIO属于阻塞式的io操作。可以使用多线程实现异步I0,同时处理多个请求。缺点:非常消耗服务器资源CPU

阻塞IO的流程

BIO属于阻塞式的io操作。

可以使用多线程实现异步IO,同时处理多个请求。缺点:非常消耗服务器资源CPU

Netty与NIO超详细讲解

当我们在调用一个io函数的时候,如果没有获取到数据的情况下,那么就会一直等待;等待的过程中会导致整个应用程序一直是一个阻塞的过程,无法去做其他的实现。

IO复用

IO复用机制:IO实际指的就是网络的IO、多路也就是多个不同的tcp连接;复用也就是指使用同一个线程合并处理多个不同的IO操作,这样的话可以减少CPU资源。

信号驱动I/O

发出一个请求实现观察监听,当有数据的时候直接走我们异步回调;

异步IO

异步io也就是发出请求数据之后,剩下的事情完全实现异步完成

同步与异步

站在多线程的角度总结:

同步整个应用代码执行顺序是从上往下执行 并且返回到结果;

异步:开启多个不同的分支实现并行执行 每个线程互不影响;

站在web项目角度分析

默认的情况Http请求就是一个同步形式实现调用 基于请求与响应,如果我们响应非常耗时的话,会导致客户端一直等待(用户体验非常不好)

NIO

Java的nio是在Jdk1.4版本之后推出了一套新的io方案,这种io方案对原有io做了一次性能上的升级

NIO翻译成英文 no blocking io 简称为 nio 非阻塞io,不是new io。

比传统的io支持了面向缓冲区、基于通道实现的io的方案。

BIO 与 NIO 区别:

Bio是一个阻塞式的io,它是西向与流传输也就是根据每个字节实现传输效率非常低,

而我们的nio是雨向与缓冲区的非阻塞边.其中最大的亮点:运多路复用机制,

I0多路复用

多路实际上指的是多个不同的 tcp 连接。

复用:一个线程可以维护多个不同的io操作。

优点:占用cpu资源非常小、保证线程安全问题。

IO 多路复用实现原理

使用一个Java案例来描述IO多路复用的思路:

public class SocketNioTcpServer {
   private static List<SocketChannel> listSocketChannel = new ArrayList<>();
   private static ByteBuffer byteBuffer = ByteBuffer.allocate(512);
   public static void main(String[] args) {
       try {
           // 1.创建一个ServerSocketChannel
           ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
           // 2. 绑定地址
           ServerSocketChannel bind = serverSocketChannel.bind(new InetSocketAddress(8080));
           serverSocketChannel.configureBlocking(false);
           while (true) {
               SocketChannel socketChannel = serverSocketChannel.accept();
               if (socketChannel != null) {
                   socketChannel.configureBlocking(false);
                   listSocketChannel.add(socketChannel);
               }
               for (SocketChannel scl : listSocketChannel) {
                   try {
                       int read = scl.read(byteBuffer);
                       if (read > 0) {
                           byteBuffer.flip();
                           Charset charset = Charset.forName("UTF-8");
                           String receiveText = charset.newDecoder().decode
                                   (byteBuffer.asReadOnlyBuffer()).toString();
                           System.out.println("receiveText:" + receiveText);
                       }
                   } catch (Exception e) {
                       e.printStackTrace();
                   }
               }
           }
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
}

NIO核心组件

通道(Channel)

通常我们nio所有的操作都是通过通道开始的,所有的通道都会注册到统一个选择器(Selector)上实现管理,在通过选择器将数据统一写入到 buffer中。

缓冲区(Buffer)

Buffer本质上就是一块内存区,可以用来读取数据,也就先将数据写入到缓冲区中、在统一的写入到硬盘上。

选择器(Selector)

Selector可以称做为选择器,也可以把它叫做多路复用器,可以在单线程的情况下可以去维护多个Channel,也可以去维护多个连接;

使用Java原生API实现NIO操作

public class NIOServer {
   /**
    * 创建一个选择器
    */
   private Selector selector;
   public void initServer(int port) throws IOException {
       // 获得一个ServerSocketChannel通道
       ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
       // 设置通道为非阻塞
       serverSocketChannel.configureBlocking(false);
       // 将该通道对应的ServerSocket绑定到port端口
       serverSocketChannel.bind(new InetSocketAddress(port));
       // 获得一个通道管理器
       this.selector = Selector.open();
       // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
       // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
       serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
   }
   public void listen() throws IOException {
       System.out.println("服务端启动成功!");
       // 轮询访问selector
       while (true) {
           // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
           int select = selector.select();
           if (select == 0) {
               continue;
           }
           // 获得selector中选中的项的迭代器,选中的项为注册的事件
           Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
           while (ite.hasNext()) {
               SelectionKey key = (SelectionKey) ite.next();
               // 删除已选的key,以防重复处理
               ite.remove();

if (key.isAcceptable()) {// 客户端请求连接事件
                   ServerSocketChannel server = (ServerSocketChannel) key.channel();
                   // 获得和客户端连接的通道
                   SocketChannel channel = server.accept();
                   // 设置成非阻塞
                   channel.configureBlocking(false);
                   // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
                   channel.register(this.selector, SelectionKey.OP_READ);

} else if (key.isReadable()) {// 获得了可读的事件
                   read(key);
               }

}

}
   }
   public void read(SelectionKey key) throws IOException {
       // 服务器可读取消息:得到事件发生的Socket通道
       SocketChannel channel = (SocketChannel) key.channel();
       // 创建读取的缓冲区
       ByteBuffer buffer = ByteBuffer.allocate(512);
       channel.read(buffer);
       byte[] data = buffer.array();
       String msg = new String(data).trim();
       System.out.println("服务端收到信息:" + msg);
       ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));
       channel.write(outBuffer);// 将消息回送给客户端
   }
   public static void main(String[] args) throws IOException {
       NIOServer server = new NIOServer();
       server.initServer(8000);
       server.listen();
   }
}

Redis为什么支持高并发

Redis官方没有windows版本redis,只有linux版本的reids。

Redis的底层是采用nio 多路io复用机制实现对多个不同的连接(tcp)实现io的复用;能够非常好的支持高并发,同时能够先天性支持线程安全的问题。为什么现场安全?因为使用一个线程维护多个不同的io操作 原理使用nio的选择器,将多个不同的Channel统一交给我们的selector(选择器管理)。

但是nio的实现在不同的操作系统上存在差别:在我们windows操作系统上使用 select 实现轮训机制、在linux操作系统使用epoll

备注:windows操作系统是没有epoll

在windows操作系统中使用select实现轮训机制时间复杂度是为 o(n),而且这种情况也会存在空轮训的情况,效率非常低、其次默认对我们的轮训有一定限制,所以这样的话很难支持上万tcp连接。

所以在这时候linux操作就出现epoll实现事件驱动回调形式通知,不会存在空轮训的情况,只是对活跃的socket实现主动回调,这样的性能有很大的提升 所以时间复杂度为是o(1)

注意:windows操作系统没有epoll、只有linux操作系统有。

所以为什么Nginx、redis能够支持非常高的并发 最终都是靠的linux版本的 io 多路复用机制epoll

来源:https://blog.csdn.net/weixin_44044929/article/details/123893420

标签:Netty,NIO,模型,原理
0
投稿

猜你喜欢

  • Android编程开发之EditText中inputType属性小结

    2022-04-13 10:03:11
  • 详解如何在C#中使用投影(Projection)

    2023-03-28 09:14:15
  • android阅读器长按选择文字功能实现代码

    2023-09-16 08:48:42
  • SpringBoot使用@ResponseBody返回图片的实现

    2023-11-28 04:41:24
  • Android开发中使用Intent打开第三方应用及验证可用性的方法详解

    2021-08-12 19:28:47
  • Android 广播大全 Intent Action 事件详解

    2021-09-10 12:40:15
  • Unity排行榜优化滚动效果

    2021-09-08 13:02:07
  • java打印指定年月的日历

    2023-11-11 19:21:19
  • Ajax登录验证实现代码

    2022-02-22 10:47:51
  • Unity3D Shader实现贴图切换效果

    2021-07-10 00:31:20
  • Java基于Calendar类输出指定年份和月份的日历代码实例

    2023-09-20 07:40:57
  • spring boot下 500 404 错误页面处理的方法

    2021-09-03 07:12:36
  • JDK动态代理,代理接口没有实现类,实现动态代理方式

    2021-12-21 11:55:22
  • Mybatis中注入执行sql查询、更新、新增及建表语句案例代码

    2023-03-07 18:53:55
  • flutter material widget组件之信息展示组件使用详解

    2023-06-22 08:45:35
  • java的Builder原理和实现详解

    2023-09-11 22:46:01
  • C# WinForm程序处理后台繁忙导致前台控件假死现象解决方法

    2021-09-02 12:22:44
  • Android实现仪表盘效果

    2021-07-26 13:19:37
  • Flutter的键值存储数据库使用示例详解

    2023-10-15 02:13:15
  • 详解SpringMVC @RequestBody接收Json对象字符串

    2022-03-20 05:00:10
  • asp之家 软件编程 m.aspxhome.com