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)
}
 
													 
													