github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/net/rpc/server.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package rpcは、オブジェクトのエクスポートされたメソッドに、ネットワークやその他のI/O接続を通じてアクセスする機能を提供します。サーバーはオブジェクトを登録し、オブジェクトのタイプ名に基づいてサービスとして表示されるようにします。登録後、オブジェクトのエクスポートされたメソッドはリモートからアクセス可能になります。サーバーは、異なるタイプの複数のオブジェクト(サービス)を登録することができますが、同じタイプの複数のオブジェクトを登録することはエラーです。 7 8 以下の条件を満たすメソッドのみがリモートアクセス可能になります。それ以外のメソッドは無視されます: 9 10 - メソッドの型がエクスポートされていること。 11 - メソッドがエクスポートされていること。 12 - メソッドが2つの引数を持ち、両方の引数がエクスポートされている(または組み込み)型であること。 13 - メソッドの2番目の引数がポインタであること。 14 - メソッドが戻り値としてerror型を持つこと。 15 16 要するに、メソッドは次のようなスキーマである必要があります。 17 18 func (t *T) MethodName(argType T1, replyType *T2) error 19 20 ここで、T1とT2はencoding/gobでマーシャリングできる型です。 21 これらの要件は、異なるコーデックが使用されている場合でも適用されます。 22 (将来的には、カスタムコーデックに対してこれらの要件は緩和されるかもしれません。) 23 24 メソッドの最初の引数は呼び出し元から提供される引数を表し、 25 2番目の引数は呼び出し元に返される結果パラメータを表します。 26 メソッドの戻り値がnilでない場合、それはクライアントが [errors.New] によって作成されたかのようにクライアントが確認する文字列として送り返されます。 27 エラーが返された場合、応答パラメータはクライアントに送り返されません。 28 29 サーバーは、[ServeConn] を呼び出すことによって単一の接続上のリクエストを処理することができます。また、通常はネットワークリスナーを作成し、[Accept] を呼び出すか、HTTPリスナーの場合は [HandleHTTP] と [http.Serve] を呼び出します。 30 31 サービスを使用するためには、クライアントは接続を確立し、その後、接続上で [NewClient] を呼び出します。[Dial]([DialHTTP])という便利な関数は、生のネットワーク接続(HTTP接続)に対して両方の手順を実行します。結果として得られる [Client] オブジェクトには、サービスとメソッドを指定するための2つのメソッド、[Call] とGoがあり、引数を含むポインタと結果パラメータを受け取るポインタを指定します。 32 33 Callメソッドは、リモート呼び出しが完了するまで待機し、 34 Goメソッドは非同期に呼び出しを開始し、Call構造体のDoneチャネルを使用して完了をシグナルします。 35 36 明示的なコーデックが設定されていない場合、データの転送には [encoding/gob] パッケージが使用されます。 37 38 以下にシンプルな例を示します。サーバーはArithタイプのオブジェクトをエクスポートしたい場合です。 39 40 package server 41 42 import "errors" 43 44 type Args struct { 45 A, B int 46 } 47 48 type Quotient struct { 49 Quo, Rem int 50 } 51 52 type Arith int 53 54 func (t *Arith) Multiply(args *Args, reply *int) error { 55 *reply = args.A * args.B 56 return nil 57 } 58 59 func (t *Arith) Divide(args *Args, quo *Quotient) error { 60 if args.B == 0 { 61 return errors.New("divide by zero") 62 } 63 quo.Quo = args.A / args.B 64 quo.Rem = args.A % args.B 65 return nil 66 } 67 68 サーバーの呼び出し(HTTPサービスの場合): 69 70 arith := new(Arith) 71 rpc.Register(arith) 72 rpc.HandleHTTP() 73 l, err := net.Listen("tcp", ":1234") 74 if err != nil { 75 log.Fatal("listen error:", err) 76 } 77 go http.Serve(l, nil) 78 79 この時点で、クライアントは"Arith"というサービスとそのメソッド"Arith.Multiply"、"Arith.Divide"を見ることができます。呼び出すためには、クライアントはまずサーバーにダイヤルします。 80 81 client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") 82 if err != nil { 83 log.Fatal("dialing:", err) 84 } 85 86 そして、リモート呼び出しを行うことができます。 87 88 // 同期呼び出し 89 args := &server.Args{7,8} 90 var reply int 91 err = client.Call("Arith.Multiply", args, &reply) 92 if err != nil { 93 log.Fatal("arith error:", err) 94 } 95 fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply) 96 97 または 98 99 // 非同期呼び出し 100 quotient := new(Quotient) 101 divCall := client.Go("Arith.Divide", args, quotient, nil) 102 replyCall := <-divCall.Done // divCallと等しい 103 // エラーをチェックし、出力などを行います。 104 105 サーバーの実装では、クライアントのためのシンプルで型セーフなラッパーを提供することがよくあります。 106 107 net/rpcパッケージは凍結されており、新しい機能は受け付けていません。 108 */ 109 package rpc 110 111 import ( 112 "github.com/shogo82148/std/io" 113 "github.com/shogo82148/std/net" 114 "github.com/shogo82148/std/net/http" 115 "github.com/shogo82148/std/sync" 116 ) 117 118 const ( 119 // HandleHTTPで使用されるデフォルト値 120 DefaultRPCPath = "/_goRPC_" 121 DefaultDebugPath = "/debug/rpc" 122 ) 123 124 // RequestはRPC呼び出しの前に書かれるヘッダーです。内部で使用されますが、ネットワークトラフィックを分析する際などデバッグの支援のためにここで記述されています。 125 type Request struct { 126 ServiceMethod string 127 Seq uint64 128 next *Request 129 } 130 131 // Responseは、すべてのRPCの戻り値の前に書かれるヘッダです。内部で使用されますが、ネットワークトラフィックを分析する際など、デバッグの支援としてここで文書化されています。 132 type Response struct { 133 ServiceMethod string 134 Seq uint64 135 Error string 136 next *Response 137 } 138 139 // ServerはRPCサーバーを表します。 140 type Server struct { 141 serviceMap sync.Map 142 reqLock sync.Mutex 143 freeReq *Request 144 respLock sync.Mutex 145 freeResp *Response 146 } 147 148 // NewServerは新しい [Server] を返します。 149 func NewServer() *Server 150 151 // DefaultServerは [*Server] のデフォルトインスタンスです。 152 var DefaultServer = NewServer() 153 154 // Registerは、以下の条件を満たすレシーバーのメソッドのセットをサーバーに公開します: 155 // - エクスポートされた型のエクスポートされたメソッド 156 // - 2つの引数、両方がエクスポートされた型 157 // - 2番目の引数がポインタであること 158 // - エラー型の1つの戻り値 159 // 160 // レシーバーがエクスポートされた型でないか、適切なメソッドがない場合は、エラーを返します。また、エラーをパッケージlogを使用してログに記録します。 161 // クライアントは "Type.Method" の形式の文字列を使用して各メソッドにアクセスします。ここで、Typeはレシーバーの具体的な型です。 162 func (server *Server) Register(rcvr any) error 163 164 // RegisterNameは [Register] と同様ですが、レシーバーの具体的な型の代わりに提供された名前を型に使用します。 165 func (server *Server) RegisterName(name string, rcvr any) error 166 167 // ServeConnは、単一の接続上でサーバーを実行します。 168 // ServeConnは、クライアントが切断するまで接続を提供するためにブロックします。 169 // 呼び出し元は通常、goステートメントでServeConnを呼び出します。 170 // ServeConnは、接続上でgobワイヤーフォーマット(パッケージgobを参照)を使用します。 171 // 代替のコーデックを使用するには、[ServeCodec] を使用します。 172 // 並行アクセスに関する情報については、[NewClient] のコメントを参照してください。 173 func (server *Server) ServeConn(conn io.ReadWriteCloser) 174 175 // ServeCodecは [ServeConn] と同様ですが、指定されたコーデックを使用して 176 // リクエストをデコードし、レスポンスをエンコードします。 177 func (server *Server) ServeCodec(codec ServerCodec) 178 179 // ServeRequestは [ServeCodec] と似ていますが、単一のリクエストを同期的に提供します。 180 // 完了時にコーデックを閉じることはありません。 181 func (server *Server) ServeRequest(codec ServerCodec) error 182 183 // Acceptはリスナー上で接続を受け入れ、各受信接続のリクエストを処理します。 184 // Acceptはリスナーがnon-nilのエラーを返すまでブロックされます。通常、呼び出し元はgoステートメントでAcceptを呼び出します。 185 func (server *Server) Accept(lis net.Listener) 186 187 // Registerはレシーバのメソッドを [DefaultServer] に登録します。 188 func Register(rcvr any) error 189 190 // RegisterNameは、レシーバの具体的な型ではなく、与えられた名前を型として使用します。[Register] と同様の動作です。 191 func RegisterName(name string, rcvr any) error 192 193 // ServerCodecはRPCセッションのサーバー側でのRPCリクエストの読み取りとRPCレスポンスの書き込みを実装します。 194 // サーバーは [ServerCodec.ReadRequestHeader] と [ServerCodec.ReadRequestBody] をペアで呼び出して接続からリクエストを読み取り、[ServerCodec.WriteResponse] を呼び出してレスポンスを書き込みます。 195 // サーバーは接続が終了したら [ServerCodec.Close] を呼び出します。ReadRequestBodyはnilの引数で呼び出されることがあり、リクエストの本文を読み取って破棄するためのものです。 196 // 同時アクセスに関する情報については、[NewClient] のコメントを参照してください。 197 type ServerCodec interface { 198 ReadRequestHeader(*Request) error 199 ReadRequestBody(any) error 200 WriteResponse(*Response, any) error 201 202 Close() error 203 } 204 205 // ServeConnは [DefaultServer] を単一の接続上で実行します。 206 // ServeConnは、クライアントが切断するまで接続を処理するまでブロックします。 207 // 通常、呼び出し元はgo文でServeConnを呼び出します。 208 // ServeConnは、接続上でgobワイヤーフォーマット(パッケージgobを参照)を使用します。 209 // 別のコーデックを使用するには、[ServeCodec] を使用してください。 210 // 同時アクセスに関する情報については、[NewClient] のコメントを参照してください。 211 func ServeConn(conn io.ReadWriteCloser) 212 213 // ServeCodecは [ServeConn] と似ていますが、指定されたコーデックを使用して 214 // リクエストをデコードし、レスポンスをエンコードします。 215 func ServeCodec(codec ServerCodec) 216 217 // ServeRequest は [ServeCodec] に似ていますが、単一のリクエストを同期的に処理します。 218 // 処理が完了してもコーデックを閉じません。 219 func ServeRequest(codec ServerCodec) error 220 221 // Acceptはリスナー上で接続を受け付け、各受信された接続に対して [DefaultServer] にリクエストを処理します。 222 // Acceptはブロックします。通常、呼び出し元はgo文でそれを呼び出します。 223 func Accept(lis net.Listener) 224 225 // ServeHTTPはRPCリクエストに答えるための [http.Handler] を実装します。 226 func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) 227 228 // HandleHTTPはrpcPathでRPCメッセージのためのHTTPハンドラを登録し、debugPathではデバッグハンドラを登録します。 229 // 通常はGoステートメント内で [http.Serve]()を呼び出す必要があります。 230 func (server *Server) HandleHTTP(rpcPath, debugPath string) 231 232 // HandleHTTPはRPCメッセージのためのHTTPハンドラを [DefaultServer] に登録し、[DefaultRPCPath] にデバッグハンドラを登録します。 233 // 通常はgoステートメントで [http.Serve]()を呼び出す必要があります。 234 func HandleHTTP()