--Create a "web application that returns Hello World HTML" or "a tutorial that is close to the initial state in each official document" and compare the memory usage. --Use a web application server that seems to be popular at the moment --Use the default settings of the framework or those recommended by the framework for the template engine --Measure memory usage on macOS Catalina --Use the RSS value of the ps command to measure the memory usage (for those using multiple processes, the memory usage is simply the sum of the RSS values. Since the memory shared part is not considered, it is a little more. Memory usage may be low)
Arrange in ascending order of memory usage.
Go 1.13 + Gin 1.5
main.go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLGlob("templates/*")
router.GET("/hello", func(c *gin.Context) {
c.HTML(http.StatusOK, "hello.tmpl", gin.H{})
})
router.Run(":8080")
}
templates/hello.tmpl
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ go build main.go
$ GIN_MODE=release ./main
$ curl http://localhost:8080/hello
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 30713 0.0 0.1 4405168 7220 s000 S+ 12:04AM 0:00.03 ./main
Go 1.13 + Revel 0.21
Creating a new Revel application | Revel - A Web Application Framework for Go!
$ revel new myapp
Revel tool | Revel - A Web Application Framework for Go!
$ revel package -a myapp -m prod
Deployment | Revel - A Web Application Framework for Go!
$ tar zxvf myapp.tar.gz
run.sh
#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
"$SCRIPTPATH/myapp" -importPath myapp -srcPath "$SCRIPTPATH/src" -runMode prod
$ ./run.sh
Revel engine is listening on.. localhost:9000
$ curl http://localhost:9000/
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 32777 0.0 0.1 4405468 8928 s000 S+ 12:32AM 0:00.04 /Users/hoge/go/myapp
hoge 32771 0.0 0.0 4288312 1048 s000 S+ 12:32AM 0:00.01 /bin/sh ./run.sh
Ruby 2.7 + Sinatra 2.0 + Puma 4.3
myapp.rb
require 'sinatra'
get '/hello' do
erb :hello
end
hello.erb
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ APP_ENV=production RACK_ENV=production ruby myapp.rb
== Sinatra (v2.0.8.1) has taken the stage on 4567 for production with backup from Puma
Puma starting in single mode...
* Version 4.3.1 (ruby 2.7.0-p0), codename: Mysterious Traveller
* Min threads: 0, max threads: 16
* Environment: production
* Listening on tcp://0.0.0.0:4567
$ curl http://localhost:4567/hello
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 99121 0.0 0.3 4378332 24540 s002 S+ 10:18PM 0:00.43 puma 4.3.1 (tcp://0.0.0.0:4567)
Python 3.8 + Flask 1.1 + Gunicorn 20.0
myapp.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/hello")
def hello():
return render_template("hello.html")
templates/hello.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ gunicorn myapp:app
[2020-01-04 23:55:18 +0900] [29978] [INFO] Starting gunicorn 20.0.4
[2020-01-04 23:55:18 +0900] [29978] [INFO] Listening at: http://127.0.0.1:8000 (29978)
[2020-01-04 23:55:18 +0900] [29978] [INFO] Using worker: sync
[2020-01-04 23:55:18 +0900] [29995] [INFO] Booting worker with pid: 29995
$ curl http://localhost:8000/hello
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 29995 0.0 0.3 4273780 22632 s000 S+ 11:55PM 0:00.37 /Users/hoge/.pyenv
hoge 29978 0.0 0.2 4265996 18532 s000 S+ 11:55PM 0:00.46 /Users/hoge/.pyenv
Node.js 12.14 + Express 4.16
$ express --view=pug myapp
$ cd myapp/
$ npm install
$ npm audit fix
$ node ./bin/www
$ curl http://localhost:3000/
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 19537 0.0 0.5 4565284 43228 s000 S+ 9:59PM 0:00.83 node ./bin/www
Python 3.8 + Django 3.0 + Gunicorn 20.0
Creating your first Django app, part 1|Django documentation| Django
$ django-admin startproject mysite
$ python manage.py startapp polls
Fixed some settings in mysite / settings.py.
DEBUG = False
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]']
# Application definition
INSTALLED_APPS = [
'polls',
Creating your first Django app, part 3|Django documentation| Django
polls/templates/polls/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
polls/views.py
from django.shortcuts import render
def index(request):
return render(request, 'polls/index.html')
$ gunicorn mysite.wsgi:application
[2020-01-04 23:44:32 +0900] [29172] [INFO] Starting gunicorn 20.0.4
[2020-01-04 23:44:32 +0900] [29172] [INFO] Listening at: http://127.0.0.1:8000 (29172)
[2020-01-04 23:44:32 +0900] [29172] [INFO] Using worker: sync
[2020-01-04 23:44:32 +0900] [29191] [INFO] Booting worker with pid: 29191
$ curl http://localhost:8000/polls/
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 29191 0.0 0.4 4283728 34016 s000 S+ 11:44PM 0:00.55 /Users/hoge/.pyenv
hoge 29172 0.0 0.2 4265996 18552 s000 S+ 11:44PM 0:00.43 /Users/hoge/.pyenv
Ruby 2.7 + Ruby on Rails 6.0+ Puma 4.3
Getting Started with Rails — Ruby on Rails Guides
$ rails new blog
$ cd blog
$ rails generate controller Welcome index
app/views/welcome/index.html.erb
Hello, World
config/routes.rb
Rails.application.routes.draw do
get 'welcome/index'
root 'welcome#index'
end
$ RAILS_ENV=production rails assets:precompile
$ rails server -e production
=> Booting Puma
=> Rails 6.0.2.1 application starting in production
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.1 (ruby 2.7.0-p0), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: production
* Listening on tcp://0.0.0.0:3000
$ curl http://localhost:3000/
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 66416 0.0 1.0 4445976 81964 s000 S+ 9:42AM 0:02.97 puma 4.3.1 (tcp://0.0.0.0:3000) [blog]
Java 11.0 + Spring Boot 2.2 + Tomcat Embed Core 9.0
build.gradle
plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@SpringBootApplication
@Controller
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello, World!</title>
</head>
<body>
Hello, World!
</body>
</html>
$ gradle bootJar
Java allocates a large amount of memory immediately after startup, so specify 32MB as the minimum heap size.
$ java -Xms32m -jar build/libs/demo-0.0.1-SNAPSHOT.jar
$ curl http://localhost:8080/hello
$ ps au
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
hoge 20294 0.0 2.0 8094612 171024 s000 S+ 10:09PM 0:17.35 java -Xms32m -jar build/libs/demo-0.0.1-SNAPSHOT.jar