什么是RPC
远程过程调用(Remote Procedure Call,缩写为 RPC)
用通俗易懂的语言描述就是:RPC允许跨机器、跨语言调用计算机程序方法。打个比方,我用go语言写了个获取用户信息的方法getUserInfo,并把go程序部署在阿里云服务器上面,现在我有一个部署在腾讯云上面的php项目,需要调用golang的getUserInfo方法获取用户信息,php跨机器调用go方法的过程就是RPC调用。
golang中使用RPC
net/rpcnet/rpc/jsonrpc
RPC格式的函数
Go RPC的函数只有符合下面的条件才能被远程访问,不然会被忽略,详细的要求如下:
- 函数必须是导出的(首字母大写)
- 必须有两个导出类型的参数
- 第一个参数是接收的参数,第二个参数是返回给客- 户端的参数,第二个参数必须是指针类型的
- 函数还要有一个返回值error
比如:
func (t *T) MethodName(argType T1, replyType *T2) error
net/rpc库
下面的例子演示一下如何使用golang官方的net/rpc库实现RPC方法,使用http作为RPC的载体,通过net/http包监听客户端连接请求。
server.go
package main
import (
"errors"
"fmt"
"log"
"net"
"net/http"
"net/rpc"
)
// Arith 算数运算结构体
type Arith struct {
}
// ArithRequest 算数运算请求结构体
type ArithRequest struct {
A int
B int
}
// ArithResponse 算数运算响应结构体
type ArithResponse struct {
// 乘积
Pro int
// 商
Quo int
// 余数
Rem int
}
// Multiply 乘法运算方法
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
// Divide 除法运算方法
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("divide by zero")
}
res.Quo = req.A / req.B
res.Rem = req.A % req.B
return nil
}
func main() {
// 注册rpc服务
rpc.Register(new(Arith))
// 采用http协议作为rpc载体
rpc.HandleHTTP()
// 服务器创建监听端口
listen, err := net.Listen("tcp", "127.0.0.1:8095")
if err != nil {
log.Fatalln("fatal error: ", err)
}
fmt.Println("start connection")
// 处理请求
http.Serve(listen, nil)
}
client.go
package main
import (
"fmt"
"log"
"net/rpc"
)
// ArithRequest 算数运算请求结构体
type ArithRequest struct {
A int
B int
}
// ArithResponse 算数运算响应结构体
type ArithResponse struct {
// 乘积
Pro int
// 商
Quo int
// 余数
Rem int
}
func main() {
// 请求服务器连接
conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8095")
if err != nil {
log.Fatalln("dailing error: ", err)
}
req := ArithRequest{9, 2}
var res ArithResponse
// Call函数远程调用服务器的函数
err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
err = conn.Call("Arith.Divide", req, &res)
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}
net/rpc/jsonrpc
net/rpc/jsonrpc
server.go
package main
import (
"errors"
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
// Arith 算数运算结构体
type Arith struct {
}
// ArithRequest 算数运算请求结构体
type ArithRequest struct {
A int
B int
}
// ArithResponse 算数运算响应结构体
type ArithResponse struct {
Pro int // 乘积
Quo int // 商
Rem int // 余数
}
// Multiply 乘法运算方法
func (a *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
res.Pro = req.A * req.B
return nil
}
// Divide 除法运算方法
func (a *Arith) Divide(req ArithRequest, res *ArithResponse) error {
if req.B == 0 {
return errors.New("divide by zero")
}
res.Quo = req.A / req.B
res.Rem = req.A % req.B
return nil
}
func main() {
rpc.Register(new(Arith)) // 注册rpc服务
lis, err := net.Listen("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("fatal error: ", err)
}
fmt.Println("start connection")
for {
conn, err := lis.Accept() // 接收客户端连接请求
if err != nil {
continue
}
go func(conn net.Conn) { // 并发处理客户端请求
fmt.Println("new client in coming")
jsonrpc.ServeConn(conn)
}(conn)
}
}
client.go
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
// ArithRequest 算数运算请求结构体
type ArithRequest struct {
A int
B int
}
// ArithResponse 算数运算响应结构体
type ArithResponse struct {
Pro int // 乘积
Quo int // 商
Rem int // 余数
}
func main() {
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
if err != nil {
log.Fatalln("dailing error: ", err)
}
req := ArithRequest{9, 2}
var res ArithResponse
err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
err = conn.Call("Arith.Divide", req, &res)
if err != nil {
log.Fatalln("arith error: ", err)
}
fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
}