Java performance techniques and firewall tunnel technology
Based on JavaSM Developer ConnectionSM, in accordance with the 1998 Javaone Conference on Java Development Skills and Tips for Tips for Java Developers, this article gives this article. A large number of performance adjustments and firewall tunnel techniques for use. These technologies will be described by the original code mating legend, the original code can be downloaded.
Performance --Applet download speed
An important factor affecting Applet download performance is that it issues the number of request data to the server. A enhanced performance is to package the Applet image into a single class file.
Usually, if the applet has six graphics buttons, you need to send additional six requests to the web server to download these image files. Inside the Internet, this doesn't seem to any inconvenience, but considering the relaxation of speed and reliability, these additional requests may have a serious adverse effect. Because the ultimate goal is to download Applet as soon as possible.
One way to store images in a class file is to use some schemes encoded by ASCII - such as X-PIXMAP (XPM). In this scenario, the image is not saved on the server in a GIF file format, but the image file is encoded as a string and stored in a single class file. The following code example uses the package of JavaCup winners in Javaone'96. These classes - XImagesource and XPMPARSER - provide all methods that need to read standard XPM files (which can be found in SunSite). As an initialization encoding process, you can create an XPM file with a variety of graphics tools. On Solaris is ImageTool, as well as many other GNU image packages.
In the next example, you will use the button image below:
The same buttons are also available in the GroupReader Applet of Java Developer Connection, and you can see the form of strings after encoding images in XPM.
Assume that six member variables are used in the sample class MyApplet. These variables are: _reply, _post, _reload, _catchup, _back10, _reset, and _faq.
The following code implements loading of images. For each image, Toolkit is used to create images from the XPM image source object.
Toolkit Kit = Toolkit.getDefaultToolkit ();
Image image;
Image = kit.createImage (New Ximagesource (_Reply);
Image = kit.createImage (New Ximagesource (_POST));
Image = kit.createImage (New Ximagesource (_RELOAD));
Image = kit.createImage (New Ximagesource (_CATCHUP));
Image = kit.createImage (new ximagesource);
Image = kit.createImage (New Ximagesource (_RESET));
Image = kit.createImage (new ximagesource (_faq));
The above technologies reduce network traffic because the definition of each image is part of a single class file. Another technology is to use a GIF file (see below), which requires a request to the web server for each loaded image.
Image image;
Image = getImage ("reply.gif");
Image = GetImage ("post.gif"); image = GetImage ("Reload.gif");
Image = GetImage ("catchup.gif");
Image = GetImage ("Back10.gif");
Image = GetImage ("reset.gif");
Image = GetImage ("FAQ.gif");
In fact, using XPM encoded images, the class file is increased, but the number of network requests is reduced. By making an XPM image defining a part of the Applet class file, the image load process is part of the Applet class file generally loaded without the additional classes! Thus, these images can be used to create buttons or other UI components. If the Java Foundation Class - JFC is used, such as JButton, the image can be used as follows:
Imageicon icon = new imageicon
Kit.createImage (New XImagesource (_Reply)));
JButton Button = New JButton (icon, "reply");
Another way to improve Applet download performance is to use JAR (Java Archive) files. By using this document flag on the HTML page, you can specify a list of JAR files to include all Applet resources. The advantage of using JAR files is that all files related to applet are used in a file for download. Disadvantile, if you have multiple JAR files, the class loader (ClassLoader) will load each JAR file when the applet is started. This way, when not using resources very frequently, the JAR file containing these files will be downloaded anyway - no matter whether these resources are used.
The method of solving this JAR file problem is to put only those frequent files into the document (JAR file). In this way, the real-needed file will be downloaded in a request. Uncommonly removed the resources from the JAR file to ensure that they are downloaded only when they need it.
Thread pools - runtime performance
JDC's Applet (Java Developer Connection Applet server uses thread pool to improve performance. This technology is also used on the Java Web Server. For a multi-threaded background knowledge, please refer to the relevant article.
The driving force behind the thread pool is that from the perspective of system resources, the startup process of the thread is a big overhead. In this way, create a reserve of the ready-to-peer thread that is about to enter the front desk, then store the sleep thread thread pool is a more efficient performance scheme.
The following code details a method of implementing a thread pool. In the thread pool constructor (see Pool.java and below), Workerthread first is initialized and started. The call to start () executes the RUN () method of the Workerthread. In Run (), wait () call makes the thread hang - waiting for the next step. Then, the sleep thread is pushed into the stack.
Worker worker;
Workerthread W;
For (int i = 0; i <_max; i ) {
Worker = (worker) _workerclass.newinstance ();
W = New Workerthread ("Worker #" i, worker);
W.Start ();
_waiting.push (w);
Workerthread has two methods, Wake () and Run () (Run have discussed above). When the operation arrives, the wake () method is called, the method is assigned and informs the sleep thread (which is initialized by the thread pool). Wake () method The call to Notify () causes the blocked thread to disengage the waiting state, then execute the httpserverWorker's Run () method. When this operation is complete, workerthread is put back to the stack (assuming the thread pool is not full), or simply terminates.
Synchronized void wake (Object data) {
_DATA = data;
NOTIFY ();
}
Synchronized public void run () {
Boolean stop = false;
While (! stop) {
IF (_data == null) {
Try {
Wait ();
} catch (interruptedexception e) {
E.PrintStackTrace ();
CONTINUE;
}
}
IF (_data! = null) {
_worker.run (_data);
}
_DATA = NULL;
STOP =! (_ push (this));
}
}
At the highest level, the next work is processed by PerformWork () (see pool.java). Here, when an operation arrives, an existing WorkerthRead will pop up from the stack (if the thread pool is happens to be empty, create a new). Then, the Workerthread sleep is activated by a call using its Wake () method.
Public void PerformWork (Object Data)
Throws instantiationException {
Workerthread w = NULL;
Synchronized (_waiting) {
IF (_waiting.empty ()) {
Try {
W = New Workerthread ("Additional Worker",
(Worker) _workerclass.newinstance ());
W.Start ();
} catch (exception e) {
Throw new installationException
Problem Creating Instance of Worker.class: "
E.getMessage ());
}
} else {
W = (workerthread) _waiting.pop ();
}
}
W.Wake (data);
}
In order to indicate that the thread pool class code is running, refer to the discussion of firewall tunnel technology in the HTTPSERVER class in the next section.
Try {8888
_pool = new pool (poolsize, httpserverworker.class);
} catch (exception e) {
E.PrintStackTrace ();
Throw new interfacerror (E.getMessage ());
}
Among the HTTPServer constructors above, a new thread pool instance is created to maintain the HTTPSERVERWORKER instance. These examples are created and stored as part of the workerthread data. When you activate Workerthread (by calling Wake ()), an instance of the latter (see httpserverWorker.java) is called through the httpserverworker's Run () method. The following code appears in the primary service routine of HTTPServer (see httpserver.java). Each time a request arrives, the thread initializes its data and starts its operation. Note: If you use too much system overhead to create a new hash table for each workerthread, you need to modify this code without using the Worker abstraction class. Try {
Socket s = _serversocket.accept ();
Hashtable data = new hashtable ();
Data.Put ("socket", s);
Data.Put ("httpserver", this);
_Pool.PerformWork (DATA);
} catch (exception e) {
E.PrintStackTrace ();
} 88
Firewall tunnel
The firewall tunnel technology is first in JDC, as part of the chat applet (see JDC Article Burrowing Through FireWalls). The JDC Engineering Group expanded these technologies in its new firewall tunnel framework and solved other corresponding problems. The next discussion relates to many of the above performance discussions in the section.
The challenge to be solved by the firewall tunnel is how to confer an interactive interactibility that is available to the user of the user with the user of the user through the HTTP proxy. With the following technique, JDC has completed multiplexing for Applet, which allows them to send and receive information through the firewall.
The following section describes the HTTP tunnel technology.
The communication of the client to the server
Sending a simple message from the client to the server is implemented by using the URLConnection class.
First, the client creates a new URLCONNECTION to send a request to the server (see httpclient.java). The server responds and then close the connection. Usually, communication in this direction rarely occurs problems, so that the code is relatively simple.
Synchronized public byte [] send (byte data [])
THROWS IOEXCEPTION {
BYTE BUFFER [];
// ESTABLISH A Connection
URL URL = New URL (_URL);
UrlConnection Connection = Url.openConnection ();
Connection.SetuseCaches (FALSE);
Connection.SetDoOutput (TRUE);
// WRITE OUT THE DATA
Dataoutputstream Data = New DataOutputStream
New bufferedoutputstream
Connection.getOutputStream ()));
DataOut.writeInt (httpclient.data);
DataOut.writeInt (Data.Length);
DataOut.write (data);
Dataout.flush ();
Dataout.close ();
Int length;
DataInputStream INPUT = New DataInputStream
New BufferedInputStream
Connection.getInputStream ()));
INT type = INPUT.Readint (); if (type == httpclient.data) {
Length = INPUT.READINT ();
Buffer = new byte [length];
INPUT.Readfully (BUFFER);
} else {
Buffer = NULL;
Throw new oException ("Unknown Response Type";
}
INPUT.CLOSE ();
Return buffer;
}
The point where the code should be noted is:
The URLConnection must be set (such as described in the API) to make it do not use a cache and allows Posting-SetuseChes (False) and SetDoOutput (TRUE).
Due to performance, the input and output stream of UrlConnection uses bufferedio streams.
The data read and written contains additional information such as the type and length of the read and write data.
On the server, the code is substantially the same (see httpserverworker.java).
Public void run (object data) {
Socket Socket = (Socket) ((Hashtable) DATA .GET ("socket");
Httpserver Server = (HSHTABLE) DATA .GET ("httpserver");
Try {
DataInputStream Input = New DataInputStream (New
BufferedInputStream (Socket.getInputStream ()));
String line = INPUT.READLINE ();
IF (line.touppercase (). StartSwith ("post")) {
For (; (line = INPUT.READLINE ()). Length ()> 0;);
INT type = input.readint ()
Switch (Type) {
Case httpclient.data: {
INT length = INPUT.READINT ();
BYTE BUFFER [] = new byte [length];
INPUT.Readfully (BUFFER);
ByteArrayoutputStream DataOut = New
ByteArrayoutputStream ();
Server.NotifyListener (New ByteArrayinputStream
(buffer); DataOut;
DataOutputStream Output = New DataOutputStream
New bufferedoutputstream (socket.
GetOutputStream ()));
Output.writebytes (_HTTPRESPONSE);
Output.writeint (httpclient.data);
Output.writeInt (Dataout.TobyteaRray (). Length);
Output.write (Dataout.TobyteArray ());
Output.flush ();
INPUT.CLOSE ();
Output.close ();
Socket.close ();
Break;
}
DEFAULT: {
System.err.println ("Invalid Type:" TYPE);
}
}
} else {system.err.println ("INVALID HTTP Request: line);
}
} catch (ioexception e) {
E.PrintStackTrace ();
Try {
Socket.close ();
} catch (exception e) {
}
}
}
The HTTPWORDERSERVER class implements Worker from the thread pool package. Data objects transmitted to the RUN method include socket objects and other information. The key points worth noting in the code are:
Each request must be processed as a real HTTP request - check the correct HTTP header.
Similar to the client's code, the IO stream of the socket uses bufferedio stream.
When the response is sent, the correct HTTP header must be included.
The communication of the client to the service is relatively simple. However, when a suspend connection is set between the client and the server (to allow the server to initiate information connection), the code becomes more complicated.
Communication to the client to the client
Allows the server to send information to the client to request the connection between the parties to the handshake protocol. One of the easiest ways to establish a connection is to create a URLConnection by the client, keeping its connection status until it is ready to send data.
The settings for the client are quite simple, as the following sample code (see httpclient.java).
CONNECTION = _SENDPENDPENDING ();
DataOutputStream Output = New
DataOutputStream (connection.getOutputStream ());
Output.writeint (Pending);
Output.flush ();
Output.close ();
Output = NULL;
INT CODE = (httpurlconnection ".getResponsecode ();
Switch (code) {
Case 200: {// http_ok
DataInputStream INPUT = New
DataInputStream (connection.getinputstream ());
Length = INPUT.READINT ();
BYTE BUFFER [] = new byte [length];
IF (Length> = 0) {
INPUT.Readfully (BUFFER);
_Listener.Service
New ByteArrayInputStream (buffer);
} else {
System.err.Println ("Invalid Length:"
Length);
}
Buffer = NULL;
INPUT.CLOSE ();
INPUT = NULL;
Break;
}
Case 504: {// http_gateway_timeout
CONNECTION = _SENDPENDPENDING ();
Break;
}
Default: {// Other http_server errors
System.out.println ("INVALID CODE:"
Code);
}
}
When using a method of suspended, there are several problems must be solved. As shown in the Run () method, the client thread is responsible for maintaining the connection hang with the server side. For resource considering, the thread is until a monitors are first added to HTTPCLIENT (see TestClient.java).
Once started, the pending request is sent out by the _sendpending method. A unique value is also sent, so that the server will know that this message is suspended. The next point is that the client responds to the HTTP's response code, which may come from a variety of network sources (such as HTTP proxy). Note that the simplified code above is only waiting for two possible response code: http_gateway_timeout and http_ok. These two code is the most important because they determine if the client has received viable data, or you need to send another suspend connection request. In the case of http_ok, the client can safely read data from HTTPServer. However, if it is http_gateway_timeout code, the client must assume that the suspend connection has been connected to a HTTP agent in the route, so it is necessary to resend the suspend connection to the HTTP server. By default, all other code in the above example will cause the hung up thread to stop.
At the server side, the socket object from hangs must be reserved for each client (see httpserverworker.java). One way to track these clients is to use Vector objects:
Case httpclient.pending: {
Server.addclient (socket);
Break;
}
The above code provides additional examples for suspend connections, and calls HTTPServer's addClient () method (see httpserve.java) to store socket object references.
Synchronized void addclient (socket s) {
_clients.addelement (s);
}
In order to send information back to the client, the server simply enumerates and delivers data by enumerating the socket list.
In the simplified version of this code, there is basically no error handling - can and should add more processing. Another important feature that should be added to the server code is a way tolerance for slow client. If the suspend connection is not immediately available, it is simply ignored that the client is not a good way - better is to wait for a while, see if the slow client is spending the time to send the time.
Synchronized public void send (byte data []) {
ENUMERATION Elements = _clients.ements ();
While (Elements.haASMoreElements ()) {
Socket S = (socket) Elements.nexTelement ();
Try {
DataOutputStream Output = New
DataOutputStream (New Bufferedoutputstream (New BufferedOutputStream
(S. GetOutputStream ())));
Int length;
Writeresponse (OUTPUT);
Output.writeint (data.length);
Output.write (data);
Output.flush ();
Output.close ();
} catch (ioexception e) {
E.PrintStackTrace ();
}
Finally {
Try {
s.close ();
} catch (ioexception e) {
E.PrintStackTrace ();
}
}
}
_clients.removeallelements ();
}
About author