標準ライブラリだけでサーバを作る
目標
標準ライブラリだけでHTTPサーバーを作りながら、Goが何を強制するのかを見ます。
フレームワークを選ぶ前に、net/httpがすでに何を提供しているのかを確認します。
なぜ重要なのか
Goを初めて使う人は、if err != nilにうんざりすると言います。しかしその長さは
コストではなく、明示性の対価です。どの行が失敗しうるのかが型に
書かれていて、無視するなら無視すると書く必要があります。
このラボでは、その代償と利点の両方を実際に体験します。そして、Goで実際に 事故が起きる場所(ゴルーチンのリーク、キャンセルの未伝播)を自分で作ってみます。
環境
ネットワークがないためgo getは動作しません。このラボは標準ライブラリだけを
使うので問題ありません。むしろそれがこのコースの論点です。
go mod init <이름> 모듈 시작
go build -o srv . 빌드
go vet ./... 흔한 실수 찾기
gofmt -l . 포맷이 어긋난 파일 나열
ステップ
- モジュールの初期化 →
/root/work/gosrv/go.mod /healthサーバー- エラーを値として扱う(
%wでラップ) errors.Is/As→/root/work/gosrv/04-errors.txt- JSONレスポンス →
/root/work/gosrv/05-json.txt - contextでキャンセルを伝播 →
/root/work/gosrv/06-ctx.txt - ゴルーチンのリーク →
/root/work/gosrv/07-leak.txt go vet/gofmt→/root/work/gosrv/08-vet.txt
参考
- サーバーを起動したあとは、
kill %1で片付けます。8080が塞がったままだと、次の ビルドが起動できません。 - エラーメッセージは小文字で始め、ピリオドで終えません。ほかのメッセージの中に 埋め込まれるからです。
モジュールを作成する
モジュールの初期化 → /root/work/gosrv/go.mod
mkdir -p /root/work/gosrv && cd /root/work/gosrv && go mod init labhub.local/gosrvを実行してください。ネットワークがないので外部パッケージは取得できません。このラボは標準ライブラリだけを使います。go.modができれば完了です。
HTTPサーバーを1つ作る
/healthサーバー
main.goでnet/httpを使って/healthを作り、8080で待ち受けさせます。go build -o srv .でビルドしたあと、./srv &で起動し、curl -s localhost:8080/healthで確認してください。フレームワークなしで、ここまでが標準ライブラリに入っています。
エラーを値として返す
エラーを値として扱う(%wでラップ)
parseAge(s string) (int, error)を作り、数字でなければエラーを返すようにします。ラップするときは、fmt.Errorf("나이 파싱(%s): %w", s, err)(韓国語の文字列は「年齢のパース」という意味です)のように%wを使ってください。%vでラップすると元のエラーが失われ、errors.Isで問い合わせられなくなります。
原因を問い合わせる
errors.Is/As → /root/work/gosrv/04-errors.txt
errors.Isまたはerrors.Asで、ラップしたエラーの原因を判別するコードを入れ、その出力を/root/work/gosrv/04-errors.txtに保存してください。ラップしたエラーから元のエラーを取り出せることを、目で確認するのが目的です。
JSONを返す
JSONレスポンス → /root/work/gosrv/05-json.txt
encoding/jsonで構造体をレスポンスします。フィールドタグ(json:"name")を付けないと、大文字のまま出力されます。Goのexportルールのためです。curl -s localhost:8080/api | jq .の結果を/root/work/gosrv/05-json.txtに保存してください。
キャンセルを伝播する
contextでキャンセルを伝播 → /root/work/gosrv/06-ctx.txt
遅いハンドラーを作り、r.Context()のDone()を監視して、クライアントが切断したら処理を止めるようにしてください。curl --max-time 1で切断し、サーバーのログにキャンセルが出力されるか確認します。出力を/root/work/gosrv/06-ctx.txtに保存してください。
ゴルーチンがリークするのを見る
ゴルーチンのリーク → /root/work/gosrv/07-leak.txt
わざとリークするゴルーチン(受け取り側のいないチャネルへ送信する)を作り、runtime.NumGoroutine()を前後で出力して/root/work/gosrv/07-leak.txtに保存してください。数が増えたまま減らないのがリークです。
先にツールに聞く
go vet / gofmt → /root/work/gosrv/08-vet.txt
go vet ./...とgofmt -l .を実行し、結果を/root/work/gosrv/08-vet.txtに保存してください。どちらも静かなら合格です。Goはフォーマットを議論しません。gofmtが正解であり、だからレビューでその話が消えます。