性能 - Spring Boot - 服务器响应时间
Posted
技术标签:
【中文标题】性能 - Spring Boot - 服务器响应时间【英文标题】:Performance - Spring Boot - Server Response Time 【发布时间】:2019-03-09 23:17:26 【问题描述】:我的 Spring Boot 应用程序出现了一个奇怪的行为:
前端/客户端 - 角度 6 后端 - spring boot - spring MVC - 嵌入式 tomcat - Linux重新启动后端后,对控制器的第一次调用大约需要 5 秒,以下相同的请求只需要 50 毫秒。这在 90% 的情况下是可重现的,有时甚至第一次调用也很快。
我确定,问题出在服务器上而不是客户端上。在浏览器上,我看到 TTFB 时间(到第一个字节的时间)增加到 5 秒。以下请求只需要 10ms 的 TTFB。
通过服务器上的监控工具(应用程序动态),我可以收集如此慢的服务器调用,并且在调用图上我可以看到:
org.apache.catalina.webresources.JarWarResourceSet:getArchiveEntries:117
需要 4916 毫秒。所以这是我的瓶颈,我想。但我不知道如何解决它。
我已经尝试过的:
从 hikaricp 切换到 apache tomcat jdbc 连接池 将弹簧靴从 2.0.0 升级到 2.0.5 Java 升级到 1.8.0_181 属性 spring.jpa.tomcat.testOnBorrow = true 属性 spring.jpa.tomcat.validationQuery = 选择 1一切都不会影响服务器延迟。
更新
由于war文件被多次扫描而导致时间丢失。
org.apache.catalina.webresources.CachedResource.validateResource 正在检查我们是否有一个 war 文件 (isPackedWarFile),这个检查返回 false。即使它是一个战争文件。对于这种行为不端,我有一个解决方法。我将 tomcat.resource.cache-tt 设置为高值。
但是现在 org.apache.catalina.webresources.Cache.getResource 有一个 noCache 方法。在这种方法中,class 和 jar 文件被排除在缓存之外。这就是再次扫描war文件的原因。
扫描整个战争文件大约需要 5 秒。而这个突破是一个停止世界的突破。而且这种扫描是完全没有必要的,因为war文件没有被炸开,所以它的内容是不能改变的。
更新
如果我将战争文件放入 tomcat 安装中,一切都会很快。嵌入式tomcat就是问题所在。
【问题讨论】:
类似地,在已安装的 Tomcat 中,我们发现<Host unpackWARs="true">
可以产生很大的不同。与扩展目录相比,直接在.war
中进行资源扫描显然效率低下。我相信扫描WEB-INF/lib
中的jar 文件的索引,当它们本身被归档时会更有效。
Spring Boot Web 服务器的其他风格 - jetty 和 undertow 怎么样?
我需要服务器的 AJP 支持。我认为码头已经放弃了对 AJP 的支持。不确定是否有低潮。无论如何,我想使用 tomecat。
【参考方案1】:
我想您已经这样做了,但如果您还没有这样做,请查看 https://cwiki.apache.org/confluence/display/TOMCAT/HowTo+FasterStartUp 并实施那里建议的修复。
关于禁用嵌入式 tomcat 扫描,这里的 cmets 中有一个建议https://github.com/spring-projects/spring-boot/issues/1610
如果上述建议都不能帮助您解决延迟问题,可能的解决方法是在服务器启动时发出第一个请求(并从那里触发延迟)。
@SpringBootApplication
public class Application implements CommandLineRunner
@Autowired
private RestTemplate template;
public static void main (String args[])
SpringApplication.run(Application.class, args);
@Override
public void run(String... strings) throws Exception
// do an initial request from here to trigger scanning the war
template.exchange(...);
这样,您的客户端将不再遇到 5s 延迟。我知道这是一个 hack,所以如果你找到一种更简洁的方法来代替它。
【讨论】:
希望至少黑客能帮助你:) 我现在已经放弃了作为 fat jar 的部署。当我将我的应用程序部署到 tomact 中时,它运行良好。 对于那些感兴趣的人来说,第一个网址似乎已移至cwiki.apache.org/confluence/display/TOMCAT/HowTo+FasterStartUp【参考方案2】:我遇到了类似的 CPU 使用率高和响应延迟的问题。
org.apache.catalina.webresources.JarWarResourceSet:getArchiveEntries
在扫描战争文件时大约需要 5 秒。在扫描期间,没有任何请求得到处理。
我已将Spring boot version
从1.4.2.RELEASE
更新为1.5.12.RELEASE
,解决了这个问题。确实,似乎问题出在嵌入式 Tomcat
上,该问题已在后续版本中得到修复。
【讨论】:
我使用的是 Spring Boot 2 (2.0.5)。此版本在 8.5.34 版中包含一个嵌入式 tomcat。而且这里的问题仍然存在。 @tomas 你介意分享一下你是否能够解决这个问题以及如何解决? 我也面临这个问题。使用 jvisual vm 和采样器在本地检查时间在 org.apache.catalina.webresources.JarWarResourceSet:getArchiveEntries 中消耗。搬到 jar 对我来说不是一个选择。任何其他想法。【参考方案3】:使用可执行 JAR 文件(而不是 WAR)与嵌入式 Tomcat 一起运行为我解决了这个问题。这是recommended way,用于加速从可执行档案中加载资源。
-
你可以有one Spring Boot project and deploy to both JAR or WAR,
我不得不从 WAR 切换到 JAR 以获得更好的性能,并且在 freemarker 中使用 JSP 标记库时遇到了问题。这是a way to solve that issue when using JSP taglibs。
【讨论】:
感谢您的回复。我很确定我当时尝试了 jar 和战争部署。两者都有这个性能问题。 我从 WAR 转到 JAR 并且不再遇到性能问题,如下所述:github.com/spring-projects/spring-boot/issues/16471【参考方案4】:您所描述的是重启对使用大量数据库连接池的基础架构的典型影响。
第一个请求:打开物理连接(100 毫秒到 2-3 秒),进行一些初始化(取决于 DB),执行 SQL(每个查询不同),返回池( 第二个请求:从池中提取(根据您的数据,我的最佳猜测是前两个步骤很慢,在数据库池预热之前,您会遇到一些非常慢的查询。 潜在的改进是:
配置一个预热期,在此期间池执行自初始化,而 Tomcat 尚未响应 在 DB 端检查在创建连接时所做的工作以及在应用端检查是否/哪些配置必须设置连接【讨论】:
这也是我的第一想法。但是连接池不是问题。通过性能分析,我可以发现它是对 WAR 文件的扫描。 如果您确定这一点,那么我建议您研究防病毒(在打开战争和检查字节码时众所周知的猪)和容器化(有时对 I/O 有一些奇怪的副作用)。第一个(防病毒)可以通过禁用它来轻松检查。第二个(容器化)通常不太重要,因为运行容器化应用程序与非容器化应用程序存在许多差异,您应该在那里寻找特定的 *** 问题。以上是关于性能 - Spring Boot - 服务器响应时间的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 使用来自服务器响应的数据编辑 json 异常
Flux 响应而不是 WebSocket (Spring boot)
在 Spring Boot 中使用自定义代码进行响应 [重复]
原始性能表格 - Spring Boot 2 Webflux vs. Spring Boot 1