Fourth, the registration and processing process detailed
Next we want to analyze the register () method of the connection. We always say that the connection to SELECTOR is a simplified statement. In fact, using selector is a java.nio.channels.SocketChannel object, but only for a specific I / O operation. After registration, there is a java.nio.channels.selectionKey returned. This selection button can be associated with any object via the attach () method. To get a connection through a key, the Connection object is associated with the key. This way, we can indirectly get a connection from Selector.
Public void register (selector selector)
THROWS IOEXCEPTION {
Key = socketchannel.register (selector, selectionKey.op_read);
Key.attach (this);
}
Go back and see ConnectionSelector. The return value of the select () method indicates how many connections have been prepared for I / O operations. If the return value is 0, return; otherwise, call the selectedKeys () get a collection of key (SET), get the previously associated Connection object, then call its readRequest () or WriteResponse () method, which method The connection is determined by the connection to the reading operation or the write operation is determined.
Now let's take a look at the Connection class. The Connection class represents the connection to process details related to all protocols. In the constructor, the SocketChannel of the parameters is set to Non-blocking mode, which is important for the server. In addition, the constructor also sets some default values to allocate the buffer RequestLineBuffer. Since the distribution direct buffer cost is slightly high, each connection here is used with a new buffer, so use java.nio.bytebuffer.allocate () instead of bytebuffer.allocatedIRect (). If the buffer is reused, the direct buffer may have higher efficiency.
Public Connection (SocketChannel Socketchannel)
THROWS IOEXCEPTION {
THIS. SocketChannel = Socketchannel;
...
SocketChannel.configureBlocking (false);
RequestlineBuffer = Bytebuffer.allocate (512);
...
}
Complete all initialization work and SocketChannel After reading preparation, the ConnectionSelector calls the ReadRequest () method, using the SocketChannel.Read (RequestLineBuffer) method to read all available data into the buffer. If you cannot read a complete line, return the CONNECTIONSELECTOR that is called, allowing another connection to the processing process; Converse, if the entire row is successfully read, the next parsing request is like in HTTPD. If the current request is legal, the program creates a java.nio.channels.filechannel for the request target file, and calls the preparentforResponse () method.
Private void prepareForResponse () throws oException {
StringBuffer responseline = new stringbuffer (128);
...
ResponseLineBuffer = bytebuffer.wrap (responseline.toString (). getBytes ("ASCII")
);
Key.INTERESTOPS (SelectionKey.op_write);
Key.selector (). Wakeup ();
}
The prepareForResponse () method constructs buffer ResponseLine and (if necessary) responders or error messages, and writing these data to ResponseLineBuffer. This bytebuffer is a simple package for a BYTE array. After generating the data to be output, we also want to notify the ConnectionSelector: From now on, it is no longer reading data, but to write data. This notification is done by calling the INTERESTEDOPS (SelectionKey.op_write) method of the selection key. In order to ensure that the selector can quickly recognize changes in the connection operation status, the Wakeup () method is also called. Next, ConnectionSelector calls the connected WriteResponse () method. First, ResponseLineBuffer is written to the socket pipe. If the contents of the buffer are written, and there is also the requested files to be sent, then call the previously opened FileChannel TRANSFERTO () method. The transferto () method typically becomes efficiently transmitting data from files to the pipe, but the actual transmission efficiency depends on the underlying operating system. At any time, the amount of data transmitted is mostly equivalent to the amount of data that can be written to the target conduit without blocking. To secure and ensure the fairness between each connection, the upper limit is set to 64 KB.
If all data has been transferred, Close () performs cleanup work. Canceling the registration of Connection is the main task here, which is done by the Cancel () method of the call button.
Public void close () {
...
IF (key! = null) key.cancel ();
...
}
What is this new program performance? The answer is yes. From the principle, an Acceptor and a Connectionslector are sufficient to support any number of open connections. Therefore, new implementation plays an advantage in scalability. However, since the two threads must be communicated by synchronous Queue () methods, they may block each other. There are two ways to solve this problem:
· Improve method for realizing queues
· Adopt multiple Acceptor / ConnectionSelector
One disadvantage of NiOhttpd is that a shortcoming of Niohttpd is that a new buffered Connection object is created for each request. This leads to the additional CPU occupancy generated by the garbage collector, which is related to the VM type. However, Sun is not annoying to say that there is HotSpot, and the short-term survival object is no longer a problem.
V. Quantitative analysis and comparison of scalability
In terms of scalability, how much is NiOHTTPD to the bottom than httpd? Let's take a look at the specific numbers. First, it is necessary to declare that the numbers here have a large number of estimated components, some important environmental factors, such as thread synchronization, context switching, switching, hard disk speed, and buffer, etc., are not considered. First evaluate how much time it takes to process R compact, assuming that the requested file size is an S byte, the bandwidth of the client is b byte / second. For httpd, this time is clearly directly dependent on the number of threads, because the T-request can only be processed at the same time. Therefore, the processing time of HTTPD can be obtained from the formula, where c is the overhead constant of the operation of the request analysis, which is the same for each request. In addition, here it is assumed that the speed of reading data from the disk is always quickly written to the Socket, and the server bandwidth is always greater than the sum of the client bandwidth, and the CPU is not fully loaded. Therefore, both the server-side bandwidth, buffer, and hard disk speeds are not necessarily considered in this formula. However, the processing time of NiOHTTPD is no longer dependent on T. For NiOHTTPD, the transfer time L depends to a large extent, depending on the bandwidth B, file size S, and constant C mentioned previously. This can be obtained, and the formula 2 can be obtained from the formula to obtain the minimum transmission time of the NiOhttpd.
Note that the ratio of the formula I is D, which measures the performance comparison between NiOhttpd and HTTPD.
Further analysis shows that if S, B, T and C are constants, R tendency to grow in endless, this limit can be conveniently calculated from equation 4.
Therefore, in addition to the number of threads and constant overhead, the time S / B connected to D is extremely important. The longer the connection lasts, the smaller the D value, the higher the advantages of NiOHTPD comparison HTTPD. Table 1 shows that when C = 10 ms, t = 100, s = 1MB, B = 8kb / s, NiOHTTPD is 126 times faster than HTTPD. If the connection lasts for a long time, NiOHTTPD shows a huge advantage. When the connection time is short, for example within a local area network of 100 MB, if the file is large, NiOHTTPD exhibits 10% advantage; if the file is small, the advantage is not obvious.