Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1625] ファイルの概要

このコミットは、Go言語の標準ライブラリであるsrc/lib/http/triv.goファイルに対する変更です。このファイルは、GoのHTTPサーバー機能の基本的な使用方法や、Goの並行処理の概念(特にチャネル)をHTTPサーバーのコンテキストでどのように活用できるかを示すための、教育的な(pedagogical)目的を持つサンプルコードが含まれていると推測されます。具体的には、HTTPリクエストを処理する際にGoのチャネルを利用する新しいハンドラの実装が追加されています。

コミット

このコミットは、Rob Pikeによって2009年2月5日に作成されました。Go言語の初期段階における、チャネルがHTTPサーバーインターフェースを満たすことができるという、教育的なデモンストレーションを目的としています。src/lib/http/triv.goファイルに18行のコードが追加され、既存のコードの削除や変更はありません。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f95a11e27f8465435dd8a69ca6da67bbc6957270

元コミット内容

    further pedagogy: a channel that satisfies the HTTP server interface
    
    R=rsc
    DELTA=18  (18 added, 0 deleted, 0 changed)
    OCL=24482
    CL=24484

変更の背景

この変更の背景には、Go言語の設計思想、特にその強力な並行処理プリミティブである「チャネル」の教育的な側面があります。Go言語は、CSP(Communicating Sequential Processes)に影響を受けた並行処理モデルを採用しており、ゴルーチン(goroutine)とチャネル(channel)を通じて、シンプルかつ効率的な並行プログラミングを可能にします。

このコミットの目的は、「チャネルがHTTPサーバーインターフェースを満たす」という概念を具体的に示すことにあります。これは、Goのインターフェースの柔軟性と、チャネルが単なるデータ転送メカニズムに留まらず、より抽象的な振る舞いを表現するための基盤となり得ることをデモンストレーションしています。HTTPサーバーのハンドラは通常、特定のインターフェース(http.Handler)を実装することで機能しますが、このコミットでは、そのインターフェースをチャネル型が満たすようにすることで、HTTPリクエストの処理をチャネル操作と結びつけるユニークな例を提供しています。これは、Goの設計者が言語の機能と哲学をどのように教育し、普及させようとしていたかを示す一例と言えるでしょう。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と、net/httpパッケージに関する知識が必要です。

  • Go言語のゴルーチン (Goroutine): Go言語における軽量なスレッドのようなものです。goキーワードを関数呼び出しの前に置くことで、その関数を新しいゴルーチンとして実行し、他の処理と並行して動作させることができます。OSのスレッドよりもはるかに軽量で、数千、数万のゴルーチンを同時に実行することが可能です。

  • Go言語のチャネル (Channel): ゴルーチン間で値を送受信するための通信メカニズムです。チャネルは、ゴルーチン間の同期と通信を安全に行うための主要な手段であり、共有メモリによる競合状態(race condition)を避けるのに役立ちます。チャネルはmake(chan Type)で作成され、ch <- valueで値を送信し、value := <-chで値を受信します。チャネルからの受信は、値が送信されるまでブロックされます。

  • net/httpパッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーを実装するための機能を提供します。

    • http.Handlerインターフェース: HTTPリクエストを処理するためのインターフェースです。このインターフェースは、ServeHTTP(ResponseWriter, *Request)という単一のメソッドを定義しています。任意の型がこのメソッドを実装していれば、その型はhttp.Handlerインターフェースを満たしているとみなされ、HTTPリクエストハンドラとして登録できます。
    • http.Handle(pattern string, handler Handler): 指定されたパスパターン(pattern)に対するHTTPリクエストを処理するために、http.Handlerインターフェースを実装したhandlerを登録します。
    • http.ListenAndServe(addr string, handler Handler): 指定されたアドレス(addr)でHTTPサーバーを起動します。handlernilの場合、デフォルトのhttp.DefaultServeMuxが使用されます。
  • Go言語のインターフェース: Go言語におけるインターフェースは、メソッドのシグネチャの集合です。型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たしているとみなされます。Goのインターフェースは「暗黙的」であり、明示的に「このインターフェースを実装します」と宣言する必要はありません。

技術的詳細

このコミットの核心は、Goのチャネル型がhttp.Handlerインターフェースをどのように満たすかを示す点にあります。通常、HTTPハンドラは構造体や関数型として定義され、その上でServeHTTPメソッドを実装します。しかし、この例では、Chanという名前のchan int型(整数のチャネル)を定義し、そのChan型に対してServeHTTPメソッドを実装しています。

