Netty(二) 网络NIO和AIO

为什么要介绍NIO和AIO

Netty作为分布式的中间件,性能要求比较高。传统的方法方法是一个客户端开启一个线程,客户端的请求会被服务器派发,并且这种方式是倾向于cpu等待的,假使客户端处理缓慢或者网络拥堵,服务器会花费不少时间等待;意味着摒弃这种方式,能提高服务器的并发能力。

BIO:Block-IO

Block-io是阻塞同步的,如上面所说的,会倾向于cpu等待,会相当依赖客户端处理性能与客户端数量多少,通信是否耗时与网速。但是这种比较传统的通信方式,模式相对简单,使用也很方便。也可加入线程池进行优化,减少每次IO的资源消耗,在池中保持线程的激活状态。

BIO就是socket和serverSocket一一对应来完成一个套接字的通道。

NIO:准备好了再通知我

NIO是NonBlock-IO,是非阻塞同步的通信方式,其实NIO本身和并发没有什么关系,而是它很大大提高线程的使用率来解决并发问题的。
NIO有三个比较重要的概念:

  • Buffer:缓冲区,实际上是一个ByteBuffer数组。
  • Channel:通道,通道分两种(网络读写和文件读写)。通道是双向的,而stream是单向的。
  • Selector:多路复用器,负责轮询Channel,找出就绪状态的Channel。(就绪状态意味着可被处理了)

NIO是一个selector轮询多个channel,channel会将自己注册在selector中,但自己的数据准备好了,就通知selector,也就是“准备好了再通知我”。

核心代码(具体代码参见下面提供的git)

....
selector.select();//消费阻塞
Set readykeys = selector.selectedKeys();//获取已经准备好的keys
Iterator i = readykeys.iterator();//迭代
long e = 0;
while (i.hasNext()) {
    SelectionKey sk = (SelectionKey) i.next();
    i.remove();//必须消除,防止重复消费
    if (sk.isAcceptable()) {
        doAccept(sk);//如果为接受状态,接受
    } else if (sk.isValid() && sk.isReadable()) {//如果是可读
        if (!time_stat.containsKey(((SocketChannel) sk.channel()).socket())) {
            //将socket方法如map
            time_stat.put(((SocketChannel) sk.channel()).socket(),
                    System.currentTimeMillis());//增加一个时间戳
            //读取
            doRead(sk);
        }
    } else if (sk.isValid() && sk.isWritable()) {
        //写
        doWrite(sk);
        e = System.currentTimeMillis();
        long b = time_stat.remove(((SocketChannel) sk.channel()).socket());
        System.out.println("spend:" + (e - b) + "ms");//输入处理写入耗时
    }
}
....

AIO:读完了再通知我

AIO是在NIO的基础上,提供了异步操作(asynchronized)。AIO是在IO操作完成后,再给线程发送通知,AIO就是完成不阻塞的,通过一个回调函数,等待IO完成后使用。
AIO提供了一个异步通道:

AsynchronousSocketChannel
此类的方法签名会有一个参数CompletionHandler,处理回调成功时或者失败时的结果。

重要的代码(具体代码参见下面提供的git)

......
......
   //首先需要适用异步通道
public final static int PORT=65500;
private AsynchronousServerSocketChannel server;
public AIOServer() throws IOException {
server=AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(PORT));
    }
    public void start(){
        System.out.println("Server listen on" +PORT);
        //注册事件和事件完成过后的处理器
        server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            public void completed(AsynchronousSocketChannel result, Object attachment) {
                final ByteBuffer buffer=ByteBuffer.allocate(1024);
                System.out.println(Thread.currentThread().getName());
                Future<Integer> writeResult=null;
                try{
                    result.read(buffer).get(100, TimeUnit.SECONDS);
                    buffer.flip();
                    writeResult=result.write(buffer);
                }catch (InterruptedException|ExecutionException e){
                    e.printStackTrace();
                }catch (TimeoutException e){
                    e.printStackTrace();
                }finally {
                    try {
                        server.accept(null,this);
                        writeResult.get();
                        result.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }

            public void failed(Throwable exc, Object attachment) {
                System.out.println("failed:"+exc);
            }


        });

    }
    ....
    ....

Netty和NIO的关系

Netty创建工作组时,提供了一个NioServerSocketChannel(),这是便开启了NIO模式。

总结

这里主要介绍的是BIO/NIO/AIO的区别,三者根据顺便来排列,是十分越来越先进的,但是并不是AIO是最好的选择,具体还要根据业务场景进行选择。

git

https://github.com/Steve8seven/Netty-seed

源代码在nio/aio/bio文件下

参考资料

《Java高并发程序设计》葛一鸣/郭超

    原文作者:橘猫它在想什么
    原文地址: https://www.jianshu.com/p/e207ad766128
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