RPC 协议
RPC定义
Remote Procedure Call Protocol
: 远程过程调用协议!
RPC
:远程进通信 , 应用层协议(http
协议同层)。底层使用 TCP
实现。
IPC
: 进程间通信
- 像调用本地函数一样,调用远程函数。
-
通过
rpc
协议,传递:函数名、函数参数。达到在本地,调用远端函数,得返回值到本地的目标。
RPC
使用
服务端:
- 注册 rpc 服务对象。给对象绑定方法( 1. 定义类, 2. 绑定类方法 )
rpc.RegisterName("服务名",回调对象)
- 创建监听器
listener, err := net.Listen()
- 建立连接
conn, err := listener.Accept()
- 将连接 绑定 rpc 服务。
rpc.ServeConn(conn)
客户端:
- 用 rpc 连接服务器。
conn, err := rpc.Dial()
- 调用远程函数。
conn.Call("服务名.方法名", 传入参数, 传出参数)
RPC 相关函数
- 注册 rpc 服务
func (server *Server) RegisterName(name string, rcvr interface{}) error 参1:服务名。字符串类型。 参2:对应 rpc 对象。 该对象绑定方法要满足如下条件: 1)方法必须是导出的 —— 首字母大写。 2)方法必须有两个参数, 都是导出类型、內建类型。 3)方法的第二个参数必须是 “指针” (传出参数) 4)方法只有一个 error 接口类型的 返回值。 例子: // 空结构体 type World stuct { } func (this *World) HelloWorld (name string, resp *string) error { } rpc.RegisterName("服务名", new(World))
- 绑定 rpc 服务
func (server *Server) ServeConn(conn io.ReadWriteCloser) conn: 成功建立好连接的 socket —— conn
- 调用远程函数:
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error serviceMethod: “服务名.方法名” args:传入参数。 方法需要的数据。 reply:传出参数。定义 var 变量,&变量名 完成传参。
编码实现
server
端
package main
import (
"fmt"
"net"
"net/rpc"
)
// World 定义类对象
type World struct {
}
func (this *World) HelloWorld(name string, resp *string) error {
*resp = name + "hello !!"
return nil
}
func main() {
// 1.注册RPC服务
err := rpc.RegisterName("xiaosheng", new(World))
if err != nil {
fmt.Println("注册服务失败")
}
// 2.设置监听
listener, err := net.Listen("tcp", "127.0.0.1:8700")
if err != nil {
fmt.Println("net listen err:", err)
return
}
defer listener.Close()
fmt.Println("[*]:开始监听---")
// 3.建立链接
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept () err:", err)
return
}
defer conn.Close()
fmt.Println("[*]:链接成功---")
// 4.绑定服务
rpc.ServeConn(conn)
}
client
端
package main
import (
"fmt"
"net/rpc"
)
func main() {
// 1.用rpc去链接服务器
conn, err := rpc.Dial("tcp", "127.0.0.1:8700")
if err != nil {
fmt.Println("Dial err:", err)
return
}
defer conn.Close()
// 2.调用远程函数
reply := ""
err = conn.Call("xiaosheng.HelloWorld", "xiaosheng", &reply)
if err != nil {
fmt.Println("Call err:", err)
}
fmt.Println(reply)
}
json 版 rpc
- 使用
nc -l 127.0.0.1 8700
充当服务器。 client.go
充当 客户端。 发起通信。会产生乱码。- 因为:
RPC
使用了go语言特有的数据序列化gob
。 其他编程语言不能解析。
- 因为:
- 解决方法: 使用 通用的 序列化、反序列化。 ——
json、protobuf
jsonrpc客户端
修改客户端,使用jsonrpc
:
conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8800")
jsonrpc server端
修改服务器端,使用 jsonrpc
:
jsonrpc.ServeConn(conn)
简单替换一下就行
另外:如果,绑定方法返回值的 error 不为空, 那么无论传出参数是否有值,服务端都不会返回数据。
rpc 封装
Server端封装
-
// 定义接口 type xxx interface { 方法名(传入参数,传出参数) error } 例: type MyInterface interface { HelloWorld(string, *string) error }
-
// 封装注册服务方法 func RegisterService (i MyInterface) { rpc.RegisterName("hello", i) }
例子
-
server
-
client
客户端封装
-
// 定义类 type MyClient struct { c *rpc.Client }
-
// 绑定类方法 func (this *MyClient)HelloWorld(a string, b *string) error { return this.c.Call("hello.HelloWorld", a, b) }
-
// 初始客户端 func InitClient(addr string) error { conn, _ := jsonrpc.Dial("tcp", adddr) return MyClient{c:conn} }
封装的demo
// client
package main
import (
"fmt"
)
func main() {
// 1.用rpc去链接服务器
myclient := InitClient("127.0.0.1:8700")
// 2.调用远程函数
reply := ""
err := myclient.HelloWorld("xiaosheng", &reply)
if err != nil {
fmt.Println("Call err:", err)
}
fmt.Println(reply)
}
// design封装client server
package main
import "net/rpc"
// -------------Server------------
//要求 服务端在注册rpc时要检测出注册对象是否合法
// MyInterface 创建接口, 在接口中定义原型方法
type MyInterface interface {
HelloWorld(string, *string) error
}
func RegisterService(i MyInterface) {
rpc.RegisterName("xiaosheng", i)
}
// -------------Client------------
// MyClient 像调用本地函数一样调用远程函数
type MyClient struct {
c *rpc.Client
}
// InitClient 由于使用了c来调用Call ,因此需要调用c.Dail来初始化,此处需要看client的实现
func InitClient(addr string) MyClient {
conn, _ := rpc.Dial("tcp", addr)
return MyClient{conn}
}
// HelloWorld 实现函数,原型按照上面 Interface来实现
func (this *MyClient) HelloWorld(a string, b *string) error {
this.c.Call("xiaosheng.HelloWorld", a, b)
return nil
}
package main
import (
"fmt"
"net"
"net/rpc"
)
// World 定义类对象
type World struct {
}
func (this *World) HelloWorld(name string, resp *string) error {
*resp = name + "hello !!"
return nil
}
func main() {
// 1.注册RPC服务
RegisterService(new(World)) // 封装的,调用的Design
// 2.设置监听
listener, err := net.Listen("tcp", "127.0.0.1:8700")
if err != nil {
fmt.Println("net listen err:", err)
return
}
defer listener.Close()
fmt.Println("[*]:开始监听---")
// 3.建立链接
conn, err := listener.Accept()
if err != nil {
fmt.Println("Accept () err:", err)
return
}
defer conn.Close()
fmt.Println("[*]:链接成功---")
// 4.绑定服务
rpc.ServeConn(conn)
}