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

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

このコミットは、Go言語の標準ライブラリ net/rpc パッケージにおけるAPIの変更とドキュメンテーションの改善に焦点を当てています。特に、RPCサービスとして公開されるメソッドの要件に関する説明が明確化され、内部的にのみ使用される型が隠蔽されています。

コミット

commit 250fa82122e1eca680c53f99e79dd08794001cc8
Author: Rob Pike <r@golang.org>
Date:   Wed Feb 29 07:34:28 2012 +1100

    net/rpc: API changes, all documentation
    except for hiding one type that is only used internally.
    
    Fixes #2944.
    
    R=golang-dev, rsc, kevlar
    CC=golang-dev
    https://golang.org/cl/5707044

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

https://github.com/golang/go/commit/250fa82122e1eca680c53f99e79dd08794001cc8

元コミット内容

このコミットの元のメッセージは以下の通りです。

"net/rpc: API changes, all documentation except for hiding one type that is only used internally. Fixes #2944."

これは、net/rpc パッケージのAPI変更と、内部使用のみの型を隠蔽することを除くすべてのドキュメンテーション更新が目的であることを示しています。また、Issue #2944 を修正するものであることも明記されています。

変更の背景

このコミットの主な背景は、net/rpc パッケージの使いやすさと堅牢性を向上させることにあります。特に、RPCサービスとしてメソッドを公開するためのルールが不明瞭であったり、誤解を招く可能性があったため、そのドキュメンテーションを明確にすることが求められていました。

具体的には、以下の点が背景として考えられます。

  1. メソッド公開要件の明確化: net/rpc は、特定のシグネチャを持つメソッドのみをRPCとして公開します。しかし、その要件(エクスポートされていること、引数の型、戻り値の型など)に関する説明が不十分であったため、開発者がRPCサービスを実装する際に混乱が生じる可能性がありました。このコミットは、これらの要件をより簡潔かつ正確に記述することで、開発者の理解を助けることを目指しています。
  2. 内部型の隠蔽: InvalidRequest のような内部的にのみ使用される型が外部に公開されていると、APIの複雑性を増し、誤用されるリスクがありました。これを匿名構造体に変更することで、内部実装の詳細を隠蔽し、APIのクリーンさを保つことが目的です。
  3. encoding/gob の役割の明確化: net/rpc はデフォルトで encoding/gob を使用してデータのシリアライズ/デシリアライズを行います。このコミットでは、その役割と、将来的にカスタムコーデックがサポートされる可能性について言及することで、パッケージの柔軟性と拡張性に関する情報を提供しています。
  4. 非同期呼び出しの明確化: Call メソッドと Go メソッドの動作の違い、特に非同期呼び出しのメカニズムに関する説明が追加され、より正確な理解を促しています。

これらの変更は、Go言語のRPCフレームワークをより使いやすく、理解しやすいものにするための継続的な改善の一環として行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびRPCに関する基本的な知識が必要です。

  1. Go言語のパッケージとエクスポート:

    • Go言語では、識別子(変数名、関数名、型名など)が大文字で始まる場合、その識別子はパッケージ外からアクセス可能(エクスポートされている)になります。小文字で始まる場合は、パッケージ内でのみアクセス可能です。
    • net/rpc では、RPCとして公開されるメソッドやその引数・戻り値の型がエクスポートされている必要があります。これは、リモートからの呼び出しを可能にするための基本的な要件です。
  2. Go言語のメソッド:

    • Go言語のメソッドは、特定の型に関連付けられた関数です。レシーバ(func (t *T) MethodName(...)t *T 部分)を持ちます。
    • net/rpc では、レシーバがポインタ型であるメソッドが一般的です。
  3. Go言語の error インターフェース:

    • Go言語では、エラー処理に error インターフェースが広く使用されます。関数やメソッドがエラーを返す場合、通常は最後の戻り値として error 型を返します。
    • net/rpc のメソッドは、RPC呼び出しの結果としてエラーをクライアントに伝えるために、error 型を戻り値として持つ必要があります。
  4. Go言語の encoding/gob パッケージ:

    • encoding/gob は、Go言語のデータ構造をバイナリ形式でエンコード/デコードするためのパッケージです。Goの型システムと密接に連携しており、構造体、スライス、マップなどのGoの値を効率的にシリアライズできます。
    • net/rpc は、デフォルトでこの gob パッケージを使用して、RPC呼び出しの引数と戻り値をネットワーク経由で転送します。gob でエンコード/デコードできる型である必要があります。
  5. RPC (Remote Procedure Call):

    • RPCは、ネットワーク上の別のコンピュータにあるプログラムのサブルーチンやプロシージャを、あたかもローカルにあるかのように呼び出すための技術です。
    • クライアントはリモートの関数を呼び出し、その結果を受け取ります。この際、データのシリアライズ/デシリアライズ、ネットワーク通信、エラーハンドリングなどがRPCフレームワークによって抽象化されます。
    • net/rpc は、Go言語でRPCサービスを簡単に構築するための標準ライブラリです。
  6. Go言語の reflect パッケージ:

    • reflect パッケージは、実行時にGoのプログラムの構造を検査(リフレクション)するための機能を提供します。
    • net/rpc のようなフレームワークは、reflect パッケージを使用して、登録されたサービスのメソッドのシグネチャを動的に検査し、RPCの要件を満たしているかを確認します。このコミットのコード変更部分 (server.goregister メソッド内) で reflect が使用されていることがわかります。

