Edited at

MicronautとSpring BootでHelloWorldサーバの起動時間を見比べた

More than 1 year has passed since last update.


概要

このエントリでは、2種類のJava言語向けのフレームワーク「Micronaut」と「Spring Boot」とで、"Hello World"をHTTP経由で返すだけの簡単なサンプルを作り、その起動時間を比べてみたものです。

Micronautは、サイトのトップの表現によれば、


A modern, JVM-based, full-stack framework for building modular, easily testable microservice applications.


ということで、JavaVMを使ってマイクロサービスを作る時にお便利な全部入りフレームワークを目指しているものです。Grailsの中の人たちが中心となって作っています。


エントリの動機

Micronaut、サイトのドキュメントを見ると「IoC」「AOP」「HTTP Server」「HTTP Client」といった基本的な部品群を有しており、「CloudNative Features」「Serverless Functions」といった構成で使うことも想定している模様。ちょこっとした小さいプログラムがサクサク動くものが欲しい時に使ってもいいかもなぁ、ということで、まずは起動時間を比べてみました。


ざくっと感触

筆者のmacでは、Micronautが1秒ちょっと、Spring Bootだと2.4秒くらい、っていうところです。


準備


方法

比較素材は、メインクラスと"Hello World"を返すコントローラの2つを持ったものとしました。

環境は、以下の通りです。


  • mac: MacBook Pro (Retina, 15-inch, Mid 2014)

  • JRE: Java(TM) SE Runtime Environment (build 1.8.0_171-b11)

  • macOS: (Darwin MBP.local 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64)

それぞれビルドしたjarファイルをjavaコマンドで実行し、フレームワークがログ中に出力している初期化時間を見比べます。


Micronaut

サイトの「Instruction」の章の通り、sdkmanを使ってSDKを入手し、コントローラを追加しました。


構成

CLI用のコマンド「mn」で作ったプロジェクトは、以下のような構成になります。GradleでJavaプロジェクトを作る時の一般的な形ですね。

|--Dockerfile

|--build.gradle
|--gradle
| |--wrapper
| | |--gradle-wrapper.jar
| | |--gradle-wrapper.properties
|--gradlew
|--gradlew.bat
|--micronaut-cli.yml
|--src
| |--main
| | |--java
| | | |--hello
| | | | |--world
| | | | | |--Application.java
| | | | | |--HelloController.java
| | |--resources
| | | |--application.yml
| | | |--logback.xml
| |--test
| | |--java
| | | |--hello
| | | | |--world


ソース

メインクラスはこのような形です。

package hello.world;

import io.micronaut.runtime.Micronaut;

public class Application {

public static void main(String[] args) {
Micronaut.run(Application.class);
}
}

コントローラはこのような形です。

package hello.world;

import io.micronaut.http.annotation.*;

@Controller("/hello")
public class HelloController {
@Get
public String index() {
return "Hello World";
}
}


ビルド

下記でjarファイルをビルドします。

$ ./gradlew build


SpringBoot

SpringBoot側は、「Spring Initializr」を使い、「Gradleプロジェクト」で「Web」のDependencyを持つものを作りました。


構成

構成は以下の通りです。

|--.gitignore

|--build.gradle
|--gradle
| |--wrapper
| | |--gradle-wrapper.jar
| | |--gradle-wrapper.properties
|--gradlew
|--gradlew.bat
|--settings.gradle
|--src
| |--main
| | |--java
| | | |--com
| | | | |--example
| | | | | |--demo
| | | | | | |--DemoApplication.java
| | | | | | |--HelloController.java
| | |--resources
| | | |--application.properties
| | | |--static
| | | |--templates
| |--test
| | |--java
| | | |--com
| | | | |--example
| | | | | |--demo
| | | | | | |--DemoApplicationTests.java


ソース

メインクラスはこのような形です。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

コントローラはこのような形です。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/hello")
public class HelloController {
@GetMapping
public String get() {
return "Hello World!";
}
}


ビルド

下記でjarファイルをビルドします。

$ ./gradlew build


計測


出力例

Micronaut

$ java -jar build/libs/hello-world-0.1-all.jar 

08:26:40.371 [main] INFO io.micronaut.runtime.Micronaut - Startup completed in 1017ms. Server Running: http://localhost:8080

Spring Boot

java -jar build/libs/demo-0.0.1-SNAPSHOT.jar 

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '
_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)

2018-09-13 08:27:46.487 INFO 35456 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on hirokis-MBP.local with PID 35456 (/Users/hiroki/work/micronaut-test/demo/build/libs/demo-0.0.1-SNAPSHOT.jar started by hiroki in /Users/hiroki/work/micronaut-test/demo)
2018-09-13 08:27:46.492 INFO 35456 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2018-09-13 08:27:46.546 INFO 35456 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@27ddd392: startup date [Thu Sep 13 08:27:46 JST 2018]; root of context hierarchy
2018-09-13 08:27:47.745 INFO 35456 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2018-09-13 08:27:47.774 INFO 35456 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2018-09-13 08:27:47.774 INFO 35456 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.34
2018-09-13 08:27:47.787 INFO 35456 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/hiroki/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2018-09-13 08:27:47.869 INFO 35456 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2018-09-13 08:27:47.869 INFO 35456 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1326 ms
2018-09-13 08:27:47.932 INFO 35456 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-09-13 08:27:47.936 INFO 35456 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-09-13 08:27:47.936 INFO 35456 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: '
hiddenHttpMethodFilter' to: [/*]
2018-09-13 08:27:47.936 INFO 35456 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: '
httpPutFormContentFilter' to: [/*]
2018-09-13 08:27:47.937 INFO 35456 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: '
requestContextFilter' to: [/*]
2018-09-13 08:27:48.047 INFO 35456 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-13 08:27:48.224 INFO 35456 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@27ddd392: startup date [Thu Sep 13 08:27:46 JST 2018]; root of context hierarchy
2018-09-13 08:27:48.295 INFO 35456 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello],methods=[GET]}" onto public java.lang.String com.example.demo.HelloController.get()
2018-09-13 08:27:48.300 INFO 35456 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-09-13 08:27:48.301 INFO 35456 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-09-13 08:27:48.325 INFO 35456 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-13 08:27:48.325 INFO 35456 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-09-13 08:27:48.450 INFO 35456 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-09-13 08:27:48.502 INFO 35456 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2018-09-13 08:27:48.508 INFO 35456 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 2.37 seconds (JVM running for 2.88)

(Micronautのログがミリ秒で、Spring Bootのログが秒というのが、個人的に味わい深いところです。)


結果

Micronautは、1秒ちょっと。

No
ms

1
1017

2
1034

3
1037

4
1028

5
1032

Spring Bootは、2.4秒くらい。

(ログには「sec」単位で出るのですが、比較のためmsecに読み替えています)

No
msec

1
2440

2
2423

3
2428

4
2412

5
2464


まとめ

このエントリでは、"Hello World"を返すだけのサーバを作り、その起動時間を見比べました。

実際のアプリを作ると、サービスやコントローラがもっと増えたりDBの接続があったりなどで時間が変わってくると思いますが、コア部分だけだと前述くらいの時間で起動することがわかりました。

Micronaut、マイクロサービス的に作る時には「これは欲しい」と思う機能が初めからいろいろ入っていて興味がわいてきました。もう少し部品が増えた版を作ったら、またエントリしたいと思います。