Spring 框架給企業(yè)軟件開(kāi)發(fā)者提供了常見(jiàn)問(wèn)題的通用解決方案鹃栽,包括那些在未來(lái)開(kāi)發(fā)中沒(méi)有意識(shí)到的問(wèn)題箫踩。但是啤握,它構(gòu)建的 J2EE 項(xiàng)目變得越來(lái)越臃腫奴艾,逐漸被 Spring Boot 所替代。Spring Boot 讓我們創(chuàng)建和運(yùn)行項(xiàng)目變得更為迅速糯俗,現(xiàn)在已經(jīng)有越來(lái)越多的人使用它禁熏。我們已經(jīng)在幾個(gè)項(xiàng)目中使用了 Spring Boot 敏簿,今天我們就來(lái)一起討論一下如何改進(jìn) Spring Boot 應(yīng)用的性能涎跨。
首先洼冻,從之前我在開(kāi)發(fā)中遇到的一個(gè)問(wèn)題說(shuō)起。在一次查看項(xiàng)目運(yùn)行日志的時(shí)候隅很,我偶然發(fā)現(xiàn)了一個(gè)問(wèn)題撞牢,日志里顯示這個(gè)項(xiàng)目總是加載 Velocity 模板引擎,但實(shí)際上這個(gè)項(xiàng)目是一個(gè)沒(méi)有 web 頁(yè)面的 REST Service 項(xiàng)目叔营。于是我花了一點(diǎn)時(shí)間去尋找產(chǎn)生這個(gè)問(wèn)題的原因普泡,以及如何改進(jìn) Spring Boot 應(yīng)用的性能。在查找了相關(guān)的資料后审编,我得出的結(jié)論如下:
組件自動(dòng)掃描帶來(lái)的問(wèn)題
默認(rèn)情況下,我們會(huì)使用 @SpringBootApplication 注解來(lái)自動(dòng)獲取的應(yīng)用的配置信息歧匈,但這樣也會(huì)給應(yīng)用帶來(lái)一些副作用垒酬。使用這個(gè)注解后,會(huì)觸發(fā)自動(dòng)配置( auto-configuration )和 組件掃描 ( component scanning )件炉,這跟使用 @Configuration勘究、@EnableAutoConfiguration 和 @ComponentScan 三個(gè)注解的作用是一樣的。這樣做給開(kāi)發(fā)帶來(lái)方便的同時(shí)斟冕,也會(huì)有兩方面的影響:
1口糕、會(huì)導(dǎo)致項(xiàng)目啟動(dòng)時(shí)間變長(zhǎng)。當(dāng)啟動(dòng)一個(gè)大的應(yīng)用程序,或?qū)⒆龃罅康募蓽y(cè)試啟動(dòng)應(yīng)用程序時(shí)磕蛇,影響會(huì)特別明顯景描。
2、會(huì)加載一些不需要的多余的實(shí)例(beans)秀撇。
3超棺、會(huì)增加 CPU 消耗。
針對(duì)以上兩個(gè)情況呵燕,我們可以移除 @SpringBootApplication 和 @ComponentScan 兩個(gè)注解來(lái)禁用組件自動(dòng)掃描棠绘,然后在我們需要的 bean 上進(jìn)行顯式配置:
// 移除 @SpringBootApplication and @ComponentScan, 用 @EnableAutoConfiguration 來(lái)替代
@Configuration
@EnableAutoConfiguration
public class SampleWebUiApplication {
// ...
// 用 @Bean 注解明確顯式配置,以便被 Spring 掃描到
@Bean
public MessageController messageController(MessageRepository messageRepository) {
return new MessageController(messageRepository);
}
如何避免組件自動(dòng)掃描帶來(lái)的問(wèn)題
我們?cè)谏厦嫣岬剑珸SpringBootApplication 注解的作用跟 @EnableAutoConfiguration 注解的作用是相當(dāng)?shù)难醪裕蔷鸵馕吨材軒?lái)上述的三個(gè)問(wèn)題夜矗。要避免這些問(wèn)題,我們就要知道我們需要的組件列表是哪些让虐,可以用 -Ddebug 的方式來(lái)幫助我們明確地定位:
mvn spring-boot:run -Ddebug
…
=========================
AUTO-CONFIGURATION REPORT
=========================
Positive matches:
-----------------
DispatcherServletAutoConfiguration
- @ConditionalOnClass classes found: org.springframework.web.servlet.DispatcherServlet (OnClassCondition)
- found web application StandardServletEnvironment (OnWebApplicationCondition)
...
接著拷貝 Positive matches
中列出的信息:
DispatcherServletAutoConfiguration
EmbeddedServletContainerAutoConfiguration
ErrorMvcAutoConfiguration
HttpEncodingAutoConfiguration
HttpMessageConvertersAutoConfiguration
JacksonAutoConfiguration
JmxAutoConfiguration
MultipartAutoConfiguration
ServerPropertiesAutoConfiguration
PropertyPlaceholderAutoConfiguration
ThymeleafAutoConfiguration
WebMvcAutoConfiguration
WebSocketAutoConfiguration
然后來(lái)更新項(xiàng)目配置紊撕,顯式地引入這些組件,引入之后澄干,再運(yùn)行一下應(yīng)用確保沒(méi)有錯(cuò)誤發(fā)生:
@Configuration
@Import({
DispatcherServletAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
HttpEncodingAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JacksonAutoConfiguration.class,
JmxAutoConfiguration.class,
MultipartAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
ThymeleafAutoConfiguration.class,
WebMvcAutoConfiguration.class,
WebSocketAutoConfiguration.class,
})
public class SampleWebUiApplication {
在上面的代碼中逛揩,我們可以刪掉我們不需要的組件信息,來(lái)提高應(yīng)用的性能麸俘,比如在我的項(xiàng)目中辩稽,不需要 JMX 和 WebSocket 功能,我就刪掉了它們从媚。刪掉之后逞泄,再次運(yùn)行項(xiàng)目,確保一切正常拜效。
將Servlet容器變成Undertow
默認(rèn)情況下喷众,Spring Boot 使用 Tomcat 來(lái)作為內(nèi)嵌的 Servlet 容器。我們可以啟動(dòng)項(xiàng)目紧憾,然后用 VisualVM 或者 JConsole 來(lái)查看應(yīng)用所占的內(nèi)存情況:
以上是我使用 Spring Boot 的默認(rèn)方式啟動(dòng)應(yīng)用后到千,用 VisualVM 監(jiān)控到的內(nèi)存的占用情況:堆內(nèi)存占用 110M,16 個(gè)線程被開(kāi)啟赴穗。
可以將 Web 服務(wù)器切換到 Undertow 來(lái)提高應(yīng)用性能憔四。Undertow 是一個(gè)采用 Java 開(kāi)發(fā)的靈活的高性能 Web 服務(wù)器,提供包括阻塞和基于 NIO 的非堵塞機(jī)制般眉。Undertow 是紅帽公司的開(kāi)源產(chǎn)品了赵,是 Wildfly 默認(rèn)的 Web 服務(wù)器。首先甸赃,從依賴信息里移除 Tomcat 配置:
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
然后添加 Undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
啟動(dòng)項(xiàng)目后柿汛,用 VisualVM 監(jiān)控到的信息顯示:堆內(nèi)存占用 90M,13個(gè)線程被開(kāi)啟埠对。
總結(jié)
這些都是我們?cè)陧?xiàng)目開(kāi)發(fā)中使用到的一些優(yōu)化 Spring
Boot 應(yīng)用的小技巧络断,對(duì)于大的應(yīng)用性能的提高還是很明顯的。大家可以嘗試一下项玛,然后告訴我們你的測(cè)試結(jié)果妓羊。
最后,附上代碼稍计,大家可以去這里下載:spring-boot-performance躁绸。
文中大部分內(nèi)容參考英國(guó)一個(gè)架構(gòu)師的博客 和 DZone 近期發(fā)布的文章,在此感謝兩位大牛。參考文章及鏈接:
(1)Spring Boot 性能優(yōu)化:Spring Boot Performance净刮;
(2)Spring Boot 內(nèi)存優(yōu)化:Spring Boot Memory Performance剥哑。
(3)https://www.techempower.com/benchmarks/;
(4)Spring 應(yīng)用程序優(yōu)化:Optimizing Spring Framework for App Engine Applications淹父。
OneAPM 為您提供端到端的 Java 應(yīng)用性能解決方案株婴,我們支持所有常見(jiàn)的 Java 框架及應(yīng)用服務(wù)器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸暑认,定位異常根本原因困介。分鐘級(jí)部署,即刻體驗(yàn)蘸际,Java 監(jiān)控從來(lái)沒(méi)有如此簡(jiǎn)單座哩。想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問(wèn) OneAPM 官方技術(shù)博客粮彤。
本文轉(zhuǎn)自 OneAPM 官方博客