[インデックス 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サービスとしてメソッドを公開するためのルールが不明瞭であったり、誤解を招く可能性があったため、そのドキュメンテーションを明確にすることが求められていました。
具体的には、以下の点が背景として考えられます。
- メソッド公開要件の明確化:
net/rpc
は、特定のシグネチャを持つメソッドのみをRPCとして公開します。しかし、その要件(エクスポートされていること、引数の型、戻り値の型など)に関する説明が不十分であったため、開発者がRPCサービスを実装する際に混乱が生じる可能性がありました。このコミットは、これらの要件をより簡潔かつ正確に記述することで、開発者の理解を助けることを目指しています。 - 内部型の隠蔽:
InvalidRequest
のような内部的にのみ使用される型が外部に公開されていると、APIの複雑性を増し、誤用されるリスクがありました。これを匿名構造体に変更することで、内部実装の詳細を隠蔽し、APIのクリーンさを保つことが目的です。 encoding/gob
の役割の明確化:net/rpc
はデフォルトでencoding/gob
を使用してデータのシリアライズ/デシリアライズを行います。このコミットでは、その役割と、将来的にカスタムコーデックがサポートされる可能性について言及することで、パッケージの柔軟性と拡張性に関する情報を提供しています。- 非同期呼び出しの明確化:
Call
メソッドとGo
メソッドの動作の違い、特に非同期呼び出しのメカニズムに関する説明が追加され、より正確な理解を促しています。
これらの変更は、Go言語のRPCフレームワークをより使いやすく、理解しやすいものにするための継続的な改善の一環として行われました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびRPCに関する基本的な知識が必要です。
-
Go言語のパッケージとエクスポート:
- Go言語では、識別子(変数名、関数名、型名など)が大文字で始まる場合、その識別子はパッケージ外からアクセス可能(エクスポートされている)になります。小文字で始まる場合は、パッケージ内でのみアクセス可能です。
net/rpc
では、RPCとして公開されるメソッドやその引数・戻り値の型がエクスポートされている必要があります。これは、リモートからの呼び出しを可能にするための基本的な要件です。
-
Go言語のメソッド:
- Go言語のメソッドは、特定の型に関連付けられた関数です。レシーバ(
func (t *T) MethodName(...)
のt *T
部分)を持ちます。 net/rpc
では、レシーバがポインタ型であるメソッドが一般的です。
- Go言語のメソッドは、特定の型に関連付けられた関数です。レシーバ(
-
Go言語の
error
インターフェース:- Go言語では、エラー処理に
error
インターフェースが広く使用されます。関数やメソッドがエラーを返す場合、通常は最後の戻り値としてerror
型を返します。 net/rpc
のメソッドは、RPC呼び出しの結果としてエラーをクライアントに伝えるために、error
型を戻り値として持つ必要があります。
- Go言語では、エラー処理に
-
Go言語の
encoding/gob
パッケージ:encoding/gob
は、Go言語のデータ構造をバイナリ形式でエンコード/デコードするためのパッケージです。Goの型システムと密接に連携しており、構造体、スライス、マップなどのGoの値を効率的にシリアライズできます。net/rpc
は、デフォルトでこのgob
パッケージを使用して、RPC呼び出しの引数と戻り値をネットワーク経由で転送します。gob
でエンコード/デコードできる型である必要があります。
-
RPC (Remote Procedure Call):
- RPCは、ネットワーク上の別のコンピュータにあるプログラムのサブルーチンやプロシージャを、あたかもローカルにあるかのように呼び出すための技術です。
- クライアントはリモートの関数を呼び出し、その結果を受け取ります。この際、データのシリアライズ/デシリアライズ、ネットワーク通信、エラーハンドリングなどがRPCフレームワークによって抽象化されます。
net/rpc
は、Go言語でRPCサービスを簡単に構築するための標準ライブラリです。
-
Go言語の
reflect
パッケージ:reflect
パッケージは、実行時にGoのプログラムの構造を検査(リフレクション)するための機能を提供します。net/rpc
のようなフレームワークは、reflect
パッケージを使用して、登録されたサービスのメソッドのシグネチャを動的に検査し、RPCの要件を満たしているかを確認します。このコミットのコード変更部分 (server.go
のregister
メソッド内) でreflect
が使用されていることがわかります。
これらの知識があることで、コミットで行われた変更の意図と、それが net/rpc
パッケージの動作にどのように影響するかを深く理解することができます。
技術的詳細
このコミットは、net/rpc
パッケージの server.go
ファイルに以下の技術的な変更を加えています。
-
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
チャネルを通じて完了を通知することが明確にされています。
- 以前のドキュメンテーションでは、RPCメソッドの要件がやや曖昧でした。特に、引数と戻り値の型について「exported or local types」と記述されていましたが、これは「exported (or builtin) types」に修正されました。これにより、組み込み型(
-
register
メソッド内の型チェックの修正:server.go
のregister
メソッドは、サービスに登録されるメソッドが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
型であることのチェックに関するコメントも追加され、コードの意図がより明確になりました。
-
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
変数の定義にあります。
-
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」に変更されたことで、
int
やstring
のような組み込み型もRPCの引数として使用できることが明確になりました。また、メソッド名がエクスポートされていることと、レシーバがエクスポートされていることの記述が「the method is exported」というより一般的な表現に集約されました。
- 変更前:
-
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
によるマーシャリングの必要性と、将来的なカスタムコーデックの可能性についても言及しています。
- 変更後:
-
Call
とGo
メソッドの動作説明の明確化:- 変更前:
- 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.
Call
とGo
の非同期動作の違いがより詳細に説明され、Go
メソッドがCall
構造体のDone
チャネルを使用して完了を通知することが明確にされました。
- 変更前:
-
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
がデフォルトのコーデックであることを明示し、明示的なコーデックが設定されていない場合にのみ使用されることを示しています。
- 変更前:
-
register
メソッド内のログメッセージの修正:- 変更前:
- log.Println(mname, "argument type not exported or local:", argType)
- 変更後:
+ log.Println(mname, "argument type not exported:", argType)
- 同様に
replyType
のチェックでも変更されています。これは、ドキュメンテーションの変更に合わせて、ログメッセージから「local」という表現を削除し、「exported」に統一することで、一貫性を保っています。
- 変更前:
-
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言語
net/rpc
パッケージのドキュメンテーション: https://pkg.go.dev/net/rpc - Go言語
encoding/gob
パッケージのドキュメンテーション: https://pkg.go.dev/encoding/gob - Go言語
reflect
パッケージのドキュメンテーション: https://pkg.go.dev/reflect
参考にした情報源リンク
- 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
パッケージの設計思想、実装の詳細、および特定の変更がなぜ行われたのかを理解する上で不可欠です。