I/O与Netty原理

什么是IO?

IO是Input、Output的简称,即输入输出。简单说就是读取数据,然后进行系统调用

一、Java IO模型

1、BIO(Blocking IO)

BIO即同步阻塞模型,每个客户端连接对应一个处理线程,在BIO中,accept和read方法都是阻塞操作,没有连接请求时,accept方法阻塞等待,如果无数据读取时,read方法阻塞。

2、NIO(Non Blocking IO)

        NIO是同步非阻塞模型,与BIO相比引入了多路复用器Selector的概念,服务端一个线程可以处理多个请求,客户端的连接请求注册在一个Selector上,服务端通过轮询Selector查看是否有IO请求。

NIO三大核心组件:

        Buffer:数据传输的载体,底层基于数组实现,针对java 基本类型提供了对应的缓冲区类。ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer,没有 BooleanBuffer。

        Channel:用于数据传输,面向缓冲区进行操作,支持双向传输,数据可以从Channel读取到Buffer中,也可以从Buffer写入到Channel中。

        Selector:多路复用器也叫选择器,当Channel注册近Selector中后,Selector内部通过不断轮询查询Channel是否有已就绪的IO事件,当Channel发生读写事件就会转入就绪状态,selector监听到后通过SelectionKeys可以获取就绪Channel的集合,然后进行后续IO操作。

3、AIO(NIO 2.0)

        AIO是异步非阻塞模型,适用于连接数多且连接时间长的应用,在读写事件完成后由回调服务区通知程序启动线程进行处理。与NIO不同,进行读写操作时,只需直接调用read或write方法,当然这两种方法都是异步的,可以理解为read、write方法都是异步,0完成后主动调用回调函数。

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,获取事件的时候无需遍历整个被监听的描述符集,只需遍历哪些被内核IO事件异步唤醒而加入Ready队列的描述符集。

二、Reactor线程模型

        Reactor模式是基于事件驱动开发的,服务端程序处理传入多路请求并将它们同步分派给对应的请求线程,Reactor模式也叫Dispatcher模式,即IO多路复用统一监听事件,收到事件后分发。

        Reactor以NIO为底层支持,其核心包括Reactor和Handler

                Reactor:Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件做出反应

                Handler:处理程序执行IO事件要完成的实际事件,Reactor通过调度对应的处理程序响应IO事件,Handler执行非阻塞操作。

根据Reactor的数量和Handler线程数量的不同,Reactor可以分为三种模型:        

  • 单线程模型(单Reactor单线程)
  • 多线程模型(单Reactor多线程)
  • 主从多线程模型(多Reactor多线程)

单线程模型

        单线程模型比较简单,Reactor内部通过Selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立的事件则由Acceptor处理,通过accept接受连接,并创建Handler进行处理,如果是读写事件直接调用连接对应的Handler来处理。单线程模型最大的弊端就是它是阻塞的,无法做到高性能,适用于Redis那类快速读写的场景。

多线程模型

        与单线程相比多了一个线程池进行数据处理,单Reactor承担所有事件的监听和响应,Handler只负责响应事件,不进行业务操作(只进行read和write),业务处理交给线程池。

线程池分配一个线程进行业务处理,然后将响应结果交给主进程的Handler,Handler将结果send给client客户端。

主从多线程模型

        多个Reactor,每个Reactor都有自己的Selector选择器,线程和dispatch。主线程中的mainReactor通过自己的Selector监控连接建立事件,收到事件后通过Accpetor接收,将新的连接分配给某个子线程。

        子线程中的subReactor将mainReator分配的连接加入连接队列中通过自己的Selector进行监听,并创建一个Handler处理后续事件。

三、Netty线程模型

Netty线程模型是Reactor模式的一个实现

1、线程组

Netty中抽象了两组线程池BossGroup和WorkerGroup,都是NIOEventLoopGroup类型,BossGroup用来接收客户端发来的连接,WorkerGroup负责具体的处理。