具体的には、以下のステップでこのデモンストレーションが行われています。

  1. Chan型の定義: type Chan chan int これにより、Chanint型のチャネルのエイリアスとして定義されます。

  2. ChanCreate関数の実装: func ChanCreate() Chan この関数は、新しいChanintチャネル)を作成し、そのチャネルを返すとともに、新しいゴルーチンを起動します。このゴルーチンは無限ループで、xというカウンタ変数を0からインクリメントし続け、その値をチャネルに送信し続けます(c <- x)。これにより、チャネルは常に新しい整数値を生成し続ける「プロデューサー」となります。

  3. Chan型に対するServeHTTPメソッドの実装: func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) これが最も重要な部分です。Chan型がhttp.Handlerインターフェースを満たすために、このメソッドが実装されています。このメソッドが呼び出される(つまり、/chanパスへのHTTPリクエストがあった)たびに、<-chという操作によってチャネルから値を受信します。チャネルからの受信は、値が利用可能になるまでブロックされます。受信した値は、fmt.Sprintf("channel send #%d\\n", <-ch)によって文字列にフォーマットされ、io.WriteString(c, ...)を通じてHTTPレスポンスとしてクライアントに書き込まれます。

  4. HTTPハンドラとしての登録: http.Handle("/chan", ChanCreate()) main関数内で、/chanというパスに対してChanCreate()の戻り値(つまり、Chan型のインスタンス)をハンドラとして登録しています。ChanCreate()はチャネルを生成し、そのチャネルに値を送り続けるゴルーチンを起動するため、/chanへの各リクエストは、このチャネルから次の整数値を受け取り、それを表示することになります。

このアプローチの技術的な含意は、HTTPリクエストの処理が、Goの並行処理プリミティブであるチャネルの操作と直接結びついている点です。各リクエストはチャネルからの受信操作をトリガーし、これにより、複数のリクエストが同時に発生した場合でも、チャネルの同期メカニズムによって値が順番に(または、チャネルのバッファリング設定に応じて)処理されることが保証されます。これは、Goのインターフェースの柔軟性と、並行処理をアプリケーションロジックに自然に統合できるGoの能力を示す優れた例です。

コアとなるコードの変更箇所

diff --git a/src/lib/http/triv.go b/src/lib/http/triv.go
index 136100135a..3c527310ed 100644
--- a/src/lib/http/triv.go
+++ b/src/lib/http/triv.go
@@ -45,11 +45,29 @@ func FileServer(c *http.Conn, req *http.Request) {
 	fmt.Fprintf(c, "[%d bytes]\n", n);
 }
 
+// a channel (just for the fun of it)
+type Chan chan int
+
+func ChanCreate() Chan {
+	c := make(Chan);
+	go func(c Chan) {
+		for x := 0;; x++ {
+			c <- x
+		}
+	}(c);
+	return c;
+}
+
+func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) {
+	io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch));
+}
+
 func main() {
 	flag.Parse();
 	http.Handle("/counter", new(Counter));
 	http.Handle("/go/", http.HandlerFunc(FileServer));
 	http.Handle("/go/hello", http.HandlerFunc(HelloServer));
+	http.Handle("/chan", ChanCreate());
 	err := http.ListenAndServe(":12345", nil);
 	if err != nil {
 		panic("ListenAndServe: ", err.String())

コアとなるコードの解説

追加されたコードは以下の3つの主要な部分と、main関数でのその利用から構成されます。

  1. type Chan chan int: これは、Chanという新しい型を定義しています。この型は、Goの組み込み型であるint型のチャネル(chan int)のエイリアスです。これにより、コード内でchan intと書く代わりにChanと書くことができ、可読性が向上します。

  2. func ChanCreate() Chan: この関数は、Chan型の新しいチャネルを作成し、そのチャネルを返す役割を担います。

    • c := make(Chan);: 新しいintチャネルcを作成します。
    • go func(c Chan) { ... }(c);: ここが重要です。無名関数を新しいゴルーチンとして起動しています。このゴルーチンは、無限ループfor x := 0;; x++の中で、変数xを0から開始して1ずつインクリメントし、その値をチャネルcに送信し続けます(c <- x)。これにより、ChanCreateが呼び出されるたびに、バックグラウンドでチャネルに整数値を供給し続ける独立した処理が開始されます。
    • return c;: 作成されたチャネルcを返します。
  3. func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request): このメソッドは、Chan型がhttp.Handlerインターフェースを満たすために実装されています。Goのインターフェースは暗黙的に満たされるため、Chan型がこのシグネチャのメソッドを持つことで、自動的にhttp.Handlerとして扱えるようになります。

    • io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch));: この行がHTTPリクエストの処理の核心です。
      • <-ch: chチャネルから値を受信します。ChanCreate関数で起動されたゴルーチンがチャネルに値を送信しているため、ここで次の利用可能な整数値が取得されます。チャネルに値がない場合、この操作は値が送信されるまでブロックされます。
      • fmt.Sprintf("channel send #%d\n", ...): 受信した整数値を「channel send #N」という形式の文字列にフォーマットします。
      • io.WriteString(c, ...): フォーマットされた文字列をHTTPレスポンスとしてクライアントに書き込みます。chttp.Conn型であり、HTTPレスポンスを書き込むための接続を表します。
  4. main関数での利用: http.Handle("/chan", ChanCreate()); main関数内で、/chanというURLパスに対して、ChanCreate()の戻り値(つまり、Chan型のインスタンス)をHTTPハンドラとして登録しています。これにより、ユーザーがブラウザでhttp://localhost:12345/chanにアクセスするたびに、ChanインスタンスのServeHTTPメソッドが呼び出され、チャネルから次の整数値が取得されて表示されます。

