AIO入门实例
摘要:阅读《Netty权威指南》笔记
AIO概念:
AIO即JDK7开始引入的NIO,提供了异步文件通道和异步到街子通道的实现。异步通道提供两种方式操作结果:
- 通过java.util.concurrent.Future类来表示异步操作的结果;
- 在执行异步操作时传入一个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接口有两个方法:
public void completed(AsynchronousSocketChannel result, AsyncTimeServerHander attachment)
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
的主要功能已经在代码里注释好了,理解起来也不是很难
总结
- AIO使用起来逻辑上很好理解,但是因为有很多的匿名函数,导致代码看起来很复杂;
- AIO非阻塞的思想其实和NIO一样,都是通过不断轮询
AsynchronousServerSocketChannel
的accept
方法获取连接,分配对应的Handler
去处理业务; - demo很简单,重要的是对代码的理解以及熟练使用API。