Spring boot servlet 在 Tomcat 中显示性能问题,但在 Jetty 中没有

Posted

技术标签:

【中文标题】Spring boot servlet 在 Tomcat 中显示性能问题,但在 Jetty 中没有【英文标题】:Spring boot servlet shows performance problems in Tomcat but not Jetty 【发布时间】:2015-10-26 18:08:24 【问题描述】:

我有一个 spring boot java servlet,它在 Jetty 下运行良好,但在我们的目标部署环境 Tomcat 下运行时出现性能问题。

    JAXB 解组遇到瓶颈,因为 ServiceLoader 花费大量时间查找要使用的 TransformerFactory 实现。 (YourKit profiling showing the look up for the TransformerFactory implementation) Saxon 是转换实现,jar 包含在 tomcat/webapps/myapp/WEB-INF/lib 中,并具有正确的 META-INF/services 文件 使用 Jetty 作为 servlet 容器时不存在此问题 我已经确认在 Jetty 和 Tomcat 下运行我从正确的 jar 中获得了 TransformerFactory 的正确实现

    将映射显式添加到 Tomcat JAVA_OPTS 选项可以解决问题:

    -Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl

编辑: 我们在 Jetty 中的 xml 获取/转换时间大约是 5 秒,而在 Tomcat 下是 20 秒,这直接是由于类加载器搜索了我们的 TransformerFactory。

我可以通过在我们的 servlet 配置中明确指定 TransformerFactory 来解决这个问题,但我的问题仍然存在: 为什么 Tomcat 需要这个显式 setProperty 而 Jetty 容器不需要?

public class Application extends SpringBootServletInitializer 
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
    System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");

    return application.sources(Application.class);

对不起,如果这是一个初级问题,我是一只学习 Java 和网络应用程序/servlet 技巧的老 C++ 狗。

【问题讨论】:

第 4 个问题:“运行可执行的 .jar 或 .war 文件时”是指这些文件使用嵌入式码头而不是嵌入式 tomcat? 当我进行测试时,他们使用的是嵌入式 Tomcat。在我所有的测试中,Jetty 一直都是外部的。 我还阅读了有关 Jetty 和 Tomcat 的类加载器的文档。从这些来看,我不希望有任何区别,因为两个容器都必须遵守标准中设置的 servlet 容器的类加载器规则。 太板了,你需要了解一下这个工厂是如何解决的,也许是注释扫描?为什么不以编程方式设置工厂? 已编辑以尝试提供更多数据并缩小问题范围。我已经解决了这个问题,但感觉就像一个黑客,可能有更合适的方法来解决它。另外,我非常好奇为什么 Jetty 在没有额外设置的情况下也能正常工作,而只有 Tomcat 需要它。 【参考方案1】:

如果没有设置标识工厂的 JAXP 系统属性,那么 JAXP 会在类路径中搜索 JAR 文件,其中包含有关 JAR 文件清单的服务文件中的转换器工厂的信息。在类路径上有许多 JAR 的情况下,这种搜索可能会非常昂贵,并且绝对最好避免。对于您看到的性能差异,最可能的解释是您的 Tomcat 配置比 Jetty 配置要搜索的 JAR 文件多得多。

到目前为止,实例化 Saxon 的最快方法是直接进行,并完全避免使用 JAXP 机制:

TransformerFactory factory = new net.sf.saxon.TransformerFactoryImpl()

这样也比较靠谱;如果您的应用程序是用 Saxon 测试的,并且您不相信它可以在类路径上的任何旧 XSLT 1.0 处理器上运行,那么您最好将 Saxon 的使用硬编码到应用程序源代码中。如果您希望您的应用程序能够使用不同的 JAXP XSLT 实现运行,那么 JAXP 机制的唯一优势就是在运行时做出选择。

【讨论】:

谢谢迈克尔。我认为这支持我对 System.setProperty 进行硬编码以定义我们正在使用的实现的解决方案。您提供的代码 sn-p 是否实际上为整个应用程序设置了 TransformerFactory?我问是因为我们无法直接控制何时/何地创建 TransformerFactory。基本上我想知道我是否应该在我的配置方法中用你的代码 sn-p 替换我的系统调用 不,所提供的代码替换了任何执行TransformerFactory factory = TransformerFactory.newInstance() 的代码,并假定您可以访问应用程序中执行此操作的位置。如果您不能以这种方式更改代码,则使用系统属性是您的最佳选择,但缺点是它适用于整个应用程序,并且可能会更改仅经过测试的应用程序部分使用的 XSLT 引擎默认的 JDK 引擎并在使用其他引擎时中断。

以上是关于Spring boot servlet 在 Tomcat 中显示性能问题,但在 Jetty 中没有的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot Servlet 过滤 监听

Spring Boot os.web.servlet.pagenotfound

如何在 web.xml 中配置 spring-boot servlet?

Spring Boot中使用Servlet与Filter

如何使用 Spring Boot 注册辅助 servlet?

从零开始的Spring Boot(2在Spring Boot中整合ServletFilterListener的方式)