このコードは、Goのチャネルが単なるデータ転送だけでなく、HTTPリクエストのような外部イベントと内部の並行処理ロジックを連携させるための強力なメカニズムとして機能することを示しています。各リクエストがチャネルからの「次のアイテム」を取得するトリガーとなり、サーバーがリクエストごとにユニークな、順序付けられた応答を生成するシンプルな方法を提供します。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびチュートリアル
  • Go言語の並行処理に関する一般的な知識
  • net/httpパッケージの基本的な使用方法に関する知識
  • Go言語のインターフェースの概念に関する知識
  • (必要に応じて)Go言語の初期のコミット履歴や設計に関する議論

[インデックス 1625] ファイルの概要

このコミットは、Go言語の標準ライブラリであるsrc/lib/http/triv.goファイルに対する変更です。このファイルは、GoのHTTPサーバー機能の基本的な使用方法や、Goの並行処理の概念(特にチャネル)をHTTPサーバーのコンテキストでどのように活用できるかを示すための、教育的な(pedagogical)目的を持つサンプルコードが含まれていると推測されます。具体的には、HTTPリクエストを処理する際にGoのチャネルを利用する新しいハンドラの実装が追加されています。

コミット

このコミットは、Rob Pikeによって2009年2月5日に作成されました。Go言語の初期段階における、チャネルがHTTPサーバーインターフェースを満たすことができるという、教育的なデモンストレーションを目的としています。src/lib/http/triv.goファイルに18行のコードが追加され、既存のコードの削除や変更はありません。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f95a11e27f8465435dd8a69ca6da67bbc6957270

元コミット内容

    further pedagogy: a channel that satisfies the HTTP server interface
    
    R=rsc
    DELTA=18  (18 added, 0 deleted, 0 changed)
    OCL=24482
    CL=24484

変更の背景

この変更の背景には、Go言語の設計思想、特にその強力な並行処理プリミティブである「チャネル」の教育的な側面があります。Go言語は、CSP(Communicating Sequential Processes)に影響を受けた並行処理モデルを採用しており、ゴルーチン(goroutine)とチャネル(channel)を通じて、シンプルかつ効率的な並行プログラミングを可能にします。

