RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。
简单的说就是要像调用本地函数一样调用服务器的函数。
RPC协议构建于TCP或UDP,或者是 HTTP之上,允许开发者直接调用另一台计算机上的程序,而开发者无需额外地为这个调用过程编写网络通信相关代码,
使得开发包括网络分布式程序在内的应用程序更加容易。
语言的RPC包的路径为net/rpc,也就是放在了net包目录下面,由此我们可以推测该RPC包是建立在net包基础之上。
package main
import (
"fmt"
"log"
"net"
"net/rpc"
)
// 定义 HelloService struct {}
type HelloService struct {}
// 给 HelloService 增加 SayHello 方法
func (p HelloService) SayHello(r string, re *string) error {
*re = "service respone: " + r
return nil
}
func main () {
// 注册 HelloService 服务
rpc.RegisterName("HelloService", new(HelloService))
// 监听 port 1234 端口
listen, err := net.Listen("tcp",":1234")
if err != nil {
log.Fatal("ListenTCP error",err)
}
fmt.Println("service start ...")
// 接受 client 端 http连接请求
conn, err := listen.Accept()
if err != nil {
log.Fatal("Accept error",err)
}
rpc.ServeConn(conn)
fmt.Println("service exit..")
}
rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数,
所有注册的方法会放在“HelloService”服务空间之下。
通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务。
package main
import (
"fmt"
"log"
// "net"
"net/rpc"
)
func main() {
client, err := rpc.Dial("tcp","localhost:1234")
if err != nil {
log.Fatal("client dial error",err)
}
var reply string
err = client.Call("HelloService.SayHello","client send info ",&reply)
if err != nil {
log.Fatal("client call error",err)
}
fmt.Println("client recv -> ",reply)
}
通过rpc.Dial拨号RPC服务,然后通过client.Call调用具体的RPC方法。
在调用client.Call时,
第一个参数是用点号链接的RPC服务名字和方法名字,“HelloService.SayHello”
第二个参数是客户端发送的信息"client send info "
第三个参数接受服务端返回信息&reply
~/gomod/src/rpc$ go run serverHttpRpc.go
service start ...
service exit..
~/gomod/src/rpc$ go run clientHttpRpc.go
client recv -> service respone: client send info
net/rpc/jsonrpc库实现RPC方法,此方式实现的RPC方法支持跨语言调用.
package main
import (
"os"
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type RectRequest struct {
W int
H int
}
type Rect struct{}
// Rect 添加 Area 方法
func(this *Rect) Area(p RectRequest, res *int) error {
*res =p.W * p.H
return nil
}
// Rect 添加 Perimeter 方法
func(this *Rect) Perimeter(p RectRequest, res *int) error {
*res = (p.W + p.H) *2
return nil
}
func main () {
// 注册 rcp 对象 Rect
rpc.Register(new(Rect))
list, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal("fatal error: ", err)
}
fmt.Fprintf(os.Stdout, "%s", "start service listen\n")
var incomingCount int
incomingCount = 0
for {
conn, err := list.Accept()
if err != nil {
continue
}
incomingCount++
go func(conn net.Conn) {
fmt.Printf("new client in coming: %d \n",incomingCount)
jsonrpc.ServeConn(conn) // 执行 jsonrpc
}(conn)
}
}
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
type Params struct {
W , H int
}
func main() {
//连接远程rpc服务, jsonrpc.Dial
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal(err)
}
//调用远程方法
var ret int
params := Params{150,100}
err = conn.Call("Rect.Area", params, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println("Rect.Area res: ", ret)
err = conn.Call("Rect.Perimeter", Params{150, 100}, &ret)
if err != nil {
log.Fatal(err)
}
fmt.Println("Rect.Perimeter res: ", ret)
}
启动 服务端
robot@ubuntu:~/gomod/src/rpc$ go run serverJsonRpc.go
start service listen
new client in coming: 1
new client in coming: 2
new client in coming: 3
new client in coming: 4
启动客户端,内容如下
robot@ubuntu:~/gomod/src/rpc$ go run clientJsonRpc.go
Rect.Area res: 15000
Rect.Perimeter res: 500
robot@ubuntu:~/gomod/src/rpc$ go run clientJsonRpc.go
Rect.Area res: 15000
Rect.Perimeter res: 500
robot@ubuntu:~/gomod/src/rpc$ go run clientJsonRpc.go
Rect.Area res: 15000
Rect.Perimeter res: 500
robot@ubuntu:~/gomod/src/rpc$ go run clientJsonRpc.go
Rect.Area res: 15000
Rect.Perimeter res: 500