この章について
前章までは、実際にコード内で呼ばれている関数・メソッドを網羅する形で処理の流れを追っていきました。
そこで作った図は「正確」ではあるのですが、インターフェースや具体型が入り混じっており、その分大枠は掴みづらいものになっています。
そのためここでは、上で紹介した2つのフェーズの重要ポイントだけに絞る形で、処理の大枠をまとめ直してみます。
2つのフェーズ
GoでWebサーバーを起動させるときの処理は、大きく2つのフェーズに分けることができます。
-
http.Server
型やnet.Conn
インターフェースの作成といった、サーバーの起動処理 - 実際に受信したリクエストをハンドラに処理させる、リクエストハンドリング
処理の大枠
ここでは、上で紹介した2つのフェーズの大枠を述べていきます。
「インターフェース」で見る
処理の重要ポイントだけ抽出するには、メソッドセットの形である程度の抽象化がなされているインターフェースに着目するのがいいです。
すると、処理の大枠は下図のようにまとめることができます。
1. サーバー起動
サーバーの起動部分で、最初に呼び出されるハンドラを内部に持つhttp.Server
型と、http通信をするためのnet.Conn
インターフェースを作成しています。
net.Conn
がhttp.Server
型の外にあるのは、おそらく依存性注入の観点での設計です。
-
http.Server
型が持つルーティング情報は、どの環境で動かしたとしても不変なもの -
net.Conn
が持つURLホストやポートといったネットワーク環境情報は、状況によって変わる
これを踏まえて、もしURLやコネクションが変わったとしてもhttp.Server
型を作り直さなくてもいいようにしているのです。
2. リクエストハンドリング
実際にリクエストを受けて、レスポンスを返す段階になると、http.Server
型はServeHTTP
メソッドがあるhttp.serverHandler
型にキャストされた上で、そのServeHTTP
メソッドを呼び出すことでリクエストを捌いていきます。
serverHandler
型から最初に呼び出されるhttp.Handler
は、http.ListenAndServe
の第二引数に渡されたルーティングハンドラです(=デフォルトだとDefaultServeMux
)。
リクエストを受け取ったhttp.Handler
は、リクエストパスを見て、他のhttp.Handler
に処理を委譲するか、自身でレスポンス作成をするかのどちらかの処理を行います。
具体型で見る
インターフェースで見た場合、リクエストをハンドルする部品は全てhttp.Handler
でした。
「他のhttp.Handler
に処理を移譲するハンドラ」と「自身でリクエストを処理するハンドラ」の違いは一体なんなのでしょうか。
それをわかりやすくするために、上記の図をhttp.Handler
インターフェースを満たしうる実体型で書き換えました。
http.Handler
インターフェース部分の具体型として使われているのは、大きく分けて二種類です。
-
http.ServeMux
型: ルーティングハンドラ。リクエストパスをみて、他のハンドラに処理を振り分ける役割を担う。 -
http.HandlerFunc
型: ユーザーが書いたhttpハンドラ。実際にレスポンス内容を作成し、net.Conn
に書き込む役割を担う。
「http.ServeMux
型にするか、http.HandlerFunc
型にするか」の選択イメージについては、以下の図のように「パス/users
以降は別のハンドラに任せる」というようなハンドリングをする場合を思い浮かべてもらえればわかりやすいかと思います。