import "rpc"

rpcパッケージは、オブジェクトのエクスポートされているメソッドに対し、ネットワークなどのI/Oコネクションを経由したアクセスを提供します。サーバ側で登録を行うと、そのオブジェクトのエクスポートされているメソッドはリモートアクセスできるようになります。サーバ側は、異なる型であればオブジェクトを複数登録することができますが、同じ型のオブジェクトを複数登録しようとするとエラーになります。

下の条件を満たしたメソッドだけがリモートアクセスに使えます。これらの条件に該当しないメソッドは認められません。

- メソッドのレシーバと名前がエクスポートされている、すなわち大文字で始まっていること。
- メソッドは引数を2つ持ち、その2つともエクスポートされている型へのポインタであること。
- メソッドの戻り値の型が、os.Errorであること。

メソッドの最初の引数は、呼び出し側から提供された引数を受け取る構造体で、2番目の引数は、呼び出し側に返す結果パラメータです。メソッドの戻り値がnilでないときは、os.ErrorStringを使って、返された文字列をクライアント側で参照できます。

サーバ側は、接続に対してServeConnを呼び出してリクエストを処理します。より一般的な方法は、ネットワークリスナを作成してからAcceptを呼び出す、もしくはHTTPリスナのときは、HandleHTTPとhttp.Serveを使います。

サービスを利用しようとするクライアントは、接続を確立してから、その接続を使ってNewClientを実行します。コンビニエンス関数Dialを使用すれば、この両方のステップを一度に実行できます。(HTTP接続には、DialHTTPを使用します。) 返されるClientオブジェクトは、CallとGoという2つのメソッドを持っており、これらメソッドには次の3つの引数、呼び出すサービスとメソッド名、受け渡すデータを格納したポインタ、戻り値を受け取るポインタを指定します。

Callメソッドは、リモート呼び出しの完了を待ちます。もうひとつのGoメソッドは、非同期に呼び出しを開始し、完成通知を受け取れるチャネルを返します。

データのやり取りには、”gob”パッケージが使用されます。

ここに簡単な例があります。サーバはArith型のオブジェクトをエクスポートします。

package server

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) os.Error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) os.Error {
	if args.B == 0 {
		return os.ErrorString("divide by zero")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

サーバの呼び出し(HTTPサービスとして):

arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
	log.Exit("listen error:", e)
}
go http.Serve(l, nil)

この時点で、クライアント側は、サービス「Arith」とそのメソッド「Arith.Multiply」、「Arith.Divide」にアクセス出来るようになります。クライアントがこれらを呼び出すにはまず、このサーバへ接続します。

client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
	log.Exit("dialing:", err)
}

この後、次の方法でリモート呼び出しが可能になります。

// 同期呼び出し
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
	log.Exit("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, *reply)

もしくは、次のように非同期で呼び出しをします。

// 非同期呼び出し
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, &quotient, nil)
replyCall := <-divCall.Done	// divCallと同じ値を受け取る
// エラーチェック、出力など

サーバの実装は、しばしば型セーフなラッパーをクライアントに提供します。

パッケージファイル

client.go debug.go server.go

定数

const (
    // Defaultは、HandleHTTPで使用されている
    DefaultRPCPath   = "/_goRPC_"
    DefaultDebugPath = "/debug/rpc"
)

変数

DefaultServerは、*Serverのデフォルトのインスタンス。

var DefaultServer = NewServer()

Accept関数

func Accept(lis net.Listener)

Acceptは、リスナ上の接続を受け付け、受け付けた接続からのDefaultServerへのリクエストを処理します。Acceptの呼び出しはブロックされるので、通常goステートメントを使って呼び出します。

HandleHTTP関数

func HandleHTTP()

HandleHTTPは、DefaultRPCPathにDefaultServerへのRPCメッセージ用HTTPハンドラと、DefaultDebugPathにデバック用ハンドラを登録します。このあと、http.Serve()を実行(通常、goステートメントを使って)する必要があります。

