java之AIO实例

43 篇文章 0 订阅

AIO介绍

JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO 2.0,Java正式提供了异步文件I/O操作,同时提供了与UNIX网络编程事件驱动I/O对应的AIO。AIO是真正的异步非阻塞I/O。它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。NIO 2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。目前的AIO与NIO底层都使用了epoll(Linux中),所以二者性能都很好,主要差异在于同步与异步,NIO是同步的,始终只有一个线程在进行结果处理,而AIO的异步回调则是基于多线程的,如果NIO结果处理中引入多线程,个人认为二者性能是相仿的。

什么是epoll?
epoll是Linux中多路复用IO接口select/poll的增强版本,select/poll模型是忙轮询,即一直不停地轮询看哪些操作已经结束可以获取操作结果了,而epoll则是将已经结束的操作的操作结果放入队列中,然后只需要遍历处理队列中的操作就可以了,避免了CPU的浪费,提升程序运行效率。

AIO与NIO有什么区别?

1.NIO是同步非阻塞I/O,AIO是异步非阻塞I/O;
2.AIO与NIO的操作结果获取方式不同,NIO的操作结束后会将操作就绪的I/O放在队列中,由Selector依次循环获取处理;AIO操作结束后则会直接回调CompletionHandler的实现类的相应函数来进行处理;
3.处理操作结果时NIO是单线程,即由Selector依次在当前线程中进行处理,如果需要多线程处理需要自行实现,这也是为什么它是同步而非异步;而AIO在回调处理操作结果时,是多线程的,其底层设有线程池。

AIO既然是异步的,那么如何获得操作结果?

1.通过返回的Future模式java.util.concurrent.Future类来表示异步操作的结果;
2.在执行异步操作时传入一个java.nio.channel,并传入CompletionHandler接口的实现类作为操作完成的回调,CompletionHandler顾名思义就是专门用来处理完成结果的。

我更推荐用CompletionHandler的方式,这些handler的调用是由 AsynchronousChannelGroup的线程池派发的。显然,线程池的大小是性能的关键因素。AsynchronousChannelGroup允许绑定不同的线程池,通过三个静态方法来创建:

AIOAPI介绍

java.nio.channels.AsynchronousChannel
       标记一个channel支持异步IO操作。

 java.nio.channels.AsynchronousServerSocketChannel
       ServerSocket的aio版本,创建TCP服务端,绑定地址,监听端口等。

java.nio.channels.AsynchronousSocketChannel
       面向流的异步socket channel,表示一个连接

java.nio.channels.AsynchronousChannelGroup
       异步channel的分组管理,目的是为了资源共享。一个AsynchronousChannelGroup绑定一个线程池,这个线程池执行两个任务:处理IO事件和派发CompletionHandler。AsynchronousServerSocketChannel创建的时候可以传入一个 AsynchronousChannelGroup,那么通过AsynchronousServerSocketChannel创建的 AsynchronousSocketChannel将同属于一个组,共享资源。

java.nio.channels.CompletionHandler
       异步IO操作结果的回调接口,用于定义在IO操作完成后所作的回调工作.  CompletionHandler有三个方法,分别对应于处理成功、失败、当操作完成时,会回调completed,出现异常失败时会回调failed。

completed

操作完成时,回调completed函数,其有result和attachment两个参数:

  • result是操作完成后的操作结果;
  • attachment是在进行回调时可以传入的附件,用于回调内的操作;

failed操作异常时回调failed函数,其有exc和attachment两个参数:

  • exc即进行操作时出现的异常;
  • attachment和completed中的一致,为在进行回调时传入的附件,用于回调内操作;

 其中的泛型参数V表示IO调用的结果,而A是发起调用时传入的attchment。

client端:

