Performance Analysis of Java IO API (Part)

xiaoxiao2021-03-06  47

IO API scalability has extremely important for web applications. In the previous API of Java 1.4, blocking I / O made many people disappointing. From the J2SE 1.4 version, Java finally has a retractable I / O API. This paper analyzes and calculates the difference in scalability in scalability.

I. Overview

IO API scalability has extremely important for web applications. In the previous API of Java 1.4, blocking I / O made many people disappointing. From the J2SE 1.4 version, Java finally has a retractable I / O API. This paper analyzes and calculated the difference in scalability in scalability. Java must call the associated OutputStream Write () method when writing data to Socket. Write () method call will only be returned if all data is written. If the transmission buffer is full and the connection speed is low, this call may take a while to complete. If the program only uses a single thread, other connections must be awaited, even if those connectors have been prepared to call WRITE (). In order to solve this problem, you must associate each socket and a thread; after this approach, another thread is still able to run when a thread is blocked due to I / O related tasks.

Although the overhead of threads is not as large as the process, considering the operating platform, threads and processes that consider a large amount of resources. Each thread should take a certain number of memory, and in addition to this, multiple threads also means switching of thread context, and this switching requires expensive resource overhead. Therefore, Java requires a new API to isolate the contaction between Socket and threads. This goal is finally implemented in the new Java I / O API (Java.nio. *).

This paper analyzes and compares a simple web server written in new, old two I / O APIs. Since the HTTP as a web protocol is no longer used only for some simple purposes, the examples described here only contain critical features, or they do neither consider security factors, and do not strictly follow the protocol specifications.

Second, the HTTP server written in the old API

First let's take a look at the HTTP server written in the old API. This implementation only uses a class. The main () method first created a serversocket bound to the 8080 port:

Public static void main () throws oException {

Serversocket Serversocket = New ServerSocket (8080);

For (int i = 0; I

New httpd (serversocket);

}

}

Next, the Main () method creates a range of HTTPD objects and initializes them with shared serversocket. In HTTPD constructor, we guarantee that every instance has a meaningful name, set the default protocol, and then start the server by calling its superclass Thread's Start () method. This move leads to an asynchronous call to the Run () method, and the Run () method includes an infinite loop.

In the infinite loop of the Run () method, the blocking ACCPET () method of ServerSocket is called. When the customer program connects the server's 8080 port, the accept () method will return a socket object. Each socket is associated with an InputStream and an OutputStream, both of which are used in successive handleRequest () method calls. This method reads the client's request, check and handle it, and then send the appropriate response to the client. If the client's request is legal, return to the client request by sendfile () method; otherwise, the client will receive the corresponding error message (call the senderror ()) method. While (true) {

...

Socket = serversocket.accept ();

...

Handlerequest ();

...

Socket.close ();

}

Now let's analyze this implementation. Can it complete the task well? The answer is basically affirmative. Of course, the request analysis process can also be further optimized because StringTokenizer's reputation is not good in performance. But this program has closed the TCP delay (it is very unsuitable for short connections), and the buffer is set for the outstanding file. Moreover, more importantly, all thread operations are independent of each other. The new connection request is determined by this machine (thus is also a faster speed) ACCEPT () method. In addition to the ServerSocket object, any other resources that may need to be synchronized between each thread. This program is faster, but unfortunately, it does not have good scalability, the reason is that it is clear that threads are a limited resource.

Third, non-blocking HTTP server

Let's take a look at another solution for another new I / O API using non-blocking. The new solution is slightly more complicated than the original program, and it requires collaboration of each thread. It contains the following four classes:

· NiOHTTPD

· Acceptor

· Connection

ConnectionSelector

The main task of NiOHTTPD is to start the server. Like the previous HTTPD, a server socket is bound to the 8080 port. The main difference between the two is that the new version of the server uses java.nio.channels.serversocketchannel instead of Serversocket. Before using the bind () method explicitly binds the socket to the port, you must first open a pipe (CHANNEL). The main () method then instantiates a ConnectionSelector and an Acceptor. In this way, every ConnectionSelector can be registered with an Acceptor; in addition, instantiate Acceptor, it provides ServerSocketChannel.

Public static void main () throws oException {

ServersocketChannel SSC = ServersocketChannel.Open ();

Ssc.socket (). Bind (New InetSocketAddress);

ConnectionSelector CS = New ConnectionSelector ();

NEW Acceptor (SSC, CS);

}

In order to understand the interaction process between the two threads, first we carefully analyze the Acceptor. The main task of Acceptor is to accept incoming connection requests and register them through the ConnectionSelector. The constructor of Acceptor calls the super class Start () method; the run () method contains the necessary infinite loops. In this loop, an obstructive accept () method is called, which eventually returns a socket object - this process is almost the same as the HTTPD process, but use the Accept () method of ServersocketChannel, not Serversocket ACCEPT () method. Finally, create a Connection object with the SocketChannel object obtained by calling the accept () method, and registers it through the Queue () method of the ConnectionSelector. While (true) {

...

SocketChannel = ServersocketChannel.Accept ();

New Connection (SocketChannel);

...

}

All in all: Acceptor can only accept connection requests in an infinite loop and register with ConnectionSelector. Like Acceptor, ConnectionSelector is also a thread. In the constructor, it constructs a queue and opened a java.nio.channels.selector with the selector.open () method. Selector is one of the most important parts of the entire server, which allows programs to register, and get a list of connections that have been allowed to read and write operations.

After the constructor calls the start () method, the infinite loop in the run () method begins. In this loop, the program calls the Select () method of the Selector. This method has been blocked until one of the registered connections is ready, or the Selector's Wakeup () method is called.

While (true) {

...

INT i = selector.select ();

RegisterQueuedConnections ();

...

// Handling connection ...

}

When the ConnectionSelector thread performs Select (), no Acceptor thread can be registered with the selector, because the corresponding method is the synchronization method, it is important to understand this. So the queue is used here, if necessary, the Acceptor thread is added to the queue.

Public void Queue (Connection Connection) {

Synchronized (queue) {

Queue.Add (Connection);

}

Selector.wakeup ();

}

Then follow the connection of the connection to the queue, and the Acceptor calls the selector's Wakeup () method. This call causes the ConnectionSelector thread to continue, and returns from the select () call being blocked. Since Selector is no longer blocked, ConnectionSelector can now register from queues. In the RegisterQueuedConnections () method, its implementation process is as follows:

IF (! queue.isempty ()) {

Synchronized (queue) {

While (! queue.isempty ()) {

Connection connection =

Queue.Remove (Queue.Size () - 1);

Connection.Register (selector);

}

}

}

转载请注明原文地址:https://www.9cbs.com/read-74964.html

New Post(0)