巷のRPCは、エンティティからスタブを作ったり面倒だったりしますが、Go言語ではクライアントとサーバの意識さえ合っていればスタブも必要ない。
まずサーバ
package main
import (
"fmt"
"net"
"net/rpc"
)
type FizzBuzz int
func (fb *FizzBuzz) Serve(n int, r *string) error {
switch {
case n % 15 == 0:
*r = "FizzBuzz"
case n % 3 == 0:
*r = "Fizz"
case n % 5 == 0:
*r = "Buzz"
default:
*r = fmt.Sprintf("%d", n)
}
return nil
}
func main(){
fb := new(FizzBuzz)
rpc.Register(fb)
server, _ := net.Listen("tcp", ":8001")
for {
client, err := server.Accept()
if err != nil {
println(err.Error())
continue
}
rpc.ServeConn(client)
}
}
Register に渡される型のメソッドで、入力引数、戻り値のアドレス の二つを引数に持ち、戻り値に error を持つ物はエクスポートされる。なのでこの場合、n が FizzBuzz の入力、r が戻り値へのアドレスになる。尚、このRPCは透過性があるので、別に TCP でなくても構わない。
次にクライアント。型がサーバと一致しているのなら、とても簡単に呼び出せる。
package main
import (
"net/rpc"
)
func main() {
client, _ := rpc.Dial("tcp", "localhost:8001")
var ret string
for i := 1; i <= 100; i++ {
client.Call("FizzBuzz.Serve", i, &ret)
println(ret)
}
}
GobというGo言語に特化したエンコーダなので、他の言語でも...という訳には行かないが、ちょっとした物を作る時にはかなり便利である。尚、Go言語には
net/rpc/jsonrpc
も含まれていて、こちらを使えばポータビリティのある RPC が書ける様になっている。