GolangのHTTPサーバーのコードリーディング

goblinルーター開発時に調査したGoのHTTPサーバー内部実装を解説。http.Server構造体、ServeMux、Handler登録フロー、goroutineによるコネクション処理を実装コードから読み解く。

Read in: en
GolangのHTTPサーバーのコードリーディング

概要

Goでrouterを作ったときにHTTPサーバーのコードの内部を読んだので、その時のメモ。

github.com - bmf-san/goblin

HTTPサーバーのコードリーディング

基本形

Goに入門したとによく見るであろう形のコード。

色々なものが省略されてこの形になっている。

package main

import (
	"net/http"
)

func main() {
	http.HandlerFunc("/index", func(w http.ReponseWriter, req *http.Request) {
		w.Write([]byte("hello world"))
	})

	http.ListenAndServe(":8080")
}

省略しないで丁寧に書いた形

先程の基本形を省略しないで書いた形。 どのような実装を経て基本形になるのか1つずつ確認していく。

package main

import (
	"net/http"
)

func main() {
	// マルチプレクサ。URLマッチングをするための構造体。静的なルーティングのみ解決する。
	mux := http.NewServeMux()
	ih := new(indexHandler)
	// muxにルーティングを登録する
	mux.Handle("/index", ih)

	srv := http.Server{
		Addr: ":8080",
		Handler: mux,
	}

	srv.ListenAndServe()
}

// Handlerインターフェースを実装した構造体。
type indexHandler struct{}

// ServeHTTPを実装
func (i *indexHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("helo world"))
}

Handlerの置き換え

まずは、Handlerの置き換え。

ServeHTTPは関数型のaliasであるHandlerFuncに置き換えることができる。

cf.


package main

import (
    "net/http"
)

func main() {
    mux := http.NewServeMux()

	// ただの関数をHandlerFunc型にキャストすれば良い。Handlerインターフェースを満たせる。
    mux.Handle("/index", http.HandlerFunc(indexHandler))

    s := http.Server{
        Addr:    ":8080",
        Handler: m,
    }
    s.ListenAndServe()
}

// 適当な構造体を用意して、ServeHTTPを実装しなくても良い。
func indexHandler(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("hello world"))
}

DefaultServeMuxの利用

muxをDefaultServeMuxで代用。

DefaultServeMuxはServeMux型の構造体を持っている。

HandlerFuncというmuxにルーティングを登録する関数を実装している。

cf.

package main

import (
    "net/http"
)

func main() {
	// muxを作らなくてもこれだけでOK
    http.HandleFunc("/index", indexHandler)

    s := http.Server{
        Addr: ":3000",
		// net/httpがデフォルトで持っている変数。DefaultServeMuxはServeMux型の構造体を持っている。HandlerFuncというmuxにルーティングを登録する関数を実装している。
        Handler: http.DefaultServeMux,
    }
    s.ListenAndServe()
}

func indexHandler(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("hello world"))
}

ListenAndServe()の利用

Server構造体(http.Server{})を作らずとも、ListenAndServe()を代用することができる。

cf.

package main

import (
    "net/http"
)

func main() {
	http.HandlerFunc("/index", func(w http.ReponseWriter, req *http.Request) {
		w.Write([]byte("hello world"))
	})

	// Server構造体(http.Server{})を作らなくても大丈夫
	http.ListenAndServe(":8080")
}

これで最初の基本形に到達。

まとめ

APIサーバー作るときとか普段余り意識しないと思うが、知っておくと何か拡張したいときに役に立つ、はず。

routerを作るときはhttp.Handlerのインターフェースを意識してmuxを作って上げれば良い。

元ネタ

GoでRouter自作実装寄りな話

Tags: Golang コードリーディング
Share: 𝕏 Post Facebook Hatena
✏️ View source / Discuss on GitHub
☕ サポート

このブログを応援していただける方は、以下からサポートをお願いします。いただいたサポートはブログ運営・技術研鑽に活用します。


関連記事