从IO模型到Netty笔记(二)

 

目录

从代码层面对NIO大致流程加深印象

SelectionKey

 ServerSocketChannel和SocketChannel

 零拷贝

为什么要有Netty

传统阻塞IO服务模型

Reactor模式

Netty模型工作原理


从IO模型到Netty笔记(一)_运气不好努力来凑-CSDN博客IO模型,NIO三大核心https://blog.csdn.net/wai_58934/article/details/123145720?spm=1001.2014.3001.5501

从代码层面对NIO大致流程加深印象

服务端

public class ProcessNio {
    public static void main(String[] args) throws IOException {
        //serverSocketChannel -----> SocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 得到selector
        Selector selector = Selector.open();
        // 绑定端口监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 把serverSocketChannel注册到selector,事件为OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 等待客户端连接
        while(true){
            //等待一秒,看是否有连接事件发生
            if(selector.select(1000)==0){
                System.out.println("等待一秒无事件发生");
                continue;
            }
            //selector.select大于0,则返回SelectionKey,拿到有事件的集合
            Set selectionKeys = selector.selectedKeys();
            // 遍历事件
            Iterator iterator = selectionKeys.iterator();
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                // 判断key是什么事件
                if(key.isAcceptable()){
                    // 为这个连接的客户端生成一个SocketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    // 注册到selector上并且声明buffer,关注OP_READ
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if(key.isReadable()){
                    // 通过key获取到channel
                    SocketChannel channel = (SocketChannel)key.channel();
                    // 获取到key的buffer
                    ByteBuffer buffer = (ByteBuffer)key.attachment();
                    channel.read(buffer);
                    System.out.println("收到的数据为:"+new String(buffer.array()));
                }
                iterator.remove();
            }
        }
    }
}

客户端

public class ClientNio {
    public static void main(String[] args) throws IOException {
        // 得到一个通道
        SocketChannel channel = SocketChannel.open();
        channel.configureBlocking(false);
        channel.connect(new InetSocketAddress("127.0.0.1",6666));
        if(!channel.isConnected()){
            while (!channel.finishConnect()){
                System.out.println("客户端连接需要时间,但是不会阻塞,可以做其他操作");
            }
        }
        String str = "少壮不努力,老大徒伤悲";
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        channel.write(buffer);
        System.in.read();
    }
}

SelectionKey

表示selector和channel的注册关系。

从IO模型到Netty笔记(二)_第1张图片

 可以查看注册到selector的key和有事件发生的key,方法如下

        selector.keys(); //全部
        selector.selectedKeys(); //有事件发生

其他常用相关方法

从IO模型到Netty笔记(二)_第2张图片

 ServerSocketChannel和SocketChannel

ServerSocketChannel在服务端监听新的客户端的连接。

从IO模型到Netty笔记(二)_第3张图片

 SocketChannel网络IO通道具体负责进行读写操作。

从IO模型到Netty笔记(二)_第4张图片

 零拷贝

优势:更少的上下文切换,更少的cpu缓存伪共享以及无cpu校验和计算。

从操作系统层面来说,内核数据只有一份(比如说不拷贝到socket buffer)就是零拷贝

从四次拷贝变为两次拷贝

四次拷贝是指:硬件数据进行DMA拷贝到内核,从内核缓冲拷贝(cpu拷贝)到用户缓冲,从用户缓冲拷贝(cpu拷贝)到socket缓冲,最后用DMA拷贝到协议栈

两次拷贝是指:从硬件驱动到内核缓冲的DMA拷贝(其实内核缓冲还需要将描述信息拷贝到socket缓冲,但由于数据量不大,所以忽略),从内核缓冲到协议栈的DMA拷贝

从IO模型到Netty笔记(二)_第5张图片

 在方法transferTo()中就用到了零拷贝

为什么要有Netty

也就是说NIO有什么不好~

  1. NIO的类库和API繁杂
  2. 需要具有额外的线程和网络编程的能力才能写出很好的NIO程序
  3. 过程繁杂,需要对断线重连,失败缓存等问题做处理
  4. Selector的空轮询导致CPU爆满

传统阻塞IO服务模型

每个请求需要独立的线程处理。并发量大时,占用很大的系统资源

采用阻塞IO的方式,比如read,去造成线程资源浪费

Reactor模式

从IO模型到Netty笔记(二)_第6张图片

 使用IO复用监听事件,收到事件后分发。

核心组成包括:Reactor和Handlers

Reactor:负责监听和分发事件,分发到适当的处理程序对IO做出反应

Handlers:处理IO事件的实际事件

从IO模型到Netty笔记(二)_第7张图片

 模式分类:单Reactor单线程,单Reactor多线程,主从Reactor多线程(netty基于此模式做出改进)

单Reactor单线程:处理线程只有一个,会造成阻塞,cpu不能充分利用。

单Reactor多线程:在这里,handler只负责响应事件,不做具体业务处理,具体处理由后边work线程池的线程处理,并将结果返回给handler,handler可以把结果返回给client。reactor在这里是单线程,在高并发场景中容易出现性能瓶颈。由于数据共享和访问,需要保证不出现线程安全问题。

主从Reactor多线程:在单Reactor多线程基础上,把reactor分为处理连接和处理读写,处理读写的reactor又可以为多线程,编程复杂度较高,在nginx、netty、memcached中都有广泛使用。

NIO有关知识(二)_运气不好努力来凑-CSDN博客Reactor?解析netty小demo。简单了解https://blog.csdn.net/wai_58934/article/details/122905772

Netty模型工作原理

从IO模型到Netty笔记(二)_第8张图片

右下角的pipeline可以简单的理解为包含了channel,里边还包含了很多处理器。

从IO模型到Netty笔记(二)_第9张图片

 

你可能感兴趣的:(NIO,java,nio,netty)