Register関数

func Register(rcvr interface{}) os.Error

Registerは、レシーバのメソッドをDefaultServer上に公開します。

RegisterName関数

func RegisterName(name string, rcvr interface{}) os.Error

RegisterNameは、Registerとほぼ同じですが、レシーバの実際の型の代わりに、指定した名前を使用します。

ServeCodec関数

func ServeCodec(codec ServerCodec)

ServeCodecは、ServeConnとほぼ同じですが、リクエストのデコードおよびレスポンスのエンコードに指定したコーディックを使用します。

ServeConn関数

func ServeConn(conn io.ReadWriteCloser)

ServeConnは、この接続上でDefaultServerを実行します。この関数はクライアントが接続を終了するまでサービスを提供し続け、その間ブロックし続けます。そのため呼び出し側は通常、ServeConnをgoステートメントで実行します。ServeConnは、gobの電文フォーマットを使って通信を行います。代替コーディックを使うときは、ServeCodecを使用してください。

Call型

Callは、アクティブなRPCを表します。

type Call struct {
    ServiceMethod string      // 呼び出す、サービスとメソッドの名前。
    Args          interface{} // 関数の引数。(*struct)
    Reply         interface{} // 関数からの応答。(*struct)
    Error         os.Error    // 実行後のエラーステータス。
    Done          chan *Call  // 呼び出し終了トリガ。値はエラーステータス。
    // contains unexported fields
}

Client型

Clientは、RPCのクライアントを表します。ひとつのクライアントを使って複数の同時呼び出しが行えます。

type Client struct {
    // contains unexported fields
}

Dial関数

func Dial(network, address string) (*Client, os.Error)

Dialは、指定したネットワークアドレスのRPCサーバへ接続します。

DialHTTP関数

func DialHTTP(network, address string) (*Client, os.Error)

DialHTTPは、指定したネットワークアドレス(デフォルトのHTTP RPCパスでリッスンしている)のHTTP RPCサーバへ接続します。

DialHTTPPath関数

func DialHTTPPath(network, address, path string) (*Client, os.Error)

DialHTTPPathは、指定したネットワークアドレスとパスでHTTP RPCサーバへ接続します。

NewClient関数

func NewClient(conn io.ReadWriteCloser) *Client

NewClientは、接続先のサービスへリクエストを行う、新しいClientを返します。

NewClientWithCodec関数

func NewClientWithCodec(codec ClientCodec) *Client

NewClientWithCodecは、NewClientとほぼ同じですが、リクエストのエンコードおよびレスポンスのデコードに指定したコーディックを使用します。

(*Client) Call関数

func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) os.Error

Callは、指定した名前を持つ関数を呼び出し、完了を待ちます。この関数はエラーステータスを返します。

(*Client) Close関数

func (client *Client) Close() os.Error

(*Client) Go関数

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call

Goは、非同期で関数を実行します。この関数は実行を行っているCall構造体を返します。doneチャネルは、同じCallオブジェクトを返すことで呼び出しが完了したことを通知します。doneがnilのときは、Go関数は新しいチャネルを割り当てます。doneに指定したチャネルはバッファリングされている必要があり、もしバッファリングされていなければ故意にクラッシュします。

ClientCodec型

ClientCodecは、クライアント側のRPCセッションにおける、RPCリクエストの書き込みとRPCレスポンスの読み込みを実装しています。クライアントがWriteRequestを呼び出すと、リクエストを接続に書き込み、ReadResponseHeaderとReadResponseBodyを対で呼び出してレスポンスを読み込みます。クライアント側は接続の終了後、Closeを呼び出してください。

type ClientCodec interface {
    WriteRequest(*Request, interface{}) os.Error
    ReadResponseHeader(*Response) os.Error
    ReadResponseBody(interface{}) os.Error

    Close() os.Error
}

ClientInfo型

ClientInfoは、RPCクライアント接続についての情報を記録します。

type ClientInfo struct {
    LocalAddr  string
    RemoteAddr string
}

