Files
astaxie aef1a50bf1 Merge pull request #569 from oleksiif/master
Adjustment to line which explain how corresponding handler is calling…
2015-11-24 11:22:06 +08:00

88 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 3.3 GoはどのようにしてWeb作業を行うか
前の節でどのようにGoを通じてWebサービスを立てるかご紹介しました。net/httpパッケージを簡単に応用して便利に立てることができたかと思います。では、Goは低レイヤーで一体何を行なっているのでしょうか万物は姿を変えてもその元は同じであります。GoのWebサービス作業も第一章でご紹介したWebの作業方法に関係しています。
## webの作業方法のいくつかの概念
以下はどれもサーバの概念のいくつかです
Requestユーザが要求するデータ。ユーザのリクエスト情報を解析します。post、get、cookie、url等の情報を含みます。
Responseサーバがクライアントにデータをフィードバックする必要があります。
Connユーザの毎回のリクエストリンクです。
Handlerリクエストを処理し、返すデータを生成する処理ロジック。
## httpパッケージが実行する機能を分析する
下の図はGoが実現するWebサービスの作業モードのプロセス図です
![](images/3.3.http.png?raw=true)
図3.9 httpパッケージの実行フロー
1. Listen Socketを作成し、指定したポートを監視します。クライアントのリクエストを待ちます。
2. Listen Socketはクライアントのリクエストを受け付けます。Client Socketを得ると、Client Socketを通じてクライアントと通信を行います。 
3. クライアントのリクエストを処理します。まず、Client SocketからHTTPリクエストのプロトコルヘッダを読み取り、もしPOSTメソッドであれば、クライアントが入力するデータをさらに読み取るかもしれません。その後対応するhandlerがリクエストを処理します。handlerがクライアントの要求するデータを準備し終えたら、Client Socketを通じてクライアントに書き出します。
この全体のプロセスではつの問題についてだけ理解しておけば構いません。これはまたGoがいかにしてWebを実行するのかということを知るという意味です。
- どのようにポートを監視するか?
- クライアントのリクエストをどのように受け付けるか?
- handlerにどのように受け渡すか
前の節のコードではGoは関数`ListenAndServe`を通してこれらの事を処理していました。ここでは実はこのように処理していますserverオブジェクトを初期化します。その後`net.Listen("tcp", addr)`をコールします。つまり、低レイヤでTCPプロトコルを用いてサービスを立ち上げます。その後我々が設定したポートを監視します。
下のコードはGoのhttpパッケージのソースコードから引用したものです。下のコードで全体のHTTP処理プロセスを見ることができます。
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()
}
}
監視した後どのようにしてクライアントのリクエストを受け取るのでしょうか?上のコードではポートの監視を実行後、`srv.Serve(net.Listener)`関数をコールしています。この関数はクライアントのリクエスト情報を処理しています。この関数では`for{}`が置かれており、まずListenerを通じてリクエストを受け取った後、Connを作成します。最後に単独のgoroutineを開きます。このリクエストのデータを引数としてこのconnに渡します。`go c.serve()`。これはマルチスレッドを行なっています。ユーザが行うリクエストはすべて真新しいgoroutineの上で行われ、互いに影響しません。
ではいかにして具体的に目的の関数でリクエストを処理するように振り分けるのでしょうかconnはまずrequestを解析します`c.readRequest()`、その後目的のhandlerを取得します`handler := sh.srv.Handler`、つまり、我々がさきほど`ListenAndServe`をコールした時、そのつ目の引数です。前の例でnilを渡したのですが、これは空ということです。デフォルトで`handler = DefaultServeMux`を取得します。この変数は一体何に使われるのでしょうかそうです。この変数はルータです。これはマッチするurlを対応するhandler関数にリダイレクトするために用いられます。我々はこれを設定したでしょうかええ。我々がコールしたコードのいの一番で`http.HandleFunc("/", sayhelloName)`をコールしたじゃないですか。これは`/`をリクエストするルートのルールを登録します。urlが"/"をリクエストした場合、ルートは関数sayhelloNameにリダイレクトします。DefaultServeMuxはServeHTTPメソッドをコールします。このメソッド内では実はsayhelloName本体をコールしています。最後にresponseの情報を入力することで、クライアントにフィードバックを返します。
全体のフローの詳細は以下の図の通りです:
![](images/3.3.illustrator.png?raw=true)
図3.10 http接続の処理フロー
ここに来て我々はつの問題に対して全て解答を得ました。Goが如何にWebを走らせるか、すでに基本的なことは理解されたのではないでしょうか
## links
* [目次](<preface.md>)
* 前へ: [GOで簡単なwebサービスを立ち上げる](<03.2.md>)
* 次へ: [Goのhttpパッケージ詳細](<03.4.md>)