Build Web Application With Golang を試してやってみる 11
はじめに
前回の記事の続きです。
前回の記事はこちら
Goのhttpパッケージ詳細
前回は、Go
がWebサーバを立てている裏で何をしているのかを見ていきました。
今回は、http
パッケージの中身を見ていくようです。Conn
と ServerMux
というのがコアの機能と書いてあります。
Connのgoroutine
goroutineを使うと、ノンブロッキングで効率的にレスポンスができるみたいです。
コードが書いてありますね、これは前回も出てきたものです、これでリクエストを待ち受けるようです。
クライアントの各リクエストはどれもConnを一つ作成しているのがわかるかと思います。
このConnには今回のリクエストの情報が保存されています。
これは目的のhandlerに渡され、このhandlerで目的のhandler情報を読み取ることができます。
このように各リクエストの独立性を保証します。
なるほど。
リクエストごとにConn
を張っているようですね、これで独立性を担保しているようです。
(1つ1つにConn
を作成すると、そうでない場合に比べリソースを喰いそうな気がしますがどうなんでしょうか。)
ServeMuxのカスタム定義
前の節でconn.serverについてご説明した際、拾は内部ではhttpパッケージのデフォルトのルートをコールしていました。
確かに!そうでした。
ルータを通して今回のリクエストのデータをバックエンドの処理関数に渡します。ではこのルータはどのように実現されているのでしょうか?
|
|
|
|
|
|
あーなんとなく構造が見えてきました。
Handlerはインターフェースですが、
前の節の中のsayhelloName関数では特にServeHTTPというインターフェースを実装してはいませんでした。どうして追加できるのでしょうか?
そうね!
もともとhttpパッケージの中ではHandlerFuncという型が定義されています。
私達が定義したsayhelloName関数はまさにこのHandlerFuncがコールされた結果であり、
この型はデフォルトでServeHTTPインターフェースを実装していることになります。
どれどれ、HandlerFunc
を見てみます。
|
|
見たことありますねぇ!
つまり、HandlerFunc(f)をコールして強制的にfをHandlerFunc型に型変換しているのです。
このようにしてfはServeHTTPメソッドを持つようになります。
ServeHTTP
というのが実装されているわけですね。
ルータでは対応するルーティングルールを保存した後、具体的にはどのようにリクエストを振り分けているのでしょうか?以下のコードをご覧ください。
muxEntry
型として保存されてイますが、たしかにこれを実行するのはどうしているのでしょう。
|
|
上に示す通りルータはリクエストを受け取った後、*であれば接続を切断し、
そうでなければmux.handler(r).ServeHTTP(w, r)をコールして対応する設定された処理Handlerを返し、h.ServeHTTP(w, r)を実行します。
ほうほう、パターンを検索して対応する関数をもらってきて実行するみたいですね。
つまり、目的のルーティングのhandlerのServerHTTPインターフェースへのコールです。ではmux.Handler(r)はどのように処理するのでしょうか?
更に中身ですね、パターンを検索する部分
のところですね。
|
|
長いですが、
もともとこれはユーザのリクエストしたURLとルータの中に保存されているmapに従ってマッチングしています。マッチングによって保存されているhandlerが返されるにあたり、このhandlerのServeHTTPインターフェースがコールされ、目的の関数を実行することができます。
|
|
ここの部分ですね。
上の紹介を通して、私達はルーティングの全体プロセスを理解しました。Goは実は外部で実装されたルータをサポートしています。ListenAndServeの第2引数が外部のルータを設定する為に用いられます。これはHandlerインターフェースのひとつで、外部ルータでHandlerインターフェースを実装し、そのServeHTTPにカスタム定義のルーティング機能を実装することができます。
多少なりとも理解できました、さてコードがあるので早速実行してみます。
Not Found
のパターンです。 (ルートに当てはまらない場合)
長いですが実行プロセスを追っていきます。
Goのコードの実行プロセス
http
パッケージへの分析を通して、全体のコードの実行プロセスを整理してみましょう。
まずHttp.HandleFunc
をコールします。
順序にしたがっていくつかの事を行います:
1DefaultServeMux
のHandlerFunc
をコールする。
2DefaultServeMux
のHandle
をコールする。
3DefaultServeMux
のmap[string]muxEntry
で目的のhandler
とルーティングルールを追加する。
次にhttp.ListenAndServe(":9090", nil)
をコールする。
順序にしたがっていくつかの事を行う:
1Server
のエンティティ化
2Server
のListenAndServe()
をコールする
3 net.Listen(“tcp”, addr)をコールし、ポートを監視する
4 forループを起動し、ループの中でリクエストをAcceptする
5 各リクエストに対してConnを一つエンティティ化し、このリクエストに対しgoroutineを一つ開いてgo c.serve()
のサービスを行う。
6 各リクエストの内容を読み込むw, err := c.readRequest()
7 handlerが空でないか判断する。もしhandlerが設定されていなければ(この例ではhandlerは設定していません)、handler
はDefaultServeMux
に設定されます。
8handler
のServeHttp
をコールする
9 この例の中では、この後DefaultServeMux.ServeHttp
の中に入ります
10 requestに従ってhandlerを選択し、このhandlerのServeHTTPに入りますmux.handler(r).ServeHTTP(w, r)
11 handlerを選択します:
A ルータがこのrequestを満足したか判断します(ループによってServerMuxのmuxEntryを走査します。)
B もしルーティングされれば、このルーティングhandlerのServeHttpをコールします。
C ルーティングされなければ、NotFoundHandlerのServeHttpをコールします
A, B, C が キャプチャのパターンに対応していますね。
前章と合わせると中身がだいぶわかってきたような気がします。
おわりに
次回はまとめですが、こちらは飛ばして フォーム
をやっていきます。まとめ
は裏で読んでおきます・・・!