InvalidRequest型

サーバが不正なリクエストを受信したときに、レスポンスの代わりに送信される値です。

type InvalidRequest struct {
    // contains unexported fields
}

Request型

Requestは、RPCの各呼び出しの前に書き込まれるヘッダです。これは内部で使用するのですが、ネットワークトラフィックの分析といったデバッグの手助けになるよう、ここに文書化しています。

type Request struct {
    ServiceMethod string // format: "Service.Method"
    Seq           uint64 // sequence number chosen by client
}

Response型

Responseは、RPCの各応答の前に書き込まれるヘッダです。これは内部で使用するのですが、ネットワークトラフィックの分析といったデバッグの手助けになるよう、ここに文書化しています。

type Response struct {
    ServiceMethod string // echoes that of the Request
    Seq           uint64 // echoes that of the request
    Error         string // error, if any.
}

Server型

Serverは、RPCサーバを表します。

type Server struct {
    sync.Mutex // serviceMapの保護
    // contains unexported fields
}

NewServer関数

func NewServer() *Server

NewServerは、新しいServerを返します。

(*Server) Accept関数

func (server *Server) Accept(lis net.Listener)

Acceptは、リスナ上の接続を受け付け、受け付けた接続からのDefaultServerへのリクエストを処理します。Acceptの呼び出しはブロックされるので、通常goステートメントを使って呼び出します。

(*Server) HandleHTTP関数

func (server *Server) HandleHTTP(rpcPath, debugPath string)

HandleHTTPは、rpcPathにRPCメッセージ用HTTPハンドラと、debugPathにデバック用ハンドラを登録します。このあと、http.Serve()を実行(通常、goステートメントを使って)する必要があります。

(*Server) Register関数

func (server *Server) Register(rcvr interface{}) os.Error

Registerは、rcvrの値が持つメソッド群が下の条件を満たすときに、サーバ上で公開します。

- エクスポートされているメソッド
- 2つの引数を持ち、その両方ともエクスポートされている構造体へのポインタ
- 戻り値はひとつで、os.Error型

レシーバがエクスポートされている型でないか、条件を満たすメソッドを持っていないときはエラーが返されます。クライアントは、次の形式の文字列「Type.Method」を使って各メソッドにアクセスします。このTypeはレシーバの実際の型です。

(*Server) RegisterName関数

func (server *Server) RegisterName(name string, rcvr interface{}) os.Error

RegisterNameは、Registerとほぼ同じですが、レシーバの実際の型の代わりに、指定した名前を使用します。

(*Server) ServeCodec関数

func (server *Server) ServeCodec(codec ServerCodec)

ServeCodecは、ServeConnとほぼ同じですが、リクエストのデコードおよびレスポンスのエンコードに指定したコーディックを使用します。

(*Server) ServeConn関数

func (server *Server) ServeConn(conn io.ReadWriteCloser)

ServeConnは、この接続上でサーバを実行します。この関数はクライアントが接続を終了するまでサービスを提供し続け、その間ブロックし続けます。そのため呼び出し側は通常、ServeConnをgoステートメントで実行します。ServeConnは、gobの電文フォーマット (gobパッケージを参照)を使って通信を行います。代替コーディックを使うときは、ServeCodecを使用してください。

(*Server) ServeHTTP関数

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTPは、http.Handlerの実装で、RPCリクエストに応答します。

ServerCodec型

ServerCodecは、サーバ側のRPCセッションにおける、RPCリクエストの読み込みとRPCレスポンスの書き込みを実装しています。サーバはReadRequestHeaderとReadRequestHeaderを対で呼び出して接続からリクエストを読み込み、WriteResponseを呼び出してレスポンスを書き戻します。サーバ側は接続の終了後、Closeを呼び出してください。

type ServerCodec interface {
    ReadRequestHeader(*Request) os.Error
    ReadRequestBody(interface{}) os.Error
    WriteResponse(*Response, interface{}) os.Error

    Close() os.Error
}