神秘的数组初始化

由于对高度可扩展的服务器设计的所有炒作以及对Node.js的狂热,我一直想重点研究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()调用,该调用将返回一组SelectionKey,然后可以对其进行处理
使用应用程序指定的事件处理程序一个接一个地处理。

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模式有以下几种。

React堆模式

有几个组件参与此模式。 我将首先研究它们,这样很容易理解该图。

Reactor Initiator:这是通过配置和启动调度程序来启动非阻塞服务器的组件。 首先,它将绑定服务器套接字,并将其注册到解复用器中,以进行客户端连接接受就绪事件。 然后,将为调度程序注册每种就绪事件类型(读/写/接受等)的事件处理程序实现。 接下来,调度程序事件循环将被调用以处理事件通知。

调度程序:定义用于注册,删除和调度事件处理程序的接口,这些事件处理程序负责对连接事件进行React,这些事件包括连接接受,一组连接上的数据输入/输出和超时事件。 为了服务于客户端连接,相关的事件处理程序(例如: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

神秘的数组初始化

神秘的数组初始化_I / O神秘化相关推荐

  1. 神秘的数组初始化_图像识别神秘化

    神秘的数组初始化 by gk_ 由gk_ 图像识别神秘化 (Image Recognition Demystified) Nothing in machine learning captivates ...

  2. 004:神秘的数组初始化_使容器神秘化101:面向初学者的深入研究容器技术

    004:神秘的数组初始化 by Will Wang 王Will 介绍 (Introduction) Regardless of whether you are a student in school, ...

  3. java如何给数组初始化?

    关于java数组的文章早已是非常多了,本文是对我个人过往学习java,理解及应用java数组的一个总结.此文内容涉及java数组的基本概念,以及java如何给数组初始化?初始化的几种方式?希望对大家有 ...

  4. C/C++数组初始化的一些误区

    以前我这样初始化一个数组,并自我感觉良好: int a[5] = { 0 }; // 全部初始化为0 这种简单的写法让我非常爽,于是我又想把数组全部初始化为1: int a[5] = { 1 }; / ...

  5. 【C】数组数组初始化总结

    C数组初始化总结 发现一个新方法,可以分段初始化数组 eg:int arrayC[MAX_LEN] = {[1 - 5]=9, [6 - 9] = 8}; 代码如下 #include <stdi ...

  6. JAVA学习笔记--数组初始化

    JAVA中,数组只是相同类型的.用一个标识符名称封装到一起的一个对象序列或基本类型数据序列.数组通过方括号下标操作符[]来定义和使用,要定义一个数组只需在类型名后面加上一个方括号即可,如: int[] ...

  7. C语言中字符数组初始化的几种方法

    欢迎关注我的微信公众号:CurryCoder的程序人生 1.C语言中的字符数组初始化 在C语言中,字符串是当做字符数组来处理的:所以字符串有两种声明方式,一种是字符数组,一种是字符指针. 1.1 直接 ...

  8. 【C 语言】数组 ( 数组本质 | 数组长度定义 | 数组初始化 | 编译时初始化 | 显式初始化 - 重置内存 )

    文章目录 一.数组本质 二.数组长度定义 三.数组初始化 1.编译时初始化 2.显式初始化 ( 重置内存 ) 一.数组本质 数组本质 : 类型角度 : 从 数组元素 类型角度分析 , 数组是 相同类型 ...

  9. golang二维数组初始化

    golang二维数组初始化来看一下,有点特别 func main() {x := 2y := 4table := make([][]int, x)for i := range table {table ...

最新文章

  1. Nature指数发榜:中科院总榜夺冠,北大、清华列学术机构Top 10
  2. 网络编程学习笔记(readv和writev函数)
  3. crontab 和 at 的简单区别和用法
  4. PHP的require与include
  5. Apache Hive
  6. Wtm携手LayUI -- .netcore 开源生态我们是认真的!
  7. 洛谷树剖模板题 P3384 | 树链剖分
  8. dell服务器硬盘锁_服务器十大排行
  9. 过年用计算机弹奏,过年实用,你可能需要的亲戚称呼计算器
  10. 浅谈ANR及如何分析解决ANR
  11. Java的arrays运用
  12. Unity 3D | 在Unity3D中创建/执行C#脚本
  13. DOS命令是如何操作目录和文件夹的?
  14. ckeditor拖拽添加html,CKEditor插入HTML
  15. error:failed to run html help compiler on index.hhp
  16. 如何从Excel文件创建在线预算报告
  17. Arduino Web Editor网页编辑器入门
  18. 2021厦大计算机考研炸了,【图片】一战厦大计算机上岸,经验帖。慢更【考研吧】_百度贴吧...
  19. 20165232 结对编程第二周总结
  20. polyline与polygon

热门文章

  1. idea打war包时,JDK版本的问题解决方式
  2. springboot+mybatis-plus实例demo
  3. bootstrap组件的案例代码
  4. 量子计算机对人类长寿,科学家称“极端长寿”在未来几十年可能会达到新的里程碑...
  5. 第3步 (请先看第2步再看第3步) 新建完spring+springmvc+mybatis项目 需要推送gitee仓库进行管理 巨详细
  6. 面试官角度的JVM面试
  7. (转)Kafka 消费者 Java 实现
  8. java 8 Lambda 表达式(副作用)
  9. elk 聚合日志_使用ELK堆栈进行日志聚合
  10. spring vaadin_在Spring Boot中使用Vaadin的简介