public class SimpleTimeClient {
	private String host;
	private int port;
	private CountDownLatch latch;
	private AsynchronousSocketChannel channel;//异步socket通道
	public static void main(String [] args){
		while (true) {
            new Thread(() -> {
                try {
                    System.out.println("time client thread: " + Thread.currentThread());
                    SimpleTimeClient client = new SimpleTimeClient("localhost", 8088);
                    client.latch.await();
                } catch (IOException | InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
	}
	private SimpleTimeClient(String host, int port) throws IOException{
		 this.host = host;
	     this.port = port;
	     this.latch = new CountDownLatch(1);
	     initChannel();
	}
	private void initChannel() throws IOException
	{	
        channel = AsynchronousSocketChannel.open();// 打开异步socket通道
        // 异步连接指定地址,连接完成后会回调ConnectionCompletionHandler
        //A attachment :AsynchronousSocketChannel的附件,用于回调通知时作为参数传递,调用者可以自定义。
        //CompletionHandler<Void,? super A> handler 异步操作回调通知接口
        channel.connect(new InetSocketAddress(host, port), null, new ConnectionCompletionHandler());
	}
	private class ConnectionCompletionHandler implements CompletionHandler<Void, Void> {
		@Override
		public void completed(Void result, Void attachment)
		{
			System.out.println("connection thread: " + Thread.currentThread());
			String msg = "query time order";
			ByteBuffer writeBuffer = ByteBuffer.allocate(msg.length());
            writeBuffer.put(msg.getBytes(StandardCharsets.UTF_8)).flip();
            // 异步写入发送数据,写入完成后会回调WriteCompletionHandler
            channel.write(writeBuffer, writeBuffer, new WriteCompletionHandler());
		}
		@Override
		public void failed(Throwable exc, Void attachment)
		{
			 exc.printStackTrace();
	         latch.countDown();//异常时执行让线程执行完毕
		}	
	}
	//写数据完成回调处理类
	private class WriteCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
		@Override
		public void completed(Integer result, ByteBuffer buffer){
			System.out.println("write thread: " + Thread.currentThread());
			if(buffer.hasRemaining())
				channel.write(buffer, buffer, this);
			else{
	            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
	            //异步读取返回的数据,读取结束后会回调ReadCompletionHandler
	            channel.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>(){
					@Override
					public void completed(Integer result, ByteBuffer buffer)
					{
						System.out.println("read thread: " + Thread.currentThread());
						buffer.flip();
						byte[] bytes = new byte[buffer.remaining()];
						buffer.get(bytes);
						String body;
						body = new String(bytes, StandardCharsets.UTF_8);
						System.out.println("now is " + body);
						latch.countDown();
					}
					@Override
					public void failed(Throwable exc, ByteBuffer attachment)
					{
						try{
							channel.close();
							latch.countDown();
						} catch (IOException e){							
							e.printStackTrace();
						}
					}
	            	
	            });
			}      
		}
		@Override
		public void failed(Throwable exc, ByteBuffer attachment)
		{
			exc.printStackTrace();
            latch.countDown();
		}	
	}
}

server端:

public class SimpleTimeServer implements Runnable{
	//维持服务线程的门闩
	private CountDownLatch latch;
	//异步socket服务通道
	private AsynchronousServerSocketChannel asynchronousServerSocketChannel;
	public static void main(String [] args){
		try{
			System.out.println("我是主线程: " + Thread.currentThread());
			new SimpleTimeServer(8088).run();
			System.out.println("监听线程已挂");
		} catch (IOException e) {
            e.printStackTrace();
        }
	}
	private SimpleTimeServer(int port) throws IOException{
		//开启异步socket服务
        asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open();
        //绑定端口
        asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
        System.out.println("simple time server start in " + port);
	}
	@Override
	public void run()
	{
		latch = new CountDownLatch(1);//阻塞当前线程防止服务端任务执行完退出,
		//在实际项目中,不需要启动独立的线程来处理asynchronousServerSocketChannel,这里仅仅是个demo。
		System.out.println("我是监听线程:" + Thread.currentThread());
		//异步socket服务接收请求,传递一个attach对象和实现了CompletionHandler的回调来处理AIO操作结果
		asynchronousServerSocketChannel.accept(this, new AcceptCompletionHandler());
		try{
			latch.await();
		}catch (InterruptedException e) {
            e.printStackTrace();
        }
	}
	//接收请求的结束动作处理类,当异步socket服务接收到一个请求时,会回调此handler,从而对收到的请求进行处理 
	//AsynchronousSocketChannel为处理结果 SimpleTimeServer为发起调用时传入的参数
	private class AcceptCompletionHandler implements CompletionHandler<AsynchronousSocketChannel, SimpleTimeServer>{
		@Override
		public void completed(AsynchronousSocketChannel channel, SimpleTimeServer attachment)
		{
			System.out.println("我是处理线程:" + Thread.currentThread());
			//循环监听,进行监听操作的是SimpleTimeServer运行的线程,这样做的目的是因为一个asynchronousServerSocketChannel
			//可以接收成千上万个客户端,所以当系统回调我们传入的CompletionHandler时,表示新的客户端已经接入成功,
			//所以继续调用accept接受其他客户端  如果处理不过来回用新的线程来接收其他接入
			attachment.asynchronousServerSocketChannel.accept(attachment, this);			
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			//ByteBuffer dst :接受缓冲区,用于从异步的channel中读取数据包
            //A attachment :异步channel携带的附件,通知回调时作为参数传递使用。
            //CompletionHandler<Integer,? super A> handler 接收通知回调的业务handleer
			channel.read(buffer, buffer, new ReadCompletionHandler(channel));
		}
		@Override
		public void failed(Throwable exc, SimpleTimeServer attachment)
		{
			//接收请求失败,打印异常信息,将门闩减一,服务线程终止
			exc.printStackTrace();
			attachment.latch.countDown();
		}
		//读取数据的结束动作处理类,当系统将数据读取到buffer中,会回调此handler
		private class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
			//将AsynchronousSocketChannel通过参数传递到ReadCompletionHandler中 当做成员变量用来读取包中消息和发送应答
			private AsynchronousSocketChannel channel;
	        ReadCompletionHandler(AsynchronousSocketChannel channel) {
	            this.channel = channel;
	        }
			@Override
			public void completed(Integer result, ByteBuffer attachment)
			{
				//首先对attachment进行flip操作,为后续从缓冲区读取数据做准备
				//根据缓冲区的可读字节创建byte数组,然后通过newString方法创建请求消息,对请求消息进行判断
				//如果是“query time order” 则获取当前系统的服务器时间,调用dowrite方法发送客户端。
				attachment.flip();
				byte[] body = new byte[result];//attachment.remaining()
				attachment.get(body);
				String req = new String(body, StandardCharsets.UTF_8);
				System.out.println("the time server received order: " + req);
				String currentTime = "query time order".equalsIgnoreCase(req) ? new Date(System.currentTimeMillis())
						.toString() : "BAD ORDER";
				doWrite(currentTime);
			}
			@Override
			public void failed(Throwable exc, ByteBuffer attachment)
			{
				try{//读取失败关闭通道
					channel.close();
				} catch (IOException e){			
					e.printStackTrace();
				}
			}	
			private void doWrite(String msg)
			{
				//将字符串解码为字节数组再调用AsynchronousSocketChannel的write方法
				if(msg != null){
					byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
					ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
					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 //可以对异常判断如果是I/O异常,就关闭链路,释放资源,如果是其他异常按照业务逻辑处理。
						public void failed(Throwable exc, ByteBuffer attachment)
						{
							try{
								channel.close();
							} catch (IOException e){
								e.printStackTrace();
							}
						}					
					});
				}
			}
		}
	}
}

