はじめに

前回の記事の続きです。
前回の記事はこちら

GoはどのようにしてWeb作業を行うか

前回は、Webサーバを立ててそのプログラムについて確認しました。
今回は裏でどういうことをしているのかの部分ですね。

webの作業方法のいくつかの概念

Request:ユーザが要求するデータ。ユーザのリクエスト情報を解析します。post、get、cookie、url等の情報を含みます。
Response:サーバがクライアントにデータをフィードバックする必要があります。 Conn:ユーザの毎回のリクエストリンクです。
Handler:リクエストを処理し、返すデータを生成する処理ロジック。

代表的なものですね。

httpパッケージが実行する機能を分析する

Client→Serverへの接続時に何が起きているのかの図があります。
解説に沿ってみていきます。

  1. Listen Socketを作成し、指定したポートを監視します。クライアントのリクエストを待ちます。

サーバ側で、Socket をつくってずっと待っている状態ですね。

  1. Listen Socketはクライアントのリクエストを受け付けます。Client Socketを得ると、Client Socketを通じてクライアントと通信を行います。 

Listen Socket の説明ですね。 クライアントと通信を行う部分のことです。
Client(ブラウザ)のソケットとサーバのソケット間で通信を行うようです。

  1. クライアントのリクエストを処理します。まず、Client SocketからHTTPリクエストのプロトコルヘッダを読み取り、もしPOSTメソッドであれば、クライアントが入力するデータをさらに読み取るかもしれません。その後対応するhandlerがリクエストを処理します。handlerがクライアントの要求するデータを準備し終えたら、Client Socketを通じてクライアントに書き出します。

サーバ側でリクエストを処理し、色々処理して結果をクライアントに返します。

この全体のプロセスでは3つの問題についてだけ理解しておけば構いません。
これはまたGoがいかにしてWebを実行するのかということを知るという意味です。

  • どのようにポートを監視するか?
  • クライアントのリクエストをどのように受け付けるか?
  • handlerにどのように受け渡すか?

このあたりは前回解析してみましたね、どうやらこの章で説明してくれるようです。
読んでみます。

GoのHttpサーバソース

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        go c.serve()
    }
}

上記が http パッケージのソースのようです。
先程、図でみたものと処理を照らし合わせてみます。

defer l.Close()  
これは、ソケットのクローズ処理だと思います。しっかり、defer で抜けた際にクローズできるようになっているようです。
for {} の中身を見てみると、 rw, e := l.Accept()
これはSocket が通信を許可したときに、rw が書き込まれる値(?), e はエラー値ですね。
その後エラー処理がずらっとあって(ここは飛ばします)
c, err := srv.newConn(rw)
で新しいコネクションを作っていますね。
ここで接続が確立する?っぽいですね。
最後に、go c.serve() でリクエストは別のところで処理されているっぽさそうです。

上記が自分なりの解析ですが、どうでしょうか。解説の部分を見てみます。
あってそうですかね。

c.serve() の処理の続きが図に書いてあるので見てみると、
この後 c.readRequest() でリクエスト読み出して、解析をして、 handler で登録されている関数が呼び出されるようなことが書いてありますね。
前章で書いたプログラムがだいぶわかってきました。

おわりに

次回はGoのhttpパッケージ詳細です。
プログラム書き始めてからやっと、スニペットを使い始めました。
VSCodeでスニペット登録していますが、はまるとかなり強そうです。
まだ使いこなせていませんが・・・!