[英]Will a tomcat request connection also time out when a server side process is taking too long to send a response?
[英]Why my Springboot with embbeded tomcat too slow when process first request?
環境
操作系統:macOS Mojave 版本 10.14.5(centOS 也有同樣的問題)
Springboot:2.1.6.RELEASE(內嵌tomcat 9.0.21),war
我是Spring Boot的新手,我認為這對我的項目有幫助。 現在我已經完成了我的工作,但是一個奇怪的現象困擾着我。 我的項目響應第一個請求大約需要 5 分鍾,它花費 5 分鍾而不是 5 秒,第一次之后的請求似乎正常。 它非常慢,所以我需要你的幫助。
在jstack
的幫助下,我發現大部分時間都是在做下面的事情,同樣做解壓戰爭。
"http-nio-15281-exec-5" #105 daemon prio=5 os_prio=31 tid=0x00007f988eaff800 nid=0x13b03 runnable [0x0000700013218000]
java.lang.Thread.State: RUNNABLE
at java.util.zip.Inflater.inflateBytes(Native Method)
at java.util.zip.Inflater.inflate(Inflater.java:259)
- locked <0x00000007bac79ab0> (a java.util.zip.ZStreamRef)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:152)
at java.util.zip.ZipInputStream.read(ZipInputStream.java:194)
at java.util.jar.JarInputStream.read(JarInputStream.java:207)
at java.util.zip.ZipInputStream.closeEntry(ZipInputStream.java:140)
at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:118)
at java.util.jar.JarInputStream.getNextEntry(JarInputStream.java:142)
at java.util.jar.JarInputStream.getNextJarEntry(JarInputStream.java:179)
at org.apache.catalina.webresources.JarWarResourceSet.getArchiveEntries(JarWarResourceSet.java:117)
- locked <0x00000007810e7770> (a java.lang.Object)
at org.apache.catalina.webresources.AbstractArchiveResourceSet.getResource(AbstractArchiveResourceSet.java:253)
at org.apache.catalina.webresources.StandardRoot.getResourceInternal(StandardRoot.java:282)
at org.apache.catalina.webresources.Cache.getResource(Cache.java:62)
at org.apache.catalina.webresources.StandardRoot.getResource(StandardRoot.java:217)
at org.apache.catalina.webresources.StandardRoot.getClassLoaderResource(StandardRoot.java:226)
at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2303)
at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:865)
at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.findClassIgnoringNotFound(TomcatEmbeddedWebappClassLoader.java:119)
at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.doLoadClass(TomcatEmbeddedWebappClassLoader.java:84)
at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.loadClass(TomcatEmbeddedWebappClassLoader.java:66)
- locked <0x00000007af22a990> (a java.lang.Object)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1188)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at com.sun.beans.finder.ClassFinder.findClass(ClassFinder.java:67)
at com.sun.beans.finder.ClassFinder.findClass(ClassFinder.java:110)
at java.beans.Introspector.findCustomizerClass(Introspector.java:1301)
at java.beans.Introspector.getTargetBeanDescriptor(Introspector.java:1295)
at java.beans.Introspector.getBeanInfo(Introspector.java:425)
at java.beans.Introspector.getBeanInfo(Introspector.java:262)
at java.beans.Introspector.getBeanInfo(Introspector.java:204)
at org.springframework.beans.CachedIntrospectionResults.getBeanInfo(CachedIntrospectionResults.java:248)
at org.springframework.beans.CachedIntrospectionResults.<init>(CachedIntrospectionResults.java:273)
at org.springframework.beans.CachedIntrospectionResults.forClass(CachedIntrospectionResults.java:177)
at org.springframework.beans.BeanWrapperImpl.getCachedIntrospectionResults(BeanWrapperImpl.java:174)
at org.springframework.beans.BeanWrapperImpl.getLocalPropertyHandler(BeanWrapperImpl.java:230)
at org.springframework.beans.BeanWrapperImpl.getLocalPropertyHandler(BeanWrapperImpl.java:63)
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:620)
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:612)
at org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper.getPropertyValue(DirectFieldAccessFallbackBeanWrapper.java:52)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.getId(JpaMetamodelEntityInformation.java:154)
at org.springframework.data.repository.core.support.AbstractEntityInformation.isNew(AbstractEntityInformation.java:42)
at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.isNew(JpaMetamodelEntityInformation.java:233)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:506)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush(SimpleJpaRepository.java:521)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor$$Lambda$641/1539038539.get(Unknown Source)
at org.springframework.data.repository.util.QueryExecutionConverters$$Lambda$640/28145535.apply(Unknown Source)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.transaction.interceptor.TransactionInterceptor$$Lambda$636/1377160602.proceedWithInvocation(Unknown Source)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:138)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy125.saveAndFlush(Unknown Source)
我們面臨着完全相同的問題,我花了好幾天的時間來解決它,但設法通過以下配置解決了它:
@Configuration
public class EmbeddedTomcatConfiguration {
@Bean
TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
context.setResources(new ExtractingRoot());
}
};
}
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainerCustomizer() {
return new WebServerFactoryCustomizer<TomcatServletWebServerFactory>() {
@Override
public void customize(TomcatServletWebServerFactory container) {
container.addContextCustomizers(
new TomcatContextCustomizer() {
@Override
public void customize(Context cntxt) {
cntxt.setReloadable(false);
}
});
}
};
}
}
這基本上做了兩件事......
事實證明這里發生了一些事情,我在 github 上做了一個要點和一個 repo 來分享解決方案以加快 Spring(特別是 JPA 存儲庫)啟動時間。
域名注冊地址:
解決方案1:添加配置關閉嵌入式Tomcat類重載https://gist.github.com/SimoneGianni/74b3b7e5f5986a72a95e705cc6abe6dc 。 這將防止問題每 15 分鍾發生一次,並稍微加快啟動速度。
解決方案 2:為每個實體實現 BeanInfo,或使用此https://github.com/SimoneGianni/auto-bean-info自動生成它們。
完整說明:
當 Spring 啟動時,和/或第一次使用您的 JPA 存儲庫時,和/或第一次使用特定存儲庫時(取決於緩存、加載組件的順序等),Spring 和 Hibernate 將檢查您的實體。
為此,他們將使用 java Introspector。 這沒什么不好,它是任何必須處理 java bean 的人都會使用的標准庫類。
回到 Swing 炙手可熱的時代,Java bean 是在 Angular 之前 20 年組合模塊化 UI 的方式,為此,一個 bean 可以伴隨許多其他類,如 BeanInfo 和 Descriptor,以指定它應該如何由 IDE 使用和配置。
這些附帶的類被用來更好地描述屬性、事件等。當時注釋還不是一個東西。
現在,假設您有一個名為SomethingCool.java 的bean,您可以提供SomethingCoolBeanInfo.java 和/或SomethingCoolDescriptor.java。 模式是<bean class name>+(BeanInfo|Descriptor)
。
Introspector 獲取一個 bean,首先......搜索相應的 BeanInfo 並最終搜索 Descriptor。 所以,這些是 Class.forName 查找。
至少在 java 1.8 中(還沒有檢查過更新的版本),這種查找是以相當復雜的方式完成的,在不同的類加載器上嘗試了多次。
再一次,到目前為止,沒什么不好,只是搜索兩個類。
然后,它到達 Tomcat 嵌入式類加載器。 它有兩個問題。
而且,幾乎在 2020 年,Java 已經發展,BeanInfos 和 Descriptor 主要用於構建 UI 的 IDE,而不是在處理碰巧也有 setter 和 getter 的實體時。
Spring 試圖通過忽略 BeanInfos 的標志來緩解這種調用 Introspector(並為此公開一個屬性),但至少在 1.8 中它顯然什么都不做,肯定不會跳過對描述符的搜索,正如 JDK 中所報告的那樣票https://bugs.openjdk.java.net/browse/JDK-8172961看起來像是 1.8 上的一個已知問題。
因此,它加載實體類,調用 Introspector,它將搜索 BeanInfo,但仍然掃描所有 jar 並找不到它,然后搜索 Descriptor,再次掃描所有 jar,這使它變慢。
在我的例子中,一個通常需要幾毫秒的 HTTP 請求在第一次被調用時變成了 15/20 秒,這會扼殺我們的 E2E 測試。
第一個解決方案是至少關閉嵌入式 Tomcat 緩存驅逐器,使用要點https://gist.github.com/SimoneGianni/74b3b7e5f5986a72a95e705cc6abe6dc 完成。 它是一個攔截 Spring Tomcat 配置並轉換正確標志的 bean。 它針對 Spring 2.0 進行了更新,網上有一個適用於 1.X 的版本。
它會阻止事情每 15 分鍾再次發生一次,並且不知何故它也在某種程度上加快了啟動/第一次請求,可能導致 Tomcat 緩存更積極。
但是,它仍然會搜索 BeanInfo 和 Descriptor,並且只有在它掃描了您所有的 jar 之后才會放棄。
第二種解決方案是為每個@Entity(和@MappedSuperclass,最好是任何接口或其他超類)提供一個 BeanInfo 類。 通過放置這個 BeanInfo,它會找到它(希望很快,在同一個類加載器中)並避免掃描所有 jar,並且在 BeanInfo 中,您還可以指定沒有描述符並完全跳過其他搜索。
由於為每個實體編寫空的 BeanInfos 既乏味又會污染您的代碼,因此我編寫了一個注釋處理器來做到這一點,並且我正在我的項目中使用它。
目前,您必須克隆它, mvn build 安裝它,並將其用作依賴項。 我最終會在 github 上設置一個 maven repo 也是為了我的目的,僅將它作為本地依賴項是不健康的。
請注意,在 1.9 中,他們引入了一些特定的注釋來從注釋生成 BeanInfo,但這又是面向 Swing 的,因此在更高版本中似乎沒有“避免搜索實體的東西”可用。
希望這會有所幫助,至少對我來說它有所幫助,現在初始啟動速度稍快,並且第一個請求需要幾秒鍾而不是 15/20,在執行 E2E 測試時沒有進一步的延遲。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.