Performance analysis of the Java I/O API (i)

Source: Internet
Author: User
Tags bind constructor contains requires resource socket thread client
The scalability of performance IO APIs is of great importance to Web applications. In the previous API for Java version 1.4, blocking I/O was disappointing to many people. Starting with the J2SE 1.4 version, Java finally has a scalable I/O API. This paper analyzes and calculates the variability of the new and old I/O APIs in terms of scalability.

I. Overview

The scalability of the IO API is of great importance to Web applications. In the previous API for Java version 1.4, blocking I/O was disappointing to many people. Starting with the J2SE 1.4 version, Java finally has a scalable I/O API. This paper analyzes and calculates the scalability differences between the new and old IO APIs. When Java writes data to the socket, it must invoke the associated OutputStream write () method. The Write () method call is returned only if all of the data is written. This call may take some time to complete if the send buffer is full and the connection speed is low. If the program uses only a single thread, other connections must wait, even if those connections are ready to invoke write (). To solve this problem, you have to associate each socket with a thread; After this method, another thread can still run when one thread is blocked by an I/O-related task.

Although threads are less expensive than processes, threads and processes are part of a large resource-consuming program structure, given the underlying operating platform. Each thread consumes a certain amount of memory, and beyond that, multiple threads also imply a switch to the thread context, which also requires expensive resource overhead. As a result, Java needs a new API to isolate the connection between the socket and the thread too tightly. In the new Java I/O API (java.nio.*), this goal is finally realized.

This article analyzes and compares a simple Web server written with both new and old I/O APIs. Because HTTP as a Web protocol is no longer used for simple purposes, the examples presented here contain only key features, or they do not take security into account and do not strictly comply with protocol specifications.

Ii. HTTP servers written with the old API

First, let's look at HTTP servers written with legacy APIs. This implementation uses only one class. The main () method first creates a serversocket bound to port 8080:


public static void Main () throws IOException {
ServerSocket serversocket = new ServerSocket (8080);
for (int i=0 i < Integer.parseint (args[0)); i++) {
New HTTPD (ServerSocket);
}
}


Next, the main () method creates a series of httpd objects and initializes them with the shared serversocket. In the HTTPd constructor, we guarantee that each instance has a meaningful name, sets the default protocol, and then starts the server by invoking the start () method of its superclass thread. This leads to an asynchronous call to the run () method, while the run () method contains an infinite loop.

In the infinite loop of the Run () method, the ServerSocket blocking Accpet () method is invoked. When the client connects to the server's 8080 port, the Accept () method returns a Socket object. Each socket is associated with a inputstream and a outputstream, both of which are used in subsequent handlerequest () method calls. This method will read the client's request, be checked and processed, and then send the appropriate answer to the client program. If the client's request is legitimate, the file requested by the client is returned through the Sendfile () method, otherwise the client receives the appropriate error message (call Senderror ()) method.


while (true) {
...
Socket = Serversocket.accept ();
...
HandleRequest ();
...
Socket.close ();
}


Now let's analyze this implementation. Is it able to do the job well? The answer is basically yes. Of course, the request analysis process can be further optimized because the StringTokenizer reputation has been poor in terms of performance. However, this program has at least shut down the TCP latency (which is not appropriate for a short connection), and has a buffer for outgoing files. And more importantly, all thread operations are independent of each other. Which thread the new connection request handles is determined by the native (and thus the faster) accept () method. In addition to ServerSocket objects, each thread does not share any other resources that might need to be synchronized. The solution is faster, but unfortunately, it is not very scalable, because, obviously, threads are a limited resource, "he explained."

Third, non-blocking HTTP server

Let's look at another scenario that uses a new non-blocking I/O API. The new scenario is a little more complex than the original, and it requires the collaboration of each thread. It contains the following four classes:

· Niohttpd
· Acceptor
· Connection
· Connectionselector

The primary task of NIOHTTPD is to start the server. Just like the previous httpd, a server socket is bound to port 8080. The main difference is that the new version of the server uses Java.nio.channels.ServerSocketChannel rather than serversocket. Before you can use the bind () method to explicitly bind a socket to a port, you must first open a pipe (Channel). The main () method then instantiates a connectionselector and a acceptor. In this way, each connectionselector can be registered with a acceptor and serversocketchannel is also provided when instantiating acceptor.


public static void Main () throws IOException {
Serversocketchannel SSC = Serversocketchannel.open ();
Ssc.socket (). bind (New inetsocketaddress (8080));
Connectionselector cs = new Connectionselector ();
New Acceptor (SSC, CS);
}


In order to understand the interaction between these two threads, first let's look at the acceptor. The primary task of acceptor is to accept incoming connection requests and register them through Connectionselector. The Acceptor constructor calls the superclass's start () method, and the Run () method contains the required infinite loops. In this loop, a blocking accept () method is invoked, which eventually returns a socket object-a process that is almost identical to the httpd process, but where the Serversocketchannel accept () method is used, Rather than the ServerSocket accept () method. Finally, the Socketchannel object obtained by calling the Accept () method creates a connection object for the parameter and registers it with the Connectionselector queue () method.


while (true) {
...
Socketchannel = Serversocketchannel.accept ();
Connectionselector.queue (New Connection (Socketchannel));
...
}


In summary: Acceptor can only accept connection requests and register connections through connectionselector in an infinite loop. Like Acceptor, Connectionselector is also a thread. In the constructor, it constructs a queue and opens a java.nio.channels.Selector with the Selector.open () method. Selector is one of the most important parts of the entire server, enabling programs to register connections 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 inside the Run () method begins execution. In this loop, the program invokes the selector Select () method. This method blocks until one of the registered connections is ready for I/O operations, or the selector wakeup () method is invoked.


while (true) {
...
int i = Selector.select ();
Registerqueuedconnections ();
...
Process Connection ...
}

When a connectionselector thread executes a select (), no acceptor thread can register the connection with that selector, because it is important to understand that the corresponding method is the synchronization method. Therefore, queues are used here, and acceptor threads join the queue if necessary.


public void Queue (Connection Connection) {
Synchronized (queue) {
Queue.add (connection);
}
Selector.wakeup ();
}


Immediately after putting the connection into the queue, acceptor calls the selector wakeup () method. This call causes the Connectionselector thread to continue executing, returning from the blocked select () call. Because the selector is no longer blocked, Connectionselector is now able to register the connection from the queue. In the Registerqueuedconnections () method, the implementation process is as follows:


if (!queue.isempty ()) {
Synchronized (queue) {
while (!queue.isempty ()) {
Connection Connection =
(Connection) Queue.remove (Queue.size ()-1);
Connection.register (selector);
}
}
}


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.