在高并发场景下发现SOAP解析时超慢,耗时有时会达到几十秒,抓取线程栈分析发现大量的线程在等待:
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000005c249caf0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
at com.sun.xml.internal.messaging.saaj.util.ParserPool.get(ParserPool.java:71)
at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:81)
at com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:69)
at com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:128)
at com.sun.xml.internal.messaging.saaj.soap.MessageImpl.getSOAPBody(MessageImpl.java:1351)
分析com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory代码发现:
private static ContextClassloaderLocal parserPool = new ContextClassloaderLocal() {
protected ParserPool initialValue() throws Exception {
return new ParserPool(5);
}
};
此处的ParserPool中仅有5个实例,并且不支持配置。在高并发场景下,必然会导致阻塞于此。
修改方案就是想办法替换掉此处的EnvelopeFactory,从而能够扩大ParserPool,或者支持可配置。分析代码发现,SOAP解析支持配置MessageFactory和SOAPFactory,是否可以考虑用MessageFactory或者SOAPFactory实现替换掉JDK中的实现从而实现替换掉EnvelopeFactory。进一步分析线程栈和代码发现
这样我们就可以通过实现新的MessageFactory类实现EnvelopeFactory的替换:
public class SOAPMessageFactoryImpl extends SOAPMessageFactory1_1Impl {
public SOAPMessage createMessage(MimeHeaders headers, InputStream in) throws IOException, SOAPExceptionImpl {
if (headers == null) {
headers = new MimeHeaders();
}
if (getContentType(headers) == null) {
headers.setHeader("Content-Type", "text/xml");
}
MessageImpl msg = new MessageImpl1_1(headers, in);
msg.setLazyAttachments(this.lazyAttachments);
return msg;
}
}
public class MessageImpl1_1 extends Message1_1Impl {
public MessageImpl1_1() {
}
public MessageImpl1_1(boolean isFastInfoset, boolean acceptFastInfoset) {
super(isFastInfoset, acceptFastInfoset);
}
public MessageImpl1_1(SOAPMessage msg) {
super(msg);
}
public MessageImpl1_1(MimeHeaders headers, InputStream in) throws IOException, SOAPExceptionImpl {
super(headers, in);
}
public MessageImpl1_1(MimeHeaders headers, ContentType ct, int stat, InputStream in) throws SOAPExceptionImpl {
super(headers, ct, stat, in);
}
@Override
public SOAPPart getSOAPPart() {
if (this.soapPartImpl == null) {
this.soapPartImpl = new SOAPPartImpl1_1(this);
}
return this.soapPartImpl;
}
}
public class SOAPPartImpl1_1 extends SOAPPart1_1Impl {
public SOAPPartImpl1_1() {
}
public SOAPPartImpl1_1(MessageImpl message) {
super(message);
}
@Override
protected Envelope createEnvelopeFromSource() throws SOAPException {
...
// all same to JDK implementation, except here: using EnvelopeFactory of own implementation
EnvelopeImpl envelope = (EnvelopeImpl)EnvelopeFactory.createEnvelope(tmp, this);
....
}
}
}
public class EnvelopeFactory {
protected static final Logger log = LoggerFactory.getLogger (EnvelopeFactory.class);
private static final String SAX_PARSER_POOL_SIZE_PROP_NAME = "xxx.message.soap.saxParserPoolSize";
private static final int DEFAULT_SAX_PARSER_POOL_SIZE = 5;
private static final boolean lazyContentLength = SAAJUtil.getSystemBoolean("saaj.lazy.contentlength");
private static ParserPool parserPool = new ParserPool(Integer.getInteger(
SAX_PARSER_POOL_SIZE_PROP_NAME,
DEFAULT_SAX_PARSER_POOL_SIZE));
public EnvelopeFactory() {
}
public static Envelope createEnvelope(Source src, SOAPPartImpl soapPart) throws SOAPException {
// same to JDK implementation
}
}
最后在Java参数中把MessageFactory指定为自己的实现并配置池大小:
-Djavax.xml.soap.MessageFactory=xxx.message.soap.SOAPMessageFactoryImpl -Dxxx.message.soap.saxParserPoolSize=30