2008年7月18日 星期五

Standalone JAX-WS is not ready for production

Further to the bug 'light weight http server connections leaks when calling invalid wsdl' 6712353, another problem we encountered is that the standalone jax-ws server doesn't work well when serving concurrent web service calls.

I tried to perform a stress test by making concurrent web service calls, the standalone web service server will keep throwing javax.xml.stream.XMLStreamException.

Is it a bug? Not sure. I will post it to java forum.

If it's a bug. I think it may due to Sun's light weight http server.

I did a very simple experiment:

1. Create a simple web method:


// Calculator.java
package simplews;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;

@WebService
public class Calculator {

@WebMethod
public int add(int a, int b) {
return a + b;
}
}





2. Generate web service class by using apt:

D:\>c:\Program Files\java\jdk1.6.0_06\bin"\apt -d . src\simplews\Calculator.java
warning: Annotation types without processors: [javax.xml.bind.annotation.XmlRootElement, javax.xml.bind.annotation.XmlAccessorType, javax.xml.bind.annotation.XmlType, javax.xml.bind.annotation.XmlElement]
1 warning


3. Create my endpoint:


//Main.java
package simplews;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.xml.ws.Endpoint;

public class Main {
private static ArrayBlockingQueue m_arrayBlockingQueue = null;
private static ThreadPoolExecutor m_executor = null;

public static void main(String[] args) {
// create and publish an endpoint
m_arrayBlockingQueue = new ArrayBlockingQueue(10000);
m_executor = new ThreadPoolExecutor(5, 50, 15L, TimeUnit.SECONDS, m_arrayBlockingQueue);
Calculator calculator = new Calculator();
Endpoint endpoint = Endpoint.publish("http://localhost:8080/calculator", calculator);
}
}


4. Create my web service client:


//Main.java
public class Main {

private static int totalThread = 20;
private static int countPerThread = 1000;
private static long sleeptime = 1L;

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
try {
totalThread = Integer.parseInt(args[0]);
} catch (Exception e) {
}
try {
countPerThread = Integer.parseInt(args[1]);
} catch (Exception e) {
}
try {
sleeptime = Long.parseLong(args[2]);
} catch (Exception e) {
}
for (int i = 0; i < totalThread; i++) {
Runnable runnable = new Runnable() {
int count = countPerThread;
public void run() {
while (--count > 0) {
try { // Call Web Service Operation
simplews.CalculatorService service = new simplews.CalculatorService();
simplews.Calculator port = service.getCalculatorPort();
// TODO initialize WS operation arguments here
int arg0 = 5;
int arg1 = 6;
// TODO process result here
int result = port.add(arg0, arg1);
System.out.println(Thread.currentThread().toString() + " | Result = " + result);
Thread.sleep(sleeptime);
} catch (Exception ex) {
// TODO handle custom exceptions here
ex.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();

try {
Thread.sleep(sleeptime);
} catch (Exception e) {
}
}
}
}




What I am doing here is to create certain number of threads and keep calling remote web method.


5. Start server

6. Start clients with 10 threads, 100 web service calls per thread and sleep = 1 millis.

Server side will keep throwing:


com.sun.xml.internal.ws.streaming.XMLStreamReaderException: XML reader error: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,2]
Message: The markup in the document preceding the root element must be well-formed.
at com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.wrapException(XMLStreamReaderUtil.java:242)
at com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.next(XMLStreamReaderUtil.java:70)
at com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.nextContent(XMLStreamReaderUtil.java:85)
at com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.nextElementContent(XMLStreamReaderUtil.java:75)
at com.sun.xml.internal.ws.encoding.StreamSOAPCodec.decode(StreamSOAPCodec.java:167)
at com.sun.xml.internal.ws.encoding.StreamSOAPCodec.decode(StreamSOAPCodec.java:149)
at com.sun.xml.internal.ws.encoding.StreamSOAPCodec.decode(StreamSOAPCodec.java:121)
at com.sun.xml.internal.ws.encoding.SOAPBindingCodec.decode(SOAPBindingCodec.java:280)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.decodePacket(HttpAdapter.java:207)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.access$500(HttpAdapter.java:74)
at com.sun.xml.internal.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:363)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:175)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handleExchange(WSHttpHandler.java:100)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handle(WSHttpHandler.java:77)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:65)
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:65)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:68)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:552)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:65)
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:524)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:619)
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,2]
Message: The markup in the document preceding the root element must be well-formed.
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(XMLStreamReaderImpl.java:588)
at com.sun.xml.internal.ws.util.xml.XMLStreamReaderFilter.next(XMLStreamReaderFilter.java:78)
at com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.next(XMLStreamReaderUtil.java:51)
... 21 more







At the same time, number of loopback connections (by Selector) at server
side will be increased by one per exception! Those loopback connections cannot be released unless restarting application.

The application will halt when the number of leaked connection rearch
server limit (max filedispatcher).

Bug 6712353 is dispatched and pending for fix, I am not sure when the fix can be released.

So, if you plan to use standalone jax-ws, make sure the bug is fixed otherwise you are in trouble. Currently one of our application in production is suffered and we have to detect number of leaked connections and trigger an auto-restart of the application in order to free those leaked connections.

I attached my test code here (NetBeans Project):

http://kentsang77.googlepages.com/SimpleWS.rar
http://kentsang77.googlepages.com/SimpleWSClient.rar

jdk version:

java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) 64-Bit Server VM (build 10.0-b22, mixed mode)

3 則留言:

DragonKen 提到...

Today, I tried the beta jvm 1.6.0_10 but it doesn't help.

DragonKen 提到...

updates:

I tried jetty embedded by using HttpServerSPI for j2se6.

It solve the problem of connections leaks (as it use blocking IO, no nio selector). But it still throws similar exception when the server is serving concurrent requests:

javax.xml.ws.WebServiceException: javax.xml.stream.XMLStreamException: java.io.I
OException: Closed
at com.sun.xml.internal.ws.encoding.StreamSOAPCodec.encode(Unknown Sourc
e)
at com.sun.xml.internal.ws.encoding.SOAPBindingCodec.encode(Unknown Sour
ce)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.encodePacket(Unkno
wn Source)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.access$100(Unknown
Source)
at com.sun.xml.internal.ws.transport.http.HttpAdapter$HttpToolkit.handle
(Unknown Source)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.handle(Unknown Sou
rce)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handleExc
hange(Unknown Source)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handle(Un
known Source)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.handle(J2SE6ContextHandle
r.java:69)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHand
lerCollection.java:206)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.
java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:1
52)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:50
5)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnectio
n.java:842)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:648)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.ja
va:395)
at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool
.java:450)
Caused by: javax.xml.stream.XMLStreamException: java.io.IOException: Closed
at com.sun.xml.internal.stream.writers.XMLStreamWriterImpl.writeStartDoc
ument(Unknown Source)
at com.sun.xml.internal.ws.message.AbstractMessageImpl.writeTo(Unknown S
ource)
... 20 more
Caused by: java.io.IOException: Closed
at org.mortbay.jetty.AbstractGenerator$Output.write(AbstractGenerator.ja
va:597)
at java.io.FilterOutputStream.write(Unknown Source)
at com.sun.xml.internal.stream.writers.UTF8OutputStreamWriter.write(Unkn
own Source)
at com.sun.xml.internal.stream.writers.UTF8OutputStreamWriter.write(Unkn
own Source)
... 22 more



Using embedded jetty have performance downgrade as it use blocking IO but it's much better than keep making connections leaks.

JFernand 提到...

Hey dude i faced the same problem but synchronizing my functions work fine to me.