I / O类型
根据操作的阻塞或非阻塞性质以及IO准备/完成事件通知的同步或异步性质,可以使用四种不同的方法来完成IO。
同步阻塞I / O
IO操作在此阻止应用程序,直到应用程序完成为止,这构成了大多数Web服务器中每个连接模型的典型线程的基础。
当调用阻塞的read()或write()时,将有一个上下文切换到内核,IO操作将在此发生,并且数据将被复制到内核缓冲区。 之后,内核缓冲区将被转移到用户空间应用程序级缓冲区,并且应用程序线程将被标记为可运行
然后,应用程序将解除阻塞并读取用户空间缓冲区中的数据。
每个连接线程模型试图通过将连接限制在线程中来限制这种阻塞的影响,以使其他并发连接的处理不会被一个连接上的I / O操作阻塞。 只要连接寿命短并且数据链接延迟不是那么糟糕,这很好。 但是在
如果连接寿命长或延迟长,则如果使用固定大小的线程池,线程将很长一段时间会被这些连接阻塞,导致新连接饿死,因为阻塞的线程无法在运行中重新用于服务新连接被阻止的状态,否则
如果使用新线程为每个连接提供服务,将导致在系统内产生大量线程,这可能会占用大量资源,并且对于高并发负载来说,上下文转换成本很高。
ServerSocket server = new ServerSocket(port);while(true) {Socket connection = server.accept();spawn-Thread-and-process(connection);}
同步非阻塞I / O
在此模式下,设备或连接被配置为非阻塞,因此不会阻塞read()和write()操作。 这通常意味着如果无法立即满足该操作,它将返回错误代码,指示该操作将阻塞(POSIX中的EWOULDBLOCK)或设备
暂时不可用(POSIX中为EAGAIN)。 在设备准备就绪并读取所有数据之前,应由应用程序轮询。 但是,这不是很有效,因为这些调用中的每一个都会导致上下文切换到内核并返回,而不管是否读取了某些数据。
具有就绪事件的异步非阻塞I / O
早期模式的问题在于,应用程序必须进行轮询并且忙于等待才能完成工作。 在准备好读写设备时如何通知应用程序会更好吗? 这正是此模式为您提供的。 使用特殊的系统调用(因平台而异–对于Linux为select()/ poll()/ epoll(),对于BSD为kqueue(),对于Solaris为/ dev / poll),应用程序注册了获取I / O准备就绪的兴趣来自特定设备的特定I / O操作(读或写)的信息(Linux术语中是文件描述符,因为所有套接字都是使用文件描述符抽象的)。 之后,将调用此系统调用,该系统调用将阻塞,直到至少其中一个注册文件描述符准备就绪。 一旦这是真的,准备进行I / O的文件描述符将作为
系统调用的返回,并且可以在应用程序线程中的循环中按顺序进行服务。
就绪的连接处理逻辑通常包含在用户提供的事件处理程序中,该事件处理程序仍将必须发出非阻塞的read()/ write()调用,以从设备到内核并最终向内核获取数据。
用户空间缓冲区,导致上下文切换到内核。 而且,通常没有绝对的保证可以使用该设备执行预期的I / O,因为操作系统提供的只是该设备可能已准备好进行感兴趣的I / O操作的指示。在这种情况下,阻止read()或write()可以使您摆脱困境。 但是,这应该是规范之外的例外。
因此,总体思路是以异步方式获取就绪事件,并注册一些事件处理程序以在触发此类事件通知后进行处理。 因此,您可以看到,所有这些操作都可以在单个线程中完成,同时在不同的连接之间进行多路复用,这主要是由于select()的特性(这里我选择一个代表性的系统调用),该特性可以一次返回多个套接字的就绪状态。 这是这种操作模式的吸引力的一部分,在这种操作模式下,一个线程一次可以服务大量连接。 这个
模式通常称为“非阻止I / O”模型。
Java通过其NIO API提取了特定于平台的系统调用实现之间的差异。 套接字/文件描述符使用Channels进行抽象,并且Selector封装选择系统调用。 对获取就绪事件感兴趣的应用程序向选择器注册一个Channel(通常是一个由ServerSocketChannel上的accept()获得的SocketChannel),并获得一个SelectionKey,它用作保存Channel和注册信息的句柄。 然后在Selector上执行阻塞的select()调用,该调用将返回一组SelectionKeys,然后可以对其进行处理
使用应用程序指定的事件处理程序一个接一个地处理。
Selector selector = Selector.open();channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectionKey.OP_READ);while(true) {int readyChannels = selector.select();if(readyChannels == 0) continue;Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if(key.isAcceptable()) {// a connection was accepted by a ServerSocketChannel.} else if (key.isConnectable()) {// a connection was established with a remote server.} else if (key.isReadable()) {// a channel is ready for reading} else if (key.isWritable()) {// a channel is ready for writing}keyIterator.remove();}
}
具有完成事件的异步和非阻塞I / O
就绪事件仅能通知您设备/套接字已准备就绪,请执行某些操作。 应用程序仍然必须进行从设备/套接字到用户空间缓冲区的数据读取(更准确地指示操作系统通过系统调用来这样做),直到从设备一直到用户空间缓冲区。 将作业委派给操作系统在后台运行,并在完成作业后通过将所有数据从设备传输到内核缓冲区,最后传输到应用程序级缓冲区,让它通知您是否很好? 这就是这种模式(通常称为“异步I / O”模式)背后的基本思想。 为此,需要操作系统支持AIO操作。 在Linux中,此支持存在于2.6的aio POSIX API中,而对于Windows,则以“ I / O完成端口”的形式存在。
借助NIO2,Java已通过其AsynchronousChannel API增强了对该模式的支持。
操作系统支持
为了支持就绪和完成事件通知,不同的操作系统提供了不同的系统调用。 对于就绪事件,可以在基于Linux的系统中使用select()和poll()。 但是,较新的epoll()变体是首选的,因为它比select()或poll()更有效率。 select()遭受这样的事实,即选择时间随所监视的描述符数量线性增加。 覆盖文件描述符数组引用似乎是臭名昭著的。 因此,每次称为描述符数组时,都需要从单独的副本中重新填充它。 无论如何都不是一个优雅的解决方案。
可以通过两种方式配置epoll()变体。 即边沿触发和水平触发。 在边缘触发的情况下,仅当在关联的描述符上检测到事件时,它才会发出通知。 在事件触发的通知中说,您的应用程序处理程序仅读取内核输入缓冲区的一半。 现在,即使有一些数据要读取,它下次也不会在此描述符上得到通知,除非设备准备发送更多数据而导致文件描述符事件。 另一方面,级别触发的配置将在每次要读取数据时触发通知。
根据版本,类似的系统调用以BSD形式的kqueue和/ dev / poll或Solaris中的“ Event Completion”形式出现。 Windows等效为“ I / O完成端口”。
但是,至少在Linux情况下,AIO模式的情况有所不同。 Linux在套接字上对aio的支持似乎充其量是阴暗的,有人暗示它实际上是在内核级别使用就绪事件,而在应用程序级别上对完成事件提供了异步抽象。 但是Windows似乎通过“ I / O完成端口”再次支持此类。
设计 I / O模式101
在软件开发中,到处都有模式。 I / O没什么不同。 与NIO和AIO模型相关的I / O模式有以下几种。
反应堆模式
有几个组件参与此模式。 我将首先浏览它们,这样很容易理解该图。
Reactor Initiator:这是通过配置和启动调度程序来启动非阻塞服务器的组件。 首先,它将绑定服务器套接字,并向解复用器注册它,以使客户端连接接受就绪事件。 然后,将为分派器注册每种就绪事件类型的事件处理程序实现(读/写/接受等)。 接下来,调度程序事件循环将被调用以处理事件通知。
调度程序:定义用于注册,除去和调度事件处理程序的接口,这些事件处理程序负责对连接事件做出反应,这些事件包括连接接受,一组连接上的数据输入/输出和超时事件。 为了服务于客户端连接,相关的事件处理程序(例如:accept事件处理程序)将向解复用器注册接受的客户端通道(用于底层客户端套接字的包装器)以及就绪事件的类型,以侦听该特定通道。 之后,调度程序线程将在多路分解器上为已注册的通道集调用阻塞准备状态选择操作。 一旦一个或多个注册通道准备好进行I / O,调度程序将使用注册事件处理程序逐一服务与每个就绪通道相关联的每个返回的“句柄”。 这些事件处理程序不要占用调度程序线程,这一点很重要,因为它将延迟调度程序为其他就绪连接提供服务的时间。 由于事件处理程序中的常规逻辑包括向/从就绪连接传输数据,这将阻塞直到所有数据在用户空间和内核空间数据缓冲区之间正常传输之前,因此,这些处理程序将在与线程不同的线程中运行池。
句柄:一旦向解复用器注册了通道,就封装了连接通道和就绪信息,返回句柄。 多路复用器就绪选择操作将返回一组就绪的句柄。 Java NIO的等效项是SelectionKey。
解复用器:等待一个或多个已注册连接通道的就绪事件。 Java NIO等效于Selector。
事件处理程序:指定具有钩子方法的接口,用于分配连接事件。 这些方法需要由特定于应用程序的事件处理程序实现来实现。
具体事件处理程序:包含用于从基础连接读取/写入数据以及执行所需处理或从传递的Handle发起客户端连接接受协议的逻辑。
事件处理程序通常在线程池的单独线程中运行,如下图所示。
此模式的简单回显服务器实现如下(没有事件处理程序线程池)。
public class ReactorInitiator {private static final int NIO_SERVER_PORT = 9993;public void initiateReactiveServer(int port) throws Exception {ServerSocketChannel server = ServerSocketChannel.open();server.socket().bind(new InetSocketAddress(port));server.configureBlocking(false);Dispatcher dispatcher = new Dispatcher();dispatcher.registerChannel(SelectionKey.OP_ACCEPT, server);dispatcher.registerEventHandler(SelectionKey.OP_ACCEPT, new AcceptEventHandler(dispatcher.getDemultiplexer()));dispatcher.registerEventHandler(SelectionKey.OP_READ, new ReadEventHandler(dispatcher.getDemultiplexer()));dispatcher.registerEventHandler(SelectionKey.OP_WRITE, new WriteEventHandler());dispatcher.run(); // Run the dispatcher loop}public static void main(String[] args) throws Exception {System.out.println('Starting NIO server at port : ' +NIO_SERVER_PORT);new ReactorInitiator().initiateReactiveServer(NIO_SERVER_PORT);}}public class Dispatcher {private Map<Integer, EventHandler> registeredHandlers =new ConcurrentHashMap<Integer, EventHandler>();private Selector demultiplexer;public Dispatcher() throws Exception {demultiplexer = Selector.open();}public Selector getDemultiplexer() {return demultiplexer;}public void registerEventHandler(int eventType, EventHandler eventHandler) {registeredHandlers.put(eventType, eventHandler);}// Used to register ServerSocketChannel with the// selector to accept incoming client connectionspublic void registerChannel(int eventType, SelectableChannel channel) throws Exception {channel.register(demultiplexer, eventType);}public void run() {try {while (true) { // Loop indefinitelydemultiplexer.select();Set<SelectionKey> readyHandles =demultiplexer.selectedKeys();Iterator<SelectionKey> handleIterator =readyHandles.iterator();while (handleIterator.hasNext()) {SelectionKey handle = handleIterator.next();if (handle.isAcceptable()) {EventHandler handler =registeredHandlers.get(SelectionKey.OP_ACCEPT);handler.handleEvent(handle);// Note : Here we don't remove this handle from// selector since we want to keep listening to// new client connections}if (handle.isReadable()) {EventHandler handler =registeredHandlers.get(SelectionKey.OP_READ);handler.handleEvent(handle);handleIterator.remove();}if (handle.isWritable()) {EventHandler handler =registeredHandlers.get(SelectionKey.OP_WRITE);handler.handleEvent(handle);handleIterator.remove();}}}} catch (Exception e) {e.printStackTrace();}}}public interface EventHandler {public void handleEvent(SelectionKey handle) throws Exception;}public class AcceptEventHandler implements EventHandler {private Selector demultiplexer;public AcceptEventHandler(Selector demultiplexer) {this.demultiplexer = demultiplexer;}@Overridepublic void handleEvent(SelectionKey handle) throws Exception {ServerSocketChannel serverSocketChannel =(ServerSocketChannel) handle.channel();SocketChannel socketChannel = serverSocketChannel.accept();if (socketChannel != null) {socketChannel.configureBlocking(false);socketChannel.register(demultiplexer, SelectionKey.OP_READ);}}}public class ReadEventHandler implements EventHandler {private Selector demultiplexer;private ByteBuffer inputBuffer = ByteBuffer.allocate(2048);public ReadEventHandler(Selector demultiplexer) {this.demultiplexer = demultiplexer;}@Overridepublic void handleEvent(SelectionKey handle) throws Exception {SocketChannel socketChannel =(SocketChannel) handle.channel();socketChannel.read(inputBuffer); // Read data from clientinputBuffer.flip();// Rewind the buffer to start reading from the beginningbyte[] buffer = new byte[inputBuffer.limit()];inputBuffer.get(buffer);System.out.println('Received message from client : ' +new String(buffer));inputBuffer.flip();// Rewind the buffer to start reading from the beginning// Register the interest for writable readiness event for// this channel in order to echo back the messagesocketChannel.register(demultiplexer, SelectionKey.OP_WRITE, inputBuffer);}}public class WriteEventHandler implements EventHandler {@Overridepublic void handleEvent(SelectionKey handle) throws Exception {SocketChannel socketChannel =(SocketChannel) handle.channel();ByteBuffer inputBuffer = (ByteBuffer) handle.attachment();socketChannel.write(inputBuffer);socketChannel.close(); // Close connection}}
前摄者模式
该模式基于异步I / O模型。 主要组成部分如下。
主动发起者:这是发起异步操作以接受客户端连接的实体。 这通常是服务器应用程序的主线程。 将完成处理程序与完成调度程序一起注册以处理连接接受异步事件通知。
异步操作处理器:负责异步执行I / O操作,并向应用程序级别完成处理程序提供完成事件通知。 这通常是操作系统公开的异步I / O接口。
异步操作:异步操作由异步操作处理器在单独的内核线程中运行以完成操作。
完成分配器:负责异步操作完成时回调到应用程序完成处理程序。 当异步操作处理器完成异步启动的操作时,完成调度程序将代表它执行应用程序回调。 通常,根据事件的类型将事件通知处理委托给适当的完成处理程序。
完成处理程序:这是应用程序实现的接口,用于处理异步事件完成事件。
让我们看看如何使用Java 7中添加的新Java NIO.2 API来实现此模式(作为简单的回显服务器)。
public class ProactorInitiator {static int ASYNC_SERVER_PORT = 4333;public void initiateProactiveServer(int port)throws IOException {final AsynchronousServerSocketChannel listener =AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(port));AcceptCompletionHandler acceptCompletionHandler =new AcceptCompletionHandler(listener);SessionState state = new SessionState();listener.accept(state, acceptCompletionHandler);}public static void main(String[] args) {try {System.out.println('Async server listening on port : ' +ASYNC_SERVER_PORT);new ProactorInitiator().initiateProactiveServer(ASYNC_SERVER_PORT);} catch (IOException e) {e.printStackTrace();}// Sleep indefinitely since otherwise the JVM would terminatewhile (true) {try {Thread.sleep(Long.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class AcceptCompletionHandlerimplementsCompletionHandler<AsynchronousSocketChannel, SessionState> {private AsynchronousServerSocketChannel listener;public AcceptCompletionHandler(AsynchronousServerSocketChannel listener) {this.listener = listener;}@Overridepublic void completed(AsynchronousSocketChannel socketChannel,SessionState sessionState) {// accept the next connectionSessionState newSessionState = new SessionState();listener.accept(newSessionState, this);// handle this connectionByteBuffer inputBuffer = ByteBuffer.allocate(2048);ReadCompletionHandler readCompletionHandler =new ReadCompletionHandler(socketChannel, inputBuffer);socketChannel.read(inputBuffer, sessionState, readCompletionHandler);}@Overridepublic void failed(Throwable exc, SessionState sessionState) {// Handle connection failure...}}public class ReadCompletionHandler implementsCompletionHandler<Integer, SessionState> {private AsynchronousSocketChannel socketChannel;private ByteBuffer inputBuffer;public ReadCompletionHandler(AsynchronousSocketChannel socketChannel,ByteBuffer inputBuffer) {this.socketChannel = socketChannel;this.inputBuffer = inputBuffer;}@Overridepublic void completed(Integer bytesRead, SessionState sessionState) {byte[] buffer = new byte[bytesRead];inputBuffer.rewind();// Rewind the input buffer to read from the beginninginputBuffer.get(buffer);String message = new String(buffer);System.out.println('Received message from client : ' +message);// Echo the message back to clientWriteCompletionHandler writeCompletionHandler =new WriteCompletionHandler(socketChannel);ByteBuffer outputBuffer = ByteBuffer.wrap(buffer);socketChannel.write(outputBuffer, sessionState, writeCompletionHandler);}@Overridepublic void failed(Throwable exc, SessionState attachment) {//Handle read failure.....}}public class WriteCompletionHandler implementsCompletionHandler<Integer, SessionState> {private AsynchronousSocketChannel socketChannel;public WriteCompletionHandler(AsynchronousSocketChannel socketChannel) {this.socketChannel = socketChannel;}@Overridepublic void completed(Integer bytesWritten, SessionState attachment) {try {socketChannel.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void failed(Throwable exc, SessionState attachment) {// Handle write failure.....}}public class SessionState {private Map<String, String> sessionProps =new ConcurrentHashMap<String, String>();public String getProperty(String key) {return sessionProps.get(key);}public void setProperty(String key, String value) {sessionProps.put(key, value);}}
每种类型的事件完成(接受/读取/写入)由实现CompletionHandler接口(接受/读取/ WriteCompletionHandler等)的单独的完成处理程序处理。 在这些连接处理程序内部管理状态转换。 附加的SessionState参数可用于
在一系列完成事件中保持客户端会话的特定状态。
NIO框架(HTTPCore)
如果您正在考虑实现基于NIO的HTTP服务器,那么您很幸运。 Apache HTTPCore库为使用NIO处理HTTP流量提供了出色的支持。 API通过内置的HTTP请求处理功能,在NIO层之上提供了更高级别的抽象。下面给出了一个最小的非阻塞HTTP服务器实现,该实现为任何GET请求返回一个虚拟输出。
public class NHttpServer {public void start() throws IOReactorException {HttpParams params = new BasicHttpParams();// Connection parametersparams.setIntParameter(HttpConnectionParams.SO_TIMEOUT, 60000).setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8 * 1024).setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK, true).setBooleanParameter(HttpConnectionParams.TCP_NODELAY, true);final DefaultListeningIOReactor ioReactor =new DefaultListeningIOReactor(2, params);// Spawns an IOReactor having two reactor threads// running selectors. Number of threads here is// usually matched to the number of processor cores// in the system// Application specific readiness event handlerServerHandler handler = new ServerHandler();final IOEventDispatch ioEventDispatch =new DefaultServerIOEventDispatch(handler, params);// Default IO event dispatcher encapsulating the// event handlerListenerEndpoint endpoint = ioReactor.listen(new InetSocketAddress(4444));// start the IO reactor in a new separate threadThread t = new Thread(new Runnable() {public void run() {try {System.out.println('Listening in port 4444');ioReactor.execute(ioEventDispatch);} catch (InterruptedIOException ex) {ex.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}});t.start();// Wait for the endpoint to become ready,// i.e. for the listener to start accepting requests.try {endpoint.waitFor();} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args)throws IOReactorException {new NHttpServer().start();}}public class ServerHandler implements NHttpServiceHandler {private static final int BUFFER_SIZE = 2048;private static final String RESPONSE_SOURCE_BUFFER ='response-source-buffer';// the factory to create HTTP responsesprivate final HttpResponseFactory responseFactory;// the HTTP response processorprivate final HttpProcessor httpProcessor;// the strategy to re-use connectionsprivate final ConnectionReuseStrategy connStrategy;// the buffer allocatorprivate final ByteBufferAllocator allocator;public ServerHandler() {super();this.responseFactory = new DefaultHttpResponseFactory();this.httpProcessor = new BasicHttpProcessor();this.connStrategy = new DefaultConnectionReuseStrategy();this.allocator = new HeapByteBufferAllocator();}@Overridepublic void connected(NHttpServerConnection nHttpServerConnection) {System.out.println('New incoming connection');}@Overridepublic void requestReceived(NHttpServerConnection nHttpServerConnection) {HttpRequest request =nHttpServerConnection.getHttpRequest();if (request instanceof HttpEntityEnclosingRequest) {// Handle POST and PUT requests} else {ContentOutputBuffer outputBuffer =new SharedOutputBuffer(BUFFER_SIZE, nHttpServerConnection, allocator);HttpContext context =nHttpServerConnection.getContext();context.setAttribute(RESPONSE_SOURCE_BUFFER, outputBuffer);OutputStream os =new ContentOutputStream(outputBuffer);// create the default response to this requestProtocolVersion httpVersion =request.getRequestLine().getProtocolVersion();HttpResponse response =responseFactory.newHttpResponse(httpVersion, HttpStatus.SC_OK,nHttpServerConnection.getContext());// create a basic HttpEntity using the source// channel of the response pipeBasicHttpEntity entity = new BasicHttpEntity();if (httpVersion.greaterEquals(HttpVersion.HTTP_1_1)) {entity.setChunked(true);}response.setEntity(entity);String method = request.getRequestLine().getMethod().toUpperCase();if (method.equals('GET')) {try {nHttpServerConnection.suspendInput();nHttpServerConnection.submitResponse(response);os.write(new String('Hello client..').getBytes('UTF-8'));os.flush();os.close();} catch (Exception e) {e.printStackTrace();}} // Handle other http methods}}@Overridepublic void inputReady(NHttpServerConnection nHttpServerConnection,ContentDecoder contentDecoder) {// Handle request enclosed entities here by reading// them from the channel}@Overridepublic void responseReady(NHttpServerConnection nHttpServerConnection) {try {nHttpServerConnection.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void outputReady(NHttpServerConnection nHttpServerConnection,ContentEncoder encoder) {HttpContext context = nHttpServerConnection.getContext();ContentOutputBuffer outBuf =(ContentOutputBuffer) context.getAttribute(RESPONSE_SOURCE_BUFFER);try {outBuf.produceContent(encoder);} catch (IOException e) {e.printStackTrace();}}@Overridepublic void exception(NHttpServerConnection nHttpServerConnection,IOException e) {e.printStackTrace();}@Overridepublic void exception(NHttpServerConnection nHttpServerConnection,HttpException e) {e.printStackTrace();}@Overridepublic void timeout(NHttpServerConnection nHttpServerConnection) {try {nHttpServerConnection.close();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void closed(NHttpServerConnection nHttpServerConnection) {try {nHttpServerConnection.close();} catch (IOException e) {e.printStackTrace();}}}
IOReactor类基本上将使用处理预备事件的ServerHandler实现包装解复用器功能。
Apache Synapse(开源ESB)包含一个基于NIO的HTTP服务器的良好实现,其中NIO用于为每个实例扩展大量客户端,并且随着时间的推移内存使用量一直保持不变。 该实现还包含与Axis2传输框架集成一起内置的良好调试和服务器统计信息收集机制。 可以在[1]中找到。
结论
在执行I / O时,有多种选择会影响服务器的可伸缩性和性能。 上面的每种I / O机制各有利弊,因此应根据预期的可伸缩性和性能特征以及这些方法的易维护性来做出决定。 这结束了我关于I / O的篇幅较长的文章。 随时提供您可能有的建议,更正或评论。 可以从此处下载文章中概述的服务器的完整源代码以及客户端。
相关链接
我在此过程中经历了许多参考。 以下是一些有趣的内容。
[1] http://www.ibm.com/developerworks/java/library/j-nio2-1/index.html [2] http://www.ibm.com/developerworks/linux/library/l-async / [3] http://lse.sourceforge.net/io/aionotes.txt [4] http://wknight8111.blogspot.com/?tag=aio [5] http://nick-black.com/dankwiki /index.php/Fast_UNIX_Servers [6] http://today.java.net/pub/a/today/2007/02/13/architecture-of-highly-scalable-nio-server.html [7] Java NIO ,作者:罗恩·希钦斯[8] http://www.dre.vanderbilt.edu/~schmidt/PDF/reactor-siemens.pdf [9] http://www.cs.wustl.edu/~schmidt/PDF/proactor.pdf [10] http://www.kegel.com/c10k.html参考: I / O来自Source Open博客,由我们的JCG合作伙伴 Buddhika Chamith揭秘。
翻译自: https://www.javacodegeeks.com/2012/08/io-demystified.html