AIO读取文件:AsynchronousFileChannel

第一种方式是调用返回值为Future的read()方法:这种方式中,read()接受一个ByteBuffer座位第一个参数,数据会被读取到ByteBuffer中。 第二个参数是开始读取数据的文件位置。read()方法会立刻返回,即使读操作没有完成。我们可以通过isDone()方法检查操作是否完成。

static void read1() throws IOException{
		AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
				Paths.get("E:/ModMudEngine/mt/trunk/logic/gsever/test/ser.ser"), StandardOpenOption.READ);
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		long position = 0;
		Future<Integer> operation = fileChannel.read(buffer, position);
		while (!operation.isDone());
		buffer.flip();
		byte[] data = new byte[buffer.limit()];
		buffer.get(data);
		System.out.println(new String(data));
		buffer.clear();
	}

第二种通过CompletionHandler读取数据

static void read2() throws IOException
	{
		AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(
				Paths.get("E:/ModMudEngine/mt/trunk/logic/gsever/test/ser.ser"), StandardOpenOption.READ);
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		long position = 0;
		fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
		    @Override
		    public void completed(Integer result, ByteBuffer attachment) {
		        System.out.println("result = " + result);
		        attachment.flip();
		        byte[] data = new byte[attachment.limit()];
		        attachment.get(data);
		        System.out.println(new String(data));
		        attachment.clear();		        
		    }		 
		    @Override
		    public void failed(Throwable exc, ByteBuffer attachment) {
		    	try
				{
					fileChannel.close();
				} catch (IOException e){					
					e.printStackTrace();
				}
		    }
		});
	}

