netty 学习笔记


IO 就是计算机内部与外部进行数据传输的过程,比如网络 IO 与磁盘 IO

所有 IO 都需要系统调用,由操作系统代理执行,并经历从IO设备拷贝到内核空间拷到用户空间的环节(java 中有管理堆外内存的类,是个特例)

在内核收到调用请求之后,会有数据准备、数据就绪、数据拷贝的阶段

常用的 IO 模型

BIO:同步阻塞 IO

同步阻塞 IO 模型中,应用程序某个线程发起 read 调用后,会一直阻塞,直到在内核把数据拷贝到用户空间

在这个模型中,一个线程对应一组操作,这组操作中的阻塞操作与非阻塞操作都会让线程阻塞,这样是非常浪费效率的

java 中的 BIO

传统的 IO 编程

每一个客户端连接都使用一个线程,在这个连接不传输信息的时候也会占用线程,此外所有调用都是阻塞的

        try {
            ServerSocket serverSocket = new ServerSocket(666);
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            //.......
            outputStream.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

NIO:同步非阻塞 IO

上面的代码是一个经典的每连接每线程的模型,之所以使用多线程,主要原因在于 socket.accept()、socket.read()、socket.write() 三个主要函数都是同步阻塞的,当一个连接在处理 IO 的时候,系统是阻塞的,如果是单线程的话必然就挂死在那里;但 CPU 是被释放出来的,开启多线程,就可以让 CPU 去处理更多的事情

现在的多线程一般都使用线程池,可以让线程的创建和回收成本相对较低。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。

不过,这个模型最本质的问题在于,严重依赖于线程。但线程是很”贵”的资源,主要表现在线程的创建和销毁成本很高,并且线程本身占用较大内存,像 Java 的线程栈,一般至少分配512K~1M的空间,如果系统中的线程数过千,恐怕整个 JVM 的内存都会被吃掉一半

有没有一种技术可以降低线程数的同时,保证如此高量的连接请求呢?有的,想想 redis 做的 IO 复用,我们参照这个改改代码就行了

说一下 NIO 的原理,NIO 的主要事件有几个:读就绪、写就绪、有新连接到来。主要的参与对象有几个:处理器、事件选择器

我们首先需要注册当这几个事件到来的时候所对应的处理器。然后在合适的时机告诉事件选择器,我对这个事件感兴趣。对于写操作,就是写不出去的时候对写事件感兴趣;对于读操作,就是完成连接和系统没有办法承载新读入的数据时;对于 accept,一般是服务器刚启动的时候;而对于 connect,一般是 connect 失败需要重连或者直接异步调用 connect 的时候

然后,用一个死循环选择就绪的事件,会执行系统调用,还会阻塞的等待新事件的到来。新事件到来的时候,会在选择器上注册标记位,标示可读、可写或者有连接到来

注意,select 是阻塞的,无论是通过操作系统的通知(epoll)还是不停的轮询(sellect,poll),这个函数是阻塞的。所以你可以放心大胆地在一个 while(true) 里面调用这个函数而不用担心 CPU 空转

java 中的 NIO

java 中的 NIO、Radis 中的单线程、Netty 框架就是 IO 多路复用

传统的 BIO 里面 socket.read(),如果 TCP RecvBuffer 里没有数据,函数会一直阻塞,直到收到数据,返回读到的数据。表现为我们需要传入一个 x 参数给 socket,socket 读取 x 后或者链接关闭了,才会返回

但是对于 NIO,如果 TCP RecvBuffer 有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。这个是给 socket 设置了非阻塞的条件,让其从套接字中,能读多少数据读多少数据,这样的话方法永远不会关闭

java 中的 NIO 通信是 IO 多路复用的一种实现,reactor 模式是其一种实现的思想

使用选择器,缓冲区、通道来实现,通道将用户数据拷贝到缓冲区中,选择器让程序读取缓冲区中数据

java 中一个线程对应一个选择器,一个选择器对应多个连接(通道),选择器选择那个通道是由事件确定的

Buffer 是一个内存块,底层用数组实现,要么是输入状态要么是输出状态,每一个通道都要注册到选择器中,选择器检查是否有事件发生来进行通道的选择

Reactor 模式

又叫分发者模式,反应器模式,通知者模式,这个是高性能 IO 的基石,Reactor 模式又分为三种子模式

单 Reactor 单线程:这个模式下处理连接与业务的只有一个线程,Reactor进行阻塞操作的分离,并将需要线程处理的业务交给Handler进行处理,这种模式可以应对较少客户端连接下快速业务的处理,比如Redis的IO多路复用

单 Reactor 多线程:Reactor 由一个线程控制,将业务交给线程池控制的 Handler 处理,这种模式在多线程情况下容易出现性能瓶颈(因为只有一个 Handler)

主从 Reactor:主 Reactor 负责建立连接,从 Reactor 负责处理连接,Handler 负责处理业务,分为三层处理业务较大的提高了效率,netty 就是使用了这种模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值