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

# 概要

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

[Micronaut](http://micronaut.io)は、サイトのトップの表現によれば、

> 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」](https://docs.micronaut.io/latest/guide/index.html)の章の通り、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
```

### ソース

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

```java
package hello.world;

import io.micronaut.runtime.Micronaut;

public class Application {

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

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

```java
package hello.world;

import io.micronaut.http.annotation.*;

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

### ビルド

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

```bash
$ ./gradlew build
```

## SpringBoot

SpringBoot側は、[「Spring Initializr」](https://start.spring.io)を使い、「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

```

### ソース

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

```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);
	}
}
```

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

```java
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ファイルをビルドします。

```bash
$ ./gradlew build
```

# 計測

## 出力例

Micronaut

```bash
$ 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

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