通过Future写数据: 第二个参数是开始写入数据的文件位置

static void write() throws IOException{
		Path path = Paths.get("E:/ModMudEngine/mt/trunk/logic/gsever/test/copy.txt");
		AsynchronousFileChannel fileChannel = 
		    AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);	 
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		long position = 0;	 
		buffer.put("test data".getBytes());
		buffer.flip();		 
		Future<Integer> operation = fileChannel.write(buffer, position);
		buffer.clear(); 
		while(!operation.isDone());
		System.out.println("Write done");
	}

通过CompletionHandler写数据

	static void write2() throws IOException{
		Path path = Paths.get("E:/ModMudEngine/mt/trunk/logic/gsever/test/copy.txt");
		if(!Files.exists(path)){
		    Files.createFile(path);
		}
		AsynchronousFileChannel fileChannel = 
		    AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);	 
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		long position = 0; 
		buffer.put("test dataa".getBytes());
		buffer.flip(); 
		fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
		    @Override
		    public void completed(Integer result, ByteBuffer attachment) {
		        System.out.println("bytes written: " + result);
		    } 
		    @Override
		    public void failed(Throwable exc, ByteBuffer attachment) {
		        System.out.println("Write failed");
		        exc.printStackTrace();
		        try{
					fileChannel.close();
				} catch (IOException e)
				{					
					e.printStackTrace();
				}
		    }
		});
	}

close时如果有未完成的操作或继续启动操作会抛出异常。

  • 2
    点赞
  • 5
    收藏
  • 打赏
    打赏
  • 2
    评论
联想 超融合架构定义:以软件为中心的新型 超融合架构定义:以软件为中心的新型 IT 基础架构,以 基础架构,以 基础架构,以 x86x86 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 服务器作为基础单元,由联想进行软硬件集成、优 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 化和预装,在每一个节点内紧密集成计算、存储网络虚拟其他技术通过统界面对于中的软硬 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 件资源进行管理,实现简化解决软硬兼容性可横向线扩展一体交付。联想 AIO AIO超融合是 超融合是 建设虚拟化 IT 基础设施的最佳实践,是构建企业私有云组件。 基础设施的最佳实践,是构建企业私有云组件。 基础设施的最佳实践,是构建企业私有云组件。 基础设施的最佳实践,是构建企业私有云组件。 基础设施的最佳实践,是构建企业私有云组件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

weixin_39407066

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值