spring-boot-starter-ws performance when run as jar

2019-04-08 10:21发布

问题:

We've noticed in our that SOAP web-services seem to run faster when run as

spring-boot:run

as opposed to packaging up the JAR as we do for deployment and running

java -jar mySpringApp.jar

The speed up is in the order of 2-3x so obviously we would like this for our live environment.

To validate that this wasn't something in our app I've tried this with the sample app from the spring guide

git source https://github.com/spring-guides/gs-soap-service.git

Testing this with JMeter shows the same sort of speed up. I've tested this on both Windows 7 Java 1.8.0_31 and Ubuntu 14.04 platforms with 1.8.0_45-b14.

This only seems to be the case for soap services, simple html doesn't show any significant difference in performance. Any idea what could be causing this?

回答1:

I tested this and it appears that the application spends a significant amount of time in the following two code paths:

at org.springframework.boot.loader.LaunchedURLClassLoader$1.hasMoreElements(LaunchedURLClassLoader.java:110)
at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:354)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
at javax.xml.transform.FactoryFinder$1.run(FactoryFinder.java:327)
at java.security.AccessController.doPrivileged(Native Method)
at javax.xml.transform.FactoryFinder.findServiceProvider(FactoryFinder.java:323)
at javax.xml.transform.FactoryFinder.find(FactoryFinder.java:299)
at javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:106)
at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.<init>(EfficientStreamingTransformer.java:68)
at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.newTransformer(EfficientStreamingTransformer.java:420)
at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:106)
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 org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:189)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:60)
at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:92)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:87)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:61)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:293)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)

and

at org.springframework.boot.loader.LaunchedURLClassLoader$1.hasMoreElements(LaunchedURLClassLoader.java:110)
at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:354)
at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
at javax.xml.parsers.FactoryFinder$1.run(FactoryFinder.java:293)
at java.security.AccessController.doPrivileged(Native Method)
at javax.xml.parsers.FactoryFinder.findServiceProvider(FactoryFinder.java:289)
at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:267)
at javax.xml.parsers.DocumentBuilderFactory.newInstance(DocumentBuilderFactory.java:120)
at com.sun.org.apache.xalan.internal.xsltc.trax.SAX2DOM.<init>(SAX2DOM.java:74)
at com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory.getSerializationHandler(TransletOutputHandlerFactory.java:199)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.getOutputHandler(TransformerImpl.java:436)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:342)
at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:399)
at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:108)
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 org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:189)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:60)
at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:92)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:87)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:61)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:293)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869)

The interesting lines are

at javax.xml.transform.TransformerFactory.newInstance(TransformerFactory.java:106)

and

at javax.xml.parsers.DocumentBuilderFactory.newInstance(DocumentBuilderFactory.java:120)

What this means is that for every request, the SAAJ implementation in the JRE requests a new TransformerFactory and a new DocumentBuilderFactory. These factories are located using the JDK 1.3 service provider discovery mechanism, which involves searching for certain resources under META-INF/services. The performance of that search is very sensitive to the characteristics of the class loaders from which these resources are looked up. That is why you see a difference between mvn spring-boot:run and using an executable JAR. In particular, the executable JAR contains embedded JARs and looking up resources from these embedded JARs is expensive. For mvn spring-boot:run this is not the case, which explains why it is faster.

Since this is ultimately a problem with the SAAJ implementation, one solution is to use Apache Axiom instead. To do this with the sample application from the spring guide, simply add the following code to WebServiceConfig:

    @Bean
    public SoapMessageFactory messageFactory() {
        return new AxiomSoapMessageFactory();
    }

You also need to add the following dependency:

    <dependency>
        <groupId>org.apache.ws.commons.axiom</groupId>
        <artifactId>axiom-impl</artifactId>
        <version>1.2.15</version>
    </dependency>