NIOEventLoopGroup包含多个NioEventLoop,管理NioEventLoop的生命周期。每个NioEventLoop中包含一个Nio selector、一个队列,一个线程;线程用来轮询Selector中的Channel的读写事件和对投递到队列里面的事件进行处理。

BossNioEventLoop线程执行步骤:

->1、处理accept事件,与client建立连接,生成NioSocketChannel

->2、将NioSocketChannel注册到WorkerNioEventLoop上的Selector

->3、处理任务队列的任务

WorkerNioEventLoop线程执行步骤:

->1、轮询注册到自己的Selector上的所有NioSockerChannel的read和write事件

->2、处理read和write事件,在对应的NioSocketChannel处理业务

->3、runAllTasks处理任务队列TaskQueue的任务,一些耗时的任务处理可以放到TaskQUeue中慢慢处理,不影响数据在pipeline中的流动处理

public class NettyServer {
    public static void main(String[] args) {
        /*
        * 创建两个NioEventLoopGroup对象,bossGroup用作监听客户端请求,workerGroup处理每条链接的数据读写
        * */
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        
        /*
        * 1、ServerBootstrap 作为引导类引导服务器的启动工作
        * 2、group配置上面两个NioEventLoopGroup对象的角色
        * 3、channel配置服务端的IO模型:NIO模型->NioSocketChannel;
        * 4、chilHandler用于配置每条连接的数据读写和业务逻辑
        * */
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
            .group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) {
                }
            });
        //绑定监听端口
        serverBootstrap.bind(8000);
    }

2、ChannelPipeline

        Netty中将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipeline中流动和传递。ChannelPipeline持有IO事件拦截器(ChannelHandler)的双向链表,由ChannelHandler对IO事件进行拦截和处理,可以方便的新增和删除ChannelHandler来实现不同的业务逻辑定制,实现对修改封闭对扩展开放。

        ChannelPipeline是一系列的ChannelHandler实例,入站和出站事件可以被ChannelPipeline拦截,每一个Channel创建都会绑定一个新的ChannelPipeline。

        一个事件将由ChannelInboundHandler或ChannelOutboundHandler处理,ChannelHandlerContext实现转发或传递给下一个ChannelHandler。ChannelHandler处理程序可以通知下一个ChannelHandler执行。Read(入站事件)和Write(出站事件)使用相同的pipeline,入站事件从链表头往后传递到最后一个入站Handler,出站事件会从链表尾传递到最前一个出站Handler,两种Handler互不干扰。

ChannelInboundHandler回调方法:

  • handlerAdded(): 当业务处理器被加入到流水线后回调
  • channelRegistered(): 当channel成功绑定一个NioEventLoop线程后
  • channelUnregistered():channel和NioEventLoop线程解除绑定
  • channelActive():channel激活时回调
  • channelInactive():channel不活跃时回调
  • channelRead(): channel从远程读取到数据时回调
  • channelReadComplete():read消费完读取的数据后回调
  • channelRemoved():netty移除channel上的所有业务处理器,并回调所有业务处理器的handlerRemoved()

3、异步非阻塞

在使用Netty进行网络通信时,我们发起IO请求后会马上得到返回结果,不会阻塞业务调用线程,即使是获取响应结果也不需要业务调用线程使用阻塞的方式等待,而是通过IO线程异步通知的方式。

        写操作:Netty通过在ChannelPipeline中判断NioSocketChannel的write的调用线程是不是其对应的NioEventLoop中的线程,如果不是则会把写入请求封装为WriteTask投递到期对应的NIOEventLoop中的队列里面,然后等其对应的NioEventLoop中的线程轮询读写事件的时候,从队列中取出并执行。

        读操作:当从NioSocketChannel中读取数据时,NioEventLoop轮询线程发现Selector中有数据就绪,然后事件通知方式通知业务数据已就绪,进行数据读取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值