I / O神秘化

由于对高度可扩展的服务器设计的所有炒作以及对nodejs的狂热,我一直想重点研究IO设计模式,直到现在为止都没有足够的时间进行投资。 现在已经做了一些研究,我认为最好记下我遇到的东西,作为对我以及可能遇到这篇文章的任何人的未来参考。 那好吧..让我们跳上I / O总线去兜风。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371676.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java三大特性 继承_java基础(二)-----java的三大特性之继承

在《Think in java》中有这样一句话&#xff1a;复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言&#xff0c;仅仅能够复制代码并对加以改变是不够的&#xff0c;它还必须能够做更多的事情。在这句话中最引人注目的是“复用代码”,尽可能的复用代码使我们程…

delphi用TAdoStoredProc调用存储过程,兼容sql2005、2008、2014的远程事务问题

delphi7写的程序&#xff0c;在sql2000里没问题&#xff0c;调用sql2008、2014里的存储过程时&#xff0c;如果存储过程里操作了大量数据&#xff0c;很容易会莫名其妙的自己撤销掉&#xff0c;但是程序还识别不到&#xff0c;认为还在正常执行。今天尝试换了个控件&#xff1a…

8、SpringCloud高频面试题-版本1

1、SpringCloud组件有哪些 SpringCloud 是一系列框架的有序集合。它利用 SpringBoot 的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;都可以用 SpringBoot 的开发风格做到一键启…

JavaOne 2012:Java策略主题演讲和IBM主题演讲

与 JavaOne 2010 相似&#xff0c;我对JavaOne 2012的开始也很艰难。由于“计算机和打印机技术上的困难”&#xff0c;办理登机手续的人花了70分钟为我提供JavaOne徽章。 尽管我不是世界上最有耐心的人&#xff0c;但比等待更令人失望的是&#xff0c;我错过了参加“社区会议&a…

java citymap_Java实现Map集合二级联动

Map集合可以保存键值映射关系&#xff0c;这非常适合本实例所需要的数据结构&#xff0c;所有省份信息可以保存为Map集合的键&#xff0c;而每个键可以保存对应的城市信息&#xff0c;本实例就是利用Map集合实现了省市级联选择框&#xff0c;当选择省份信息时&#xff0c;将改变…

JavaOne 2012:使用HTML5和Java构建移动应用程序

我返回了Parc 55 &#xff08;任务会议室&#xff09;&#xff0c;观看Max Katz的&#xff08; Exadel开发人员关系&#xff09;“用HTML5和Java构建移动应用程序” Bird-of-Feather&#xff08;BoF&#xff09;演示。 具体来说&#xff0c;Katz在Tiggzi &#xff08;基于云的应…

HDU 2602.Bone Collector-动态规划0-1背包

Bone Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 85530 Accepted Submission(s): 35381 Problem DescriptionMany years ago , in Teddy’s hometown there was a man who was called “Bone Col…

简单谈谈js中的MVC

MVC是什么&#xff1f; MVC是一种架构模式&#xff0c;它将应用抽象为3个部分&#xff1a;模型&#xff08;数据&#xff09;、视图、控制器&#xff08;分发器&#xff09;。 本文将用一个经典的例子todoList来展开&#xff08;代码在最后&#xff09;。 一个事件发生的过程&a…

BTrace:Java开发人员工具箱中的隐藏宝石

这篇文章是关于BTrace的 &#xff0c;我正在考虑将其作为Java开发人员的隐藏宝藏。 BTrace是用于Java平台的安全&#xff0c;动态跟踪工具。 BTrace可用于动态跟踪正在运行的Java程序&#xff08;类似于DTrace&#xff0c;适用于OpenSolaris应用程序和OS&#xff09;。 不久&am…

Spring–添加SpringMVC –第2部分

在上一部分中&#xff0c;我们为经理和员工实现了控制器。 既然我们知道了解决方法&#xff0c;我们将做很少&#xff08;但仅做很少&#xff09;更复杂的事情–任务和时间表的控制器。 因此&#xff0c;让我们从org.timesheet.web开始。 TaskController 。 首先创建一个类&…

