15.9 用 rpc 实现远程过程调用
服务器端需要注册一个对象实例,与其类型名一起,使之成为一项可见的服务:它允许远程客户端跨越网络或其他 I/O 连接访问此对象已导出的方法。总之就是在网络上暴露类型的方法。
rpc
包使用了 http 和 tcp 协议,以及用于数据传输的 gob
包。服务器端可以注册多个不同类型的对象(服务),但同一类型的多个对象会产生错误。
我们讨论一个简单的例子:定义一个类型 Args
及其方法 Multiply()
,完美地置于单独的包中。方法必须返回可能的错误。
示例15.21 rpc_objects.go
服务器端产生一个 rpc_objects.Args
类型的对象 calc
,并用 rpc.Register(object)
注册。调用 HandleHTTP()
,然后用 net.Listen
在指定的地址上启动监听。也可以按名称来注册对象,例如:rpc.RegisterName("Calculator", calc)
。
以协程启动 http.Serve(listener, nil)
后,会为每一个进入 listener
的 HTTP 连接创建新的服务线程。我们必须用诸如 time.Sleep(1000e9)
来使服务器在一段时间内保持运行状态。
示例 15.22
package main
import (
"net/http"
"log"
"net"
"net/rpc"
"time"
)
func main() {
calc := new(rpc_objects.Args)
rpc.Register(calc)
rpc.HandleHTTP()
listener, e := net.Listen("tcp", "localhost:1234")
log.Fatal("Starting RPC-server -listen error:", e)
}
go http.Serve(listener, nil)
time.Sleep(1000e9)
}
输出:
示例 15.23 rpc_client.go
package main
import (
"fmt"
"log"
"./rpc_objects"
)
const serverAddress = "localhost"
func main() {
if err != nil {
log.Fatal("Error dialing:", err)
}
// Synchronous call
args := &rpc_objects.Args{7, 8}
var reply int
err = client.Call("Args.Multiply", args, &reply)
if err != nil {
log.Fatal("Args error:", err)
}
fmt.Printf("Args: %d * %d = %d", args.N, args.M, reply)
}
先启动服务器,再运行客户端,然后就能得到如下输出结果:
该远程调用以同步方式进行,它会等待服务器返回结果。也可使用如下方式异步地执行调用:
call1 := client.Go("Args.Multiply", args, &reply, nil)
replyCall := <- call1.Done
如果最后一个参数值为 nil
,调用完成后会创建一个新的通道。
- 目录
- 上一节:
- 下一节:基于网络的通道 netchan