このコミットの目的は、「チャネルがHTTPサーバーインターフェースを満たす」という概念を具体的に示すことにあります。これは、Goのインターフェースの柔軟性と、チャネルが単なるデータ転送メカニズムに留まらず、より抽象的な振る舞いを表現するための基盤となり得ることをデモンストレーションしています。HTTPサーバーのハンドラは通常、特定のインターフェース(http.Handler)を実装することで機能しますが、このコミットでは、そのインターフェースをチャネル型が満たすようにすることで、HTTPリクエストの処理をチャネル操作と結びつけるユニークな例を提供しています。これは、Goの設計者が言語の機能と哲学をどのように教育し、普及させようとしていたかを示す一例と言えるでしょう。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と、net/httpパッケージに関する知識が必要です。

  • Go言語のゴルーチン (Goroutine): Go言語における軽量なスレッドのようなものです。goキーワードを関数呼び出しの前に置くことで、その関数を新しいゴルーチンとして実行し、他の処理と並行して動作させることができます。OSのスレッドよりもはるかに軽量で、数千、数万のゴルーチンを同時に実行することが可能です。

  • Go言語のチャネル (Channel): ゴルーチン間で値を送受信するための通信メカニズムです。チャネルは、ゴルーチン間の同期と通信を安全に行うための主要な手段であり、共有メモリによる競合状態(race condition)を避けるのに役立ちます。チャネルはmake(chan Type)で作成され、ch <- valueで値を送信し、value := <-chで値を受信します。チャネルからの受信は、値が送信されるまでブロックされます。

  • net/httpパッケージ: Go言語の標準ライブラリで、HTTPクライアントとサーバーを実装するための機能を提供します。

    • http.Handlerインターフェース: HTTPリクエストを処理するためのインターフェースです。このインターフェースは、ServeHTTP(ResponseWriter, *Request)という単一のメソッドを定義しています。任意の型がこのメソッドを実装していれば、その型はhttp.Handlerインターフェースを満たしているとみなされ、HTTPリクエストハンドラとして登録できます。
    • http.Handle(pattern string, handler Handler): 指定されたパスパターン(pattern)に対するHTTPリクエストを処理するために、http.Handlerインターフェースを実装したhandlerを登録します。
    • http.ListenAndServe(addr string, handler Handler): 指定されたアドレス(addr)でHTTPサーバーを起動します。handlernilの場合、デフォルトのhttp.DefaultServeMuxが使用されます。
  • Go言語のインターフェース: Go言語におけるインターフェースは、メソッドのシグネチャの集合です。型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たしているとみなされます。Goのインターフェースは「暗黙的」であり、明示的に「このインターフェースを実装します」と宣言する必要はありません。

技術的詳細

このコミットの核心は、Goのチャネル型がhttp.Handlerインターフェースをどのように満たすかを示す点にあります。通常、HTTPハンドラは構造体や関数型として定義され、その上でServeHTTPメソッドを実装します。しかし、この例では、Chanという名前のchan int型(整数のチャネル)を定義し、そのChan型に対してServeHTTPメソッドを実装しています。

具体的には、以下のステップでこのデモンストレーションが行われています。

  1. Chan型の定義: type Chan chan int これにより、Chanint型のチャネルのエイリアスとして定義されます。

  2. ChanCreate関数の実装: func ChanCreate() Chan この関数は、新しいChanintチャネル)を作成し、そのチャネルを返すとともに、新しいゴルーチンを起動します。このゴルーチンは無限ループで、xというカウンタ変数を0からインクリメントし続け、その値をチャネルに送信し続けます(c <- x)。これにより、チャネルは常に新しい整数値を生成し続ける「プロデューサー」となります。

  3. Chan型に対するServeHTTPメソッドの実装: func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) これが最も重要な部分です。Chan型がhttp.Handlerインターフェースを満たすために、このメソッドが実装されています。このメソッドが呼び出される(つまり、/chanパスへのHTTPリクエストがあった)たびに、<-chという操作によってチャネルから値を受信します。ChanCreate関数で起動されたゴルーチンがチャネルに値を送信しているため、ここで次の利用可能な整数値が取得されます。チャネルからの受信は、値が利用可能になるまでブロックされます。受信した値は、fmt.Sprintf("channel send #%d\\n", <-ch)によって文字列にフォーマットされ、io.WriteString(c, ...)を通じてHTTPレスポンスとしてクライアントに書き込まれます。

  4. HTTPハンドラとしての登録: http.Handle("/chan", ChanCreate()) main関数内で、/chanというパスに対してChanCreate()の戻り値(つまり、Chan型のインスタンス)をハンドラとして登録しています。ChanCreate()はチャネルを生成し、そのチャネルに値を送り続けるゴルーチンを起動するため、/chanへの各リクエストは、このチャネルから次の整数値を受け取り、それを表示することになります。

このアプローチの技術的な含意は、HTTPリクエストの処理が、Goの並行処理プリミティブであるチャネルの操作と直接結びついている点です。各リクエストはチャネルからの受信操作をトリガーし、これにより、複数のリクエストが同時に発生した場合でも、チャネルの同期メカニズムによって値が順番に(または、チャネルのバッファリング設定に応じて)処理されることが保証されます。これは、Goのインターフェースの柔軟性と、並行処理をアプリケーションロジックに自然に統合できるGoの能力を示す優れた例です。

コアとなるコードの変更箇所

diff --git a/src/lib/http/triv.go b/src/lib/http/triv.go
index 136100135a..3c527310ed 100644
--- a/src/lib/http/triv.go
+++ b/src/lib/http/triv.go
@@ -45,11 +45,29 @@ func FileServer(c *http.Conn, req *http.Request) {
 	fmt.Fprintf(c, "[%d bytes]\n", n);
 }
 
+// a channel (just for the fun of it)
+type Chan chan int
+
+func ChanCreate() Chan {
+	c := make(Chan);
+	go func(c Chan) {
+		for x := 0;; x++ {
+			c <- x
+		}
+	}(c);
+	return c;
+}
+
+func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request) {
+	io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch));
+}
+
 func main() {
 	flag.Parse();
 	http.Handle("/counter", new(Counter));
 	http.Handle("/go/", http.HandlerFunc(FileServer));
 	http.Handle("/go/hello", http.HandlerFunc(HelloServer));
+	http.Handle("/chan", ChanCreate());
 	err := http.ListenAndServe(":12345", nil);
 	if err != nil {
 		panic("ListenAndServe: ", err.String())

コアとなるコードの解説

追加されたコードは以下の3つの主要な部分と、main関数でのその利用から構成されます。

  1. type Chan chan int: これは、Chanという新しい型を定義しています。この型は、Goの組み込み型であるint型のチャネル(chan int)のエイリアスです。これにより、コード内でchan intと書く代わりにChanと書くことができ、可読性が向上します。

  2. func ChanCreate() Chan: この関数は、Chan型の新しいチャネルを作成し、そのチャネルを返す役割を担います。

    • c := make(Chan);: 新しいintチャネルcを作成します。
    • go func(c Chan) { ... }(c);: ここが重要です。無名関数を新しいゴルーチンとして起動しています。このゴルーチンは、無限ループfor x := 0;; x++の中で、変数xを0から開始して1ずつインクリメントし、その値をチャネルcに送信し続けます(c <- x)。これにより、ChanCreateが呼び出されるたびに、バックグラウンドでチャネルに整数値を供給し続ける独立した処理が開始されます。
    • return c;: 作成されたチャネルcを返します。
  3. func (ch Chan) ServeHTTP(c *http.Conn, req *http.Request): このメソッドは、Chan型がhttp.Handlerインターフェースを満たすために実装されています。Goのインターフェースは暗黙的に満たされるため、Chan型がこのシグネチャのメソッドを持つことで、自動的にhttp.Handlerとして扱えるようになります。

    • io.WriteString(c, fmt.Sprintf("channel send #%d\n", <-ch));: この行がHTTPリクエストの処理の核心です。
      • <-ch: chチャネルから値を受信します。ChanCreate関数で起動されたゴルーチンがチャネルに値を送信しているため、ここで次の利用可能な整数値が取得されます。チャネルに値がない場合、この操作は値が送信されるまでブロックされます。
      • fmt.Sprintf("channel send #%d\n", ...): 受信した整数値を「channel send #N」という形式の文字列にフォーマットします。
      • io.WriteString(c, ...): フォーマットされた文字列をHTTPレスポンスとしてクライアントに書き込みます。chttp.Conn型であり、HTTPレスポンスを書き込むための接続を表します。
  4. main関数での利用: http.Handle("/chan", ChanCreate()); main関数内で、/chanというURLパスに対して、ChanCreate()の戻り値(つまり、Chan型のインスタンス)をHTTPハンドラとして登録しています。これにより、ユーザーがブラウザでhttp://localhost:12345/chanにアクセスするたびに、ChanインスタンスのServeHTTPメソッドが呼び出され、チャネルから次の整数値が取得されて表示されます。

このコードは、Goのチャネルが単なるデータ転送だけでなく、HTTPリクエストのような外部イベントと内部の並行処理ロジックを連携させるための強力なメカニズムとして機能することを示しています。各リクエストがチャネルからの「次のアイテム」を取得するトリガーとなり、サーバーがリクエストごとにユニークな、順序付けられた応答を生成するシンプルな方法を提供します。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメントおよびチュートリアル
  • Go言語の並行処理に関する一般的な知識
  • net/httpパッケージの基本的な使用方法に関する知識
  • Go言語のインターフェースの概念に関する知識
  • Go言語の初期のコミット履歴や設計に関する議論
  • Web検索: "Go language early http server examples channels"