2016 Android Top 10 Library

过去的 2016 年&#xff0c;开源社区异常活跃&#xff0c;很多个人与公司争相开源自己的项目&#xff0c;让人眼花缭乱&#xff0c;然而有些项目只是昙花一现&#xff0c;有些项目却持久创造价值&#xff0c;为开发者提供了极大的便利&#xff0c;这些终究由时间来判断。今天&a…

您在eXo平台上的第一个Juzu Portlet

菊珠是佛教的佛珠。 一句话&#xff0c;我相信您已经学到了什么&#xff0c;印象深刻吗&#xff1f; 好的&#xff0c;我在这里不谈论佛教。 Juzu还是一个用于快速开发Portlet&#xff08;以及即将推出的独立应用程序&#xff09;的新框架。 您可以在Juzu网站上找到所需的所有…

Spring注入方式及注解配置

一&#xff1a;基于xml的DI&#xff08;Dependency Injection&#xff09; 注入类型&#xff1a; 定义学生Student实体类和小汽车Car实体类&#xff1a;进行封装和生成ToString(),并自定义属性Car Student 123456789101112131415161718192021222324252627282930313233343536373…

修改readonly属性的值

一般情况下&#xff0c;readonly属性的值是无法修改的&#xff0c;但可以通过特殊方式修改。定义一个student的类&#xff0c;其中name属性为readonly类型的变量 interface JFStudent : NSObjectproperty(nonatomic,copy,readonly) NSString *hisName;property(nonatomic,copy)…

ReactNative开发环境

此内容根据徐赢老师的文档整理后写处 原版地址&#xff1a;https://tuomaxu.gitbooks.io/reactnative/content/ ReactNative是跨平开发的解决方案&#xff0c;在开发平台的选择上&#xff0c;mac平台和win平台都可以。 所需要工具如下&#xff1a; 1&#xff0c;Nodejs环境 2&a…

MediaInfo源代码分析 1:整体结构

博客地址&#xff1a;http://blog.csdn.net/leixiaohua1020/article/details/12016231 MediaInfo源代码分析系列文章列表&#xff1a; MediaInfo源代码分析 1&#xff1a;整体结构MediaInfo源代码分析 2&#xff1a;API函数MediaInfo源代码分析 3&#xff1a;Open()函数MediaI…

射线碰撞检测

在我们的游戏开发过程中&#xff0c;有一个很重要的工作就是进行碰撞检测。例如在射击游戏中子弹是否击中敌人&#xff0c;在RPG游戏中是否捡到装备等等。在进行碰撞检测时&#xff0c;我们最常用的工具就是射线&#xff0c;Unity 3D的物理引擎也为我们提供了射线类以及相关的函…

php注册登录遍写入 遍验证,自动注册登录验证机制的php代码

在phpwind站点后台添加“广告管家”(CNZZ的一款广告投放的应用)的应用&#xff0c;整个“广告管家”通过iframe载入&#xff0c;载入的具体内容根据不同站点显示针对该站点的具体内容。出于意用性方面的考虑&#xff0c;需要以下二点&#xff1a;1、首次进入“广告管家”页面自…

Apache Wicket:记住我的功能

在Web应用程序中&#xff0c;具有“记住我”功能非常普遍&#xff0c;该功能使用户每次访问我们的网站时都能自动登录。 可以使用Spring Security来实现这种功能&#xff0c;但我认为将基于请求的身份验证框架与基于组件的Web框架结合使用并不是最好的主意。 这两个世界不能很好…

Ubuntu 安装中文

系统环境&#xff1a; 1. 官网 http://pinyin.sogou.com/linux/ 下载安装包。 2. 先运行 apt-get update 。 3. 再运行 apt-get -f install 。 4. 再运行 可能有的UBuntu系统自带了。 5. 如果下载的搜狐输入法安装包的格式为 .deb 的&#xff0c; 运行 &#xff1a; dpk…