これらの知識があることで、コミットで行われた変更の意図と、それが net/rpc パッケージの動作にどのように影響するかを深く理解することができます。

技術的詳細

このコミットは、net/rpc パッケージの server.go ファイルに以下の技術的な変更を加えています。

  1. RPCメソッドの要件の明確化 (ドキュメンテーション):

    • 以前のドキュメンテーションでは、RPCメソッドの要件がやや曖昧でした。特に、引数と戻り値の型について「exported or local types」と記述されていましたが、これは「exported (or builtin) types」に修正されました。これにより、組み込み型(int, string など)もRPC引数として使用できることが明確になります。
    • RPCメソッドの典型的なシグネチャとして func (t *T) MethodName(argType T1, replyType *T2) error が明示的に追加されました。これは、開発者がRPCメソッドを実装する際のガイドラインとなります。
    • encoding/gob がデフォルトのトランスポートメカニズムであること、および将来的にカスタムコーデックがサポートされる可能性が追記されました。
    • Call メソッドと Go メソッドの非同期動作に関する説明がより詳細になりました。Call は完了を待機するのに対し、Go は非同期で呼び出しを開始し、Call 構造体の Done チャネルを通じて完了を通知することが明確にされています。
  2. register メソッド内の型チェックの修正:

    • server.goregister メソッドは、サービスに登録されるメソッドがRPCの要件を満たしているかを動的にチェックします。
    • 以前は isExportedOrBuiltinType 関数が引数や戻り値の型をチェックする際に、ログメッセージで「argument type not exported or local」や「reply type not exported or local」と出力していました。このコミットでは、ドキュメンテーションの変更に合わせて、これらのメッセージが「argument type not exported」や「reply type not exported」に修正されました。これは、local という概念がRPCの文脈では適切でないか、あるいは exported に包含されるため、より簡潔な表現に統一されたことを示唆しています。
    • replyType がエクスポートされているかどうかのチェックが明示的にコメントで追加されました (// Reply type must be exported.)。
    • メソッドの戻り値の数が1であること、およびその戻り値が error 型であることのチェックに関するコメントも追加され、コードの意図がより明確になりました。
  3. InvalidRequest 型の隠蔽:

    • 以前は InvalidRequest という名前付きの空の構造体 type InvalidRequest struct{} が定義され、そのインスタンス var invalidRequest = InvalidRequest{} が使用されていました。
    • このコミットでは、InvalidRequest 型の定義が削除され、invalidRequest 変数が直接匿名構造体 struct{}{} のインスタンスとして初期化されるように変更されました。
    • 関連するコメントも「A value sent as a placeholder for the server's response value when the server receives an invalid request. It is never decoded by the client since the Response contains an error when it is used.」と変更され、この変数の目的がより詳細に説明されています。
    • この変更により、InvalidRequest という型が外部から参照できなくなり、内部実装の詳細が隠蔽されます。これは、APIのクリーンさを保ち、誤用を防ぐための典型的なプラクティスです。

これらの技術的な変更は、net/rpc パッケージのAPIの明確性、堅牢性、および内部実装の隠蔽を向上させることを目的としています。

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

変更は src/pkg/net/rpc/server.go ファイルに集中しています。

--- a/src/pkg/net/rpc/server.go
+++ b/src/pkg/net/rpc/server.go
@@ -13,13 +13,19 @@
 	Only methods that satisfy these criteria will be made available for remote access;
 	other methods will be ignored:
 
-		- the method name is exported, that is, begins with an upper case letter.
-		- the method receiver is exported or local (defined in the package
-		  registering the service).
-		- the method has two arguments, both exported or local types.
+		- the method is exported.
+		- the method has two arguments, both exported (or builtin) types.
 		- the method's second argument is a pointer.
 		- the method has return type error.
 
+	In effect, the method must look schematically like
+
+		func (t *T) MethodName(argType T1, replyType *T2) error
+
+	where T, T1 and T2 can be marshaled by encoding/gob.
+	These requirements apply even if a different codec is used.
+	(In future, these requirements may soften for custom codecs.)
+
 	The method's first argument represents the arguments provided by the caller; the
 	second argument represents the result parameters to be returned to the caller.
 	The method's return value, if non-nil, is passed back as a string that the client
@@ -36,10 +42,12 @@
 	call, a pointer containing the arguments, and a pointer to receive the result
 	parameters.
 
-	Call waits for the remote call to complete; Go launches the call asynchronously
-	and returns a channel that will signal completion.
+	The Call method waits for the remote call to complete while the Go method
+	launches the call asynchronously and signals completion using the Call
+	structure's Done channel.
 
-	Package "gob" is used to transport the data.
+	Unless an explicit codec is set up, package encoding/gob is used to
+	transport the data.
 
 	Here is a simple example.  A server wishes to export an object of type Arith:
 
@@ -256,6 +264,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
 		method := s.typ.Method(m)
 		mtype := method.Type
 		mname := method.Name
+		// Method must be exported.
 		if method.PkgPath != "" {
 			continue
 		}
@@ -267,7 +276,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
 		// First arg need not be a pointer.
 		argType := mtype.In(1)
 		if !isExportedOrBuiltinType(argType) {
-			log.Println(mname, "argument type not exported or local:", argType)
+			log.Println(mname, "argument type not exported:", argType)
 			continue
 		}
 		// Second arg must be a pointer.
@@ -276,15 +285,17 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
 			log.Println("method", mname, "reply type not a pointer:", replyType)
 			continue
 		}
+		// Reply type must be exported.
 		if !isExportedOrBuiltinType(replyType) {
-			log.Println("method", mname, "reply type not exported or local:", replyType)
+			log.Println("method", mname, "reply type not exported:", replyType)
 			continue
 		}
-		// Method needs one out: error.
+		// Method needs one out.
 		if mtype.NumOut() != 1 {
 			log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
 			continue
 		}
+		// The return type of the method must be error.
 		if returnType := mtype.Out(0); returnType != typeOfError {
 			log.Println("method", mname, "returns", returnType.String(), "not error")
 			continue
@@ -301,10 +312,10 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro
 	return nil
 }
 
-// A value sent as a placeholder for the response when the server receives an invalid request.
-type InvalidRequest struct{}
-// A value sent as a placeholder for the server's response value when the server
-// receives an invalid request. It is never decoded by the client since the Response
-// contains an error when it is used.
-var invalidRequest = InvalidRequest{}
+var invalidRequest = struct{}{}

コアとなるコードの解説

このコミットのコアとなるコードの変更は、主に src/pkg/net/rpc/server.go ファイル内のドキュメンテーションと、RPCメソッドの登録ロジック、そして内部で使用される invalidRequest 変数の定義にあります。

  1. RPCメソッドの要件に関するドキュメンテーションの更新:

    • 変更前:
      -		- the method name is exported, that is, begins with an upper case letter.
      -		- the method receiver is exported or local (defined in the package
      -		  registering the service).
      -		- the method has two arguments, both exported or local types.
      
    • 変更後:
      +		- the method is exported.
      +		- the method has two arguments, both exported (or builtin) types.
      
    • この変更は、RPCメソッドの要件をより簡潔かつ正確に表現しています。特に、「exported or local types」が「exported (or builtin) types」に変更されたことで、intstring のような組み込み型もRPCの引数として使用できることが明確になりました。また、メソッド名がエクスポートされていることと、レシーバがエクスポートされていることの記述が「the method is exported」というより一般的な表現に集約されました。
  2. RPCメソッドのシグネチャ例の追加:

    • 変更後:
      +	In effect, the method must look schematically like
      +
      +		func (t *T) MethodName(argType T1, replyType *T2) error
      +
      +	where T, T1 and T2 can be marshaled by encoding/gob.
      +	These requirements apply even if a different codec is used.
      +	(In future, these requirements may soften for custom codecs.)
      
    • これは、RPCメソッドの典型的なシグネチャをコード例として示すことで、開発者がRPCサービスを実装する際の具体的なガイドラインを提供します。また、encoding/gob によるマーシャリングの必要性と、将来的なカスタムコーデックの可能性についても言及しています。
  3. CallGo メソッドの動作説明の明確化:

    • 変更前:
      -	Call waits for the remote call to complete; Go launches the call asynchronously
      -	and returns a channel that will signal completion.
      
    • 変更後:
      +	The Call method waits for the remote call to complete while the Go method
      +	launches the call asynchronously and signals completion using the Call
      +	structure's Done channel.
      
    • CallGo の非同期動作の違いがより詳細に説明され、Go メソッドが Call 構造体の Done チャネルを使用して完了を通知することが明確にされました。
  4. encoding/gob の役割の明確化:

    • 変更前:
      -	Package "gob" is used to transport the data.
      
    • 変更後:
      +	Unless an explicit codec is set up, package encoding/gob is used to
      +	transport the data.
      
    • encoding/gob がデフォルトのコーデックであることを明示し、明示的なコーデックが設定されていない場合にのみ使用されることを示しています。
  5. register メソッド内のログメッセージの修正:

    • 変更前:
      -			log.Println(mname, "argument type not exported or local:", argType)
      
    • 変更後:
      +			log.Println(mname, "argument type not exported:", argType)
      
    • 同様に replyType のチェックでも変更されています。これは、ドキュメンテーションの変更に合わせて、ログメッセージから「local」という表現を削除し、「exported」に統一することで、一貫性を保っています。
  6. InvalidRequest 型の隠蔽:

    • 変更前:
      -// A value sent as a placeholder for the response when the server receives an invalid request.
      -type InvalidRequest struct{}
      -
      -var invalidRequest = InvalidRequest{}
      
    • 変更後:
      +// A value sent as a placeholder for the server's response value when the server
      +// receives an invalid request. It is never decoded by the client since the Response
      +// contains an error when it is used.
      +var invalidRequest = struct{}{}
      
    • InvalidRequest という名前付きの型定義が削除され、invalidRequest 変数が直接匿名構造体 struct{}{} のインスタンスとして初期化されるようになりました。これにより、この型が外部から参照できなくなり、内部実装の詳細が隠蔽されます。これは、APIの利用者が内部的なプレースホルダー型に依存することを防ぎ、よりクリーンなAPI設計を促進します。

これらの変更は、net/rpc パッケージのドキュメンテーションの精度を高め、APIの利用者がより簡単にRPCサービスを実装できるようにするとともに、内部実装の詳細を適切に隠蔽することで、パッケージの保守性と堅牢性を向上させています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメンテーション
  • Go言語のソースコード (src/pkg/net/rpc/server.go)
  • GitHubのコミット履歴
  • Go言語のIssueトラッカー (Issue #2944) - このコミットメッセージに記載されている Fixes #2944 は、通常、GoのIssueトラッカー上の特定の課題を指します。
    • https://go.dev/issue/2944 (検索結果から推測)
    • このIssueは "net/rpc: document method requirements" というタイトルで、まさにこのコミットの目的と合致しています。
  • Go言語のコードレビューシステム (Gerrit) - コミットメッセージに記載されている https://golang.org/cl/5707044 は、Gerrit上の変更リストへのリンクです。
    • https://go.dev/cl/5707044 (検索結果から推測)
    • この変更リストには、コミットに至るまでの議論やレビューコメントが含まれており、変更の背景や意図を深く理解するのに役立ちます。

これらの情報源は、Go言語の net/rpc パッケージの設計思想、実装の詳細、および特定の変更がなぜ行われたのかを理解する上で不可欠です。