AIO入门实例

来源:互联网 发布:詹姆斯历年数据 编辑:程序博客网 时间:2024/06/05 11:07

AIO入门实例

摘要:阅读《Netty权威指南》笔记
AIO概念:
AIO即JDK7开始引入的NIO,提供了异步文件通道和异步到街子通道的实现。异步通道提供两种方式操作结果:

  1. 通过java.util.concurrent.Future类来表示异步操作的结果;
  2. 在执行异步操作时传入一个java.nio.channels.CompletionHandler接口的实现类作为操作完成的回调(本例子主要使用这个方法)。

AIO创建的TimeServer

public class TimeServer {    public static void main(String[] args) {        int port = 8080;        if (args != null && args.length > 0) {            try {                port = Integer.valueOf(args[0]);            } catch (NumberFormatException e) {            }        }        AsyncTimeServerHander timeServerHander = new AsyncTimeServerHander(port);        new Thread(timeServerHander, "AIO-AsyncTimeServerHandler-001").start();    }}

很简单,就是初始化一个线程,接下来看AsyncTimeServerHander 的实现代码:

public class AsyncTimeServerHander implements Runnable {    private int port;    CountDownLatch latch;    AsynchronousServerSocketChannel asynchronousServerSocketChannel;    /**     * 构造方法,创建一个异步的服务端通道AsynchronousServerSocketChannel,调用它的bind方法绑定监     * 听端口,绑定成功则打印提示到输入台     *     * @param port     */    public AsyncTimeServerHander(int port) {        this.port = port;        try {            asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();            asynchronousServerSocketChannel.bind(new InetSocketAddress(port));            System.out.println("The time server is start in port " + port);        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void run() {        //阻塞当前线程,防止服务端异常退出        latch = new CountDownLatch(1);        doAccept();        try {            latch.await();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    /**     * 连接客户端的方法,采用的CompletionHandler方法,接收AcceptCompletionHandler作为采用的CompletionHandler方     * 法实例     */    public void doAccept() {        asynchronousServerSocketChannel.accept(this, new AcceptCompletionHandler());    }}

CompletionHandler接口有两个方法:

  1. public void completed(AsynchronousSocketChannel result, AsyncTimeServerHander attachment)
  2. public void failed(Throwable exc, AsyncTimeServerHander attachment)

根据方法名很容易就能理解两个方法的执行时间。
接下来看整个AcceptCompletionHandler 的代码:

public class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, AsyncTimeServerHander> {    @Override    public void completed(AsynchronousSocketChannel result, AsyncTimeServerHander attachment) {        /**         * 接收客户端请求,因为AsynchronousServerSocketChannel可以接收成千上万的客户端,         * 所以回调AsyncTimeServerHander中asynchronousServerSocketChannel.accept方法,         * 让新的客户端继续接入,最终形成一个循环         */        attachment.asynchronousServerSocketChannel.accept(attachment, this);        ByteBuffer buffer = ByteBuffer.allocate(1024);        /**异步读操作         * 第一个buffer:接收缓冲区         * 第二个buffer:异步Channel携带的附件,通知回调的时候作为入参使用(我的理解是当该Channel继续被调用时的参数)         * CompletionHandler:接收通知回调的业务Handler,这里的实现类是ReadCompletionHandler         */        result.read(buffer, buffer, new ReadCompletionHandler(result));    }    @Override    public void failed(Throwable exc, AsyncTimeServerHander attachment) {        attachment.latch.countDown();    }}

AcceptCompletionHandler 就可以看出,主要是通过不断的回调来实现非阻塞,同时不会对像NIO那样需要不断的判断连接的状态去根据具体的状态分配Handler。
继续往下看ReadCompletionHandler

public class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {    private AsynchronousSocketChannel channel;    /**     * 构造方法:将AsynchronousSocketChannel作为构造方法的参数传入,作为成员变量使用,     * 主要用于半包消息和发送应答     *     * @param channel     */    public ReadCompletionHandler(AsynchronousSocketChannel channel) {        if (channel != null) {            this.channel = channel;        }    }    /**     * 读取到消息的处理     *     * @param result     * @param attachment     */    @Override    public void completed(Integer result, ByteBuffer attachment) {        attachment.flip();        byte[] body = new byte[attachment.remaining()];        attachment.get(body);        try {            String req = new String(body, "UTF-8");            System.out.println("The time server receive order : " + req);            //消息判断,如果是正确的消息,调用doWrite发送当前消息到客户端            String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(req) ?                    new Date(System.currentTimeMillis()).toString() : "BAD ORDER";            doWrite(currentTime);        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }    }    /**     * 发送消息到客户端     *     * @param currentTime     */    private void doWrite(String currentTime) {        if (currentTime != null && currentTime.trim().length() > 0) {            byte[] bytes = currentTime.getBytes();            ByteBuffer writeBuffer = ByteBuffer.allocate(1024);            writeBuffer.put(bytes);            writeBuffer.flip();            channel.write(writeBuffer, writeBuffer, new CompletionHandler<Integer, ByteBuffer>() {                @Override                public void completed(Integer result, ByteBuffer attachment) {                    //递归调用,如果没有发送完成,继续发送                    if (attachment.hasRemaining()) {                        channel.write(attachment, attachment, this);                    }                }                @Override                public void failed(Throwable exc, ByteBuffer attachment) {                    try {                        channel.close();                    } catch (IOException e) {                        //ingore on close                    }                }            });        }    }    @Override    public void failed(Throwable exc, ByteBuffer attachment) {        try {            this.channel.close();        } catch (IOException e) {            e.printStackTrace();        }    }}

ReadCompletionHandler 的主要功能已经在代码里注释好了,理解起来也不是很难

总结

  1. AIO使用起来逻辑上很好理解,但是因为有很多的匿名函数,导致代码看起来很复杂;
  2. AIO非阻塞的思想其实和NIO一样,都是通过不断轮询AsynchronousServerSocketChannelaccept 方法获取连接,分配对应的Handler 去处理业务;
  3. demo很简单,重要的是对代码的理解以及熟练使用API。