RPC定义
RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。简单的说就是要像调用本地函数一样调用服务器的函数。
RPC协议构建于TCP或UDP,或者是 HTTP之上,允许开发者直接调用另一台计算机上的程序,而开发者无需额外地为这个调用过程编写网络通信相关代码,使得开发包括网络分布式程序在内的应用程序更加容易
1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程
6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据
http版本的RPC版
o语言的RPC包的路径为net/rpc,也就是放在了net包目录下面。因此我们可以猜测该RPC包是建立在net包基础之上的
我们先构造一个HelloService类型,其中的Hello方法用于实现打印功能:
其中Hello方法必须满足Go语言的RPC规则:
1、方法只能有两个可序列化的参数
2、其中第二个参数是指针类型
3、并且返回一个error类型
4、同时必须是公开的方法。
type HelloService struct {}
func (p *HelloService) Hello(request string, reply *string) error {
*reply = "hello:" + request
return nil
}
然后就可以将HelloService类型的对象注册为一个RPC服务
func main() {
rpc.RegisterName("HelloService", new(HelloService))
listener, err := net.Listen("tcp", ":1234")
if err != nil {
log.Fatal("ListenTCP error:", err)
}
conn, err := listener.Accept()
if err != nil {
log.Fatal("Accept error:", err)
}
rpc.ServeConn(conn)
}
其中rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,所有注册的方法会放在“HelloService”服务空间之下。然后我们建立一个唯一的TCP链接,并且通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务。
下面是客户端请求HelloService服务的代码:
func main() {
client, err := rpc.Dial("tcp", "localhost:1234")
if err != nil {
log.Fatal("dialing:", err)
}
var reply string
err = client.Call("HelloService.Hello", "hello", &reply)
if err != nil {
log.Fatal(err)
}
fmt.Println(reply)
}
首选是通过rpc.Dial拨号RPC服务,然后通过client.Call调用具体的RPC方法。在调用client.Call时,第一个参数是用点号链接的RPC服务名字和方法名字,第二和第三个参数分别我们定义RPC方法的两个参数。
最后输出结果如下:
hello:hello
jsonrpc版本的RPC版
jsonrpc方式是数据编码采用了json,而不是gob编码。
服务端:
package main;
import (
"net"
"log"
"net/rpc"
"net/rpc/jsonrpc"
)
//注意字段必须是导出
type Params struct {
Width, Height int;
}
type Rect struct{}
func (r *Rect) Area(p Params, ret *int) error {
*ret = p.Width * p.Height;
return nil;
}
func (r *Rect) Perimeter(p Params, ret *int) error {
*ret = (p.Width + p.Height) * 2;
return nil;
}
func chkError(err error) {
if err != nil {
log.Fatal(err);
}
}
func main() {
rect := new(Rect);
//注册rpc服务
rpc.Register(rect);
//获取tcpaddr
tcpaddr, err := net.ResolveTCPAddr("tcp4", "127.0.0.1:8080");
chkError(err);
//监听端口
tcplisten, err2 := net.ListenTCP("tcp", tcpaddr);
chkError(err2);
for {
conn, err3 := tcplisten.Accept();
if err3 != nil {
continue;
}
//使用goroutine单独处理rpc连接请求
//这里使用jsonrpc进行处理
go jsonrpc.ServeConn(conn);
}
}
客户端:
package main;
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
//注意字段必须是导出
type Params struct {
Width, Height int;
}
func main() {
//连接远程rpc服务
//这里使用jsonrpc.Dial
rpc, err := jsonrpc.Dial("tcp", "127.0.0.1:8080");
if err != nil {
log.Fatal(err);
}
ret := 0;
//调用远程方法
//注意第三个参数是指针类型
err2 := rpc.Call("Rect.Area", Params{50, 100}, &ret);
if err2 != nil {
log.Fatal(err2);
}
fmt.Println(ret);
err3 := rpc.Call("Rect.Perimeter", Params{50, 100}, &ret);
if err3 != nil {
log.Fatal(err3);
}
fmt.Println(ret);
}
客户端输出如下:
5000
300