😀

Go言語のnet/httpでWebサーバを作る時に心配したこと

2024/03/09に公開

概要

二つの心配事
・Webサーバが複数リクエストを捌けるか
・ハンドラーに依存性注入しているクラスのインスタンスが毎回作られるか

Webサーバを建てる

以下のコードを実行する

package main

import (
	"fmt"
	"net/http"
)

type hoge struct {
	count int
}

func NewHoge() hoge {
	return hoge{count: 0}
}

func (h hoge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.count++
	fmt.Printf("hoge %dth\n", h.count)
}

func main() {
	http.Handle("/hoge", NewHoge())
	http.ListenAndServe(":8080", nil)
}

/hogeでリクエストが来たらハンドラの処理が走る
リクエストが来た時に、ハンドラがどのように生成されるかを把握するために
ハンドラにはcount変数を持たせて、ServeHTTPが呼ばれるたびに+1している

一つ目の心配: 複数リクエストを捌けるか

まず結論: net/httpを使うとリクエストごとにハンドラが生成されて捌かれる

apachebenchを使って調べる
以下のコマンドでは50リクエストをサーバに一斉に投げる
ab -n 50 -c 50 localhost:8080/hoge

その結果は
hoge 1th
という標準出力が50行羅列される

これはハンドラのcount変数を出力したものだが
1thから50thではない事から
リクエストの度にハンドラが生成されている事が分かった

どうやらリクエストの度にgoroutineが起動しているようで
その中でハンドラが毎回生成されているからだと考えられる

ハンドラに依存性注入する

モックを使えるようにするために、ハンドラにロジッククラスを注入したい

package main

import (
	"fmt"
	"net/http"
)

type logic struct {
	count int
}

func NewLogic() logic {
	return logic{
		count: 0,
	}
}

func (l logic) increment() {
	l.count++
	fmt.Printf("logic %dth\n", l.count)
}

type hoge struct {
	l logic
}

func NewHoge(l logic) hoge {
	return hoge{
		l,
	}
}

func (h hoge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.l.increment()
}

func main() {
	logic := NewLogic()
	http.Handle("/hoge", NewHoge(logic))
	http.ListenAndServe(":8080", nil)
}


上のコードでは注入したlogicというクラスをカウントしている

2つ目の心配事: 注入したインスタンスが毎回作られるか

まず結論: ハンドラがリクエストごとに作られるので、注入したやつも毎回作られる

以下のコマンドでは50リクエストをサーバに一斉に投げる
ab -n 50 -c 50 localhost:8080/hoge

その結果は
logic 1th
という標準出力が50行羅列される

ちゃんと注入したインスタンスも毎回作られるので
処理時間のかかるlogic部分でも、待ち時間が発生することは無さそう

Discussion