1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go 言語を学ぶAdvent Calendar 2023

Day 18

【負荷試験】Rails Sever VS Go Server

Last updated at Posted at 2023-12-18

Ruby(Rails) VS Go(echo)

RailsとGo製サーバーの耐アクセス性能を比べてみようと思います。

それぞれDockerコンテナでサーバーを建てて、どれほどのアクセス量まで耐えられるのか検証します。
理想はEC2上などでやりたかったですが、本検証は手元のMacで行っております。

Rails Serverを建てる

apiモードでrails new

 rails new rails_app --api

コントローラーだけ作ります。

rails g controller posts
app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    render json: {"status": "ok"}
  end
end

ルーティング

config/routes.rb
Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  root "posts#index"
end

Dockerfile
FROM ruby:3.2.1-slim-bullseye

RUN apt-get update -qq
RUN apt-get install -y nodejs postgresql-client npm
RUN npm install --global yarn

WORKDIR /myapp
COPY . /myapp
RUN bundle install

CMD ["./bin/rails", "s", "-b", "0.0.0.0"]

ビルド

docker build -t rails_server:latest .

起動

docker run -p 3000:3000  rails_server:latest

アクセスしてみます。

$ curl localhost:3000
{"status":"ok"}

echoサーバーを建てる

お次はechoサーバーです。

Dockefile
FROM golang:1.21.5
WORKDIR /app
RUN go mod init echo_app

RUN go get github.com/labstack/echo/v4
RUN go get github.com/labstack/echo/v4/middleware

COPY . .
CMD ["go", "run", "main.go"]
main.go
package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

type ResponseData struct {
	Status string `json:"status"`
}

func main() {
	e := echo.New()

	e.Use(middleware.Logger())

	e.GET("/", hello)

	e.Logger.Fatal(e.Start(":1323"))
}

func hello(c echo.Context) error {
	data := ResponseData{
		Status: "ok",
	}
	return c.JSON(http.StatusOK, data)
}

ビルド

 docker build -t echo_server_demo:latest .

起動

docker run -p3002:1323 echo_server_demo:latest

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.11.3
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\

検証用プログラム

並行処理を用いてリクエストを送るプログラムです。

package main

import (
	"fmt"
	"net/http"
	"sync"
)

// sendRequest関数は、指定されたURLにHTTPリクエストを送信し、
// 成功か失敗かをchannelを通して返します。
func sendRequest(url string, wg *sync.WaitGroup, ch chan<- bool) {
	defer wg.Done()

	_, err := http.Get(url)
	if err != nil {
		ch <- false // 失敗
		return
	}

	ch <- true // 成功
}

func main() {
	var wg sync.WaitGroup
	requestCount := 100            // 送信するリクエストの回数
	url := "http://localhost:3000" // リクエストを送信するURL

	// 成功と失敗をカウントするための変数
	var successCount, failureCount int

	// 結果を受け取るためのchannelを作成
	ch := make(chan bool, requestCount)

	for i := 0; i < requestCount; i++ {
		wg.Add(1)
		// 各リクエストをgoroutineで実行
		go sendRequest(url, &wg, ch)
	}

	// すべてのgoroutineの完了を待機
	wg.Wait()
	close(ch)

	// 成功と失敗をカウント
	for result := range ch {
		if result {
			successCount++
		} else {
			failureCount++
		}
	}

	// 結果を表示
	fmt.Printf("Success: %d, Failure: %d\n", successCount, failureCount)
}

リクエストの成功、失敗それぞれを最後に出力しています。

100回

まずは100回送ってみます。
Railsから。

$ time go run app/day18/main.go
Request Count: 100
URL: http://localhost:3000
Success: 100, Failure: 0

real	0m4.582s
user	0m0.315s
sys	0m0.301s

次にGoです。

$ time go run app/day18/main.go
Request Count: 100
URL: http://localhost:3002
Success: 100, Failure: 0

real	0m0.395s
user	0m0.285s
sys	0m0.290s

どちらもすべてのリクエストをさばけていますが、Goのほうが10倍ほど速いですね。

200回

Rails

$ time go run app/day18/main.go
Request Count: 200
URL: http://localhost:3000
Success: 128, Failure: 72

real	0m5.877s
user	0m0.329s
sys	0m0.302s

Go

$ time go run app/day18/main.go
Request Count: 200
URL: http://localhost:3002
Success: 128, Failure: 72

real	0m0.549s
user	0m0.333s
sys	0m0.349s

ともに半分弱のリクエストが失敗となりましたが、速度差は変わらず。

500回

Rails

time go run app/day18/main.go
Request Count: 500
URL: http://localhost:3000
Success: 135, Failure: 365

real	0m6.139s
user	0m0.373s
sys	0m0.358s

Go

$ time go run app/day18/main.go
Request Count: 500
URL: http://localhost:3002
Success: 133, Failure: 367

real	0m0.626s
user	0m0.367s
sys	0m0.351s

傾向は変わらず。
成功、失敗はどちらも大差がないので、どうやらメモリやCPUの性能によって決まりそうだということがわかりますね。(どちらもDockerで2GBのメモリを指定しています)

まとめ

Framework Request Count URL Success Failure Real Time User Time Sys Time
Rails 100 http://localhost:3000 100 0 0m4.582s 0m0.315s 0m0.301s
Go 100 http://localhost:3002 100 0 0m0.395s 0m0.285s 0m0.290s
Rails 200 http://localhost:3000 128 72 0m5.877s 0m0.329s 0m0.302s
Go 200 http://localhost:3002 128 72 0m0.549s 0m0.333s 0m0.349s
Rails 500 http://localhost:3000 135 365 0m6.139s 0m0.373s 0m0.358s
Go 500 http://localhost:3002 133 367 0m0.626s 0m0.367s 0m0.351s
  • echoの方がRailsより10倍速い
  • 耐アクセス性能は言語による差分はなく、メモリやCPUの性能に依存すると思われる

速く捌ければそれだけたくさん処理できると思っていましたが、そういうわけでもないようです。

今回はDBへアクセスしていないので、純粋なhttpサーバーの性能比較となりました。
もしDBへアクセスするようにした場合、ORMの性能も影響してきます。
Activerecordは素晴らしいですが、実行速度はお世辞にも速いとは言えません。
なので、Webアプリケーションとしての性能差はもっと開くのではないかと思います。

おまけ

Chat GPTに検証結果の標準出力を与えてグラフを作成してもらいました。
(こうしてみてみるとリクエスト数200がこの環境の限界のようですね)

ea3ad4ab-3c68-4b55-9aea-baaaa18f2f8d.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?