Java Advent Calendar 2019 24日目の記事です。
クリスマス前にも関わらず暇なので
JVM言語ベースのフレームワークであるSpringBootとMicronautの単純起動速度を比較してみた
Springは知る人ぞ知るフレームワークなので紹介は省きます
##Mirconautとは
- OCI社が開発したマイクロサービス向けフレームワーク
- JVMベース
- 2018年10月に1.0GAリリース
公式サイト
https://micronaut.io/
###特徴
- プログラム言語にJava/Groovy/Kotlinが使える
- Compile Time DI&AOPを採用している
- GraalVM、NativeImageも使える
- CLIが提供されている
- 起動が早い
- メモリフットプリントが小さい
##検証
###環境
- MacBook Pro
- SpringBoot v2.2.2.RELEASE
- Micronaut 1.2.7
#####実行環境
- AdoptOpenJDK 11.0.4
- AdoptOpenJDK 8.0.232
- GraalVM 19.2.1
- JIT
- AOT(NativeImage)
###方法
- シンプルなREST APIを1つ実装する
- jarにパッケージして起動する(java -jar xxx.jar)
- 各実行環境で10回起動して、その平均値を取る
- SpringBootはTomcatとJettyの両方で起動速度を計測する
###実装
####SpringBoot
@RestController
public class HelloController {
@RequestMapping(value="/hello", method=RequestMethod.GET)
public String hello() {
return "Hello Spring";
}
}
Tomcat起動
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2019-12-23 14:29:46.247 INFO 73041 --- [ main] com.example.sphelloapp.DemoApplication : Starting DemoApplication
2019-12-23 14:29:46.250 INFO 73041 --- [ main] com.example.sphelloapp.DemoApplication : No active profile set, falling back to default profiles: default
2019-12-23 14:29:46.925 INFO 73041 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-12-23 14:29:46.935 INFO 73041 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-12-23 14:29:46.935 INFO 73041 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.29]
2019-12-23 14:29:46.984 INFO 73041 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-12-23 14:29:46.984 INFO 73041 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 695 ms
2019-12-23 14:29:47.105 INFO 73041 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-12-23 14:29:47.214 INFO 73041 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-12-23 14:29:47.216 INFO 73041 --- [ main] com.example.sphelloapp.DemoApplication : Started DemoApplication in 1.233 seconds (JVM running for 1.556)
Jetty起動
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2019-12-23 14:22:39.036 INFO 72942 --- [ main] c.e.sphelloappjetty.DemoApplication : Starting DemoApplication
2019-12-23 14:22:39.038 INFO 72942 --- [ main] c.e.sphelloappjetty.DemoApplication : No active profile set, falling back to default profiles: default
2019-12-23 14:22:39.651 INFO 72942 --- [ main] org.eclipse.jetty.util.log : Logging initialized @1229ms to org.eclipse.jetty.util.log.Slf4jLog
2019-12-23 14:22:39.713 INFO 72942 --- [ main] o.s.b.w.e.j.JettyServletWebServerFactory : Server initialized with port: 8080
2019-12-23 14:22:39.716 INFO 72942 --- [ main] org.eclipse.jetty.server.Server : jetty-9.4.24.v20191120; built: 2019-11-20T21:37:49.771Z; git: 363d5f2df3a8a28de40604320230664b9c793c16; jvm 1.8.0_232-20191009173705.graal.jdk8u-src-tar-gz-b07
2019-12-23 14:22:39.738 INFO 72942 --- [ main] o.e.j.s.h.ContextHandler.application : Initializing Spring embedded WebApplicationContext
2019-12-23 14:22:39.739 INFO 72942 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 664 ms
2019-12-23 14:22:39.815 INFO 72942 --- [ main] org.eclipse.jetty.server.session : DefaultSessionIdManager workerName=node0
2019-12-23 14:22:39.815 INFO 72942 --- [ main] org.eclipse.jetty.server.session : No SessionScavenger set, using defaults
2019-12-23 14:22:39.816 INFO 72942 --- [ main] org.eclipse.jetty.server.session : node0 Scavenging every 660000ms
2019-12-23 14:22:39.821 INFO 72942 --- [ main] o.e.jetty.server.handler.ContextHandler : Started o.s.b.w.e.j.JettyEmbeddedWebAppContext@45f45fa1{application,/,[file:///private/var/folders/xk/q20_p4gd2xz1sfmtmngwjg380000gp/T/jetty-docbase.146795275059491819.8080/],AVAILABLE}
2019-12-23 14:22:39.822 INFO 72942 --- [ main] org.eclipse.jetty.server.Server : Started @1400ms
2019-12-23 14:22:39.922 INFO 72942 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-12-23 14:22:40.036 INFO 72942 --- [ main] o.e.j.s.h.ContextHandler.application : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-12-23 14:22:40.036 INFO 72942 --- [ main] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-12-23 14:22:40.040 INFO 72942 --- [ main] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
2019-12-23 14:22:40.060 INFO 72942 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector@37574691{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2019-12-23 14:22:40.062 INFO 72942 --- [ main] o.s.b.web.embedded.jetty.JettyWebServer : Jetty started on port(s) 8080 (http/1.1) with context path '/'
2019-12-23 14:22:40.064 INFO 72942 --- [ main] c.e.sphelloappjetty.DemoApplication : Started DemoApplication in 1.299 seconds (JVM running for 1.643)
(参考)
jetty起動にする場合の設定(gradle)
dependencies {
compile('org.springframework.boot:spring-boot-starter-web') {
exclude module: 'spring-boot-starter-tomcat'
}
compile('org.springframework.boot:spring-boot-starter-jetty')
}
#####NativeImage起動
Springはまだ正式にはNativeImageに対応していない(2020年2Qに対応予定)
実験版がgithubにあるので、これで検証する(TomcatのみでJettyはない)
https://github.com/spring-projects-experimental/spring-graal-native
手順
- GitHubのspring-graal-nativeをclone
-
./build-feature.sh
を実行 -
spring-graal-native-samples/springmvc-tomcat
に移動 -
./compile.sh
を実行 (1〜2分)
これでspring側の準備は完了
####Micronaut
@Controller
public class HelloController{
@Get(value="/hello", produces = MediaType.TEXT_PLAIN)
public String index(){
return "Hello Micronaut";
}
}
起動
14:37:49.383 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 985ms. Server Running: http://localhost:8080
#####NativeImage起動
Micronautは標準でNativeImageに対応しているが、NativeImage専用のプロジェクトを作ると楽できる
※前提としてMicronautCLIがインストールされていること
Micronautインストール手順はここを参照
手順
$mn create-app hello-graal --features graal-native-image
-
HelloController.java
を実装 -
./gradlew build
を実行 -
$native-image --no-server -cp build/libs/hello-graal-0.1-all.jar
を実行 (1〜2分) -
./hello-graal
を実行
これでMicronaut側の準備は完了
###考察
- JDK(11, 8, GraalVM JIT)では差はほぼない。
- フレームワーク同士では約300msの差はあるが、これを大きいと捉えるか小さいと捉えるかは個人の考えやケースによる。筆者の場合は特に気にならない差である。
- GraalVM AOT(NativeImage)は圧倒的に早い。
- JVM上で動作させることの利点を捨ててまでNativeImageにする恩恵があるかと言われれば疑問も残る。
(JVMも1秒前後で起動するからいいんじゃないか) - ただし、今回は超シンプルなアプリだった結果であるため、たくさんクラスロードがあるようなアプリだと起動時間においてはAOTの恩恵をさらに感じるかもしれない。
##最後に
今回は単純なアプリでの検証のため、実際の複雑なアプリでは異なる結果になるかもしれないので、あくまで参考として見ていただけると幸いです。
機会があればhelidonとかQuarkusといったフレームワークも検証してみます。