实现RPC调用几大要素
- 概念: 像调用本地方法一样调用远程方法, 要实现类似的效果就必须满足一些规则,即以下几大要素
- CALLID映射(服务端和客户端都需要有一个映射表, 服务端通过表知道客户端调用的是那个方法),
- 序列化和反序列化(数据协议, 参数无法直接传输, 需要序列化成双方都知道的协议进行传输, 数据协议压缩的越小传递的字节就越小, 传递速度也就越快, 常用的json就是一种数据协议,但是压缩比率并不高,故性能也不高)
- 网络传输协议(HTTP, TCP 等)
HTTP实现一个简单的RPC
- CALLID映射通过URL, 数据协议使用URL参数, 网络传输协议使用HTTP
服务端代码
func main() {
http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
fmt.Println(fmt.Sprintf("路径:%v",r.URL.Path))
a, _ := strconv.Atoi(r.Form.Get("a"))
b, _ := strconv.Atoi(r.Form.Get("b"))
jData,_ := json.Marshal(map[string]int{
"data":a+b,
})
_, _ = w.Write(jData)
})
_ = http.ListenAndServe(":8000", nil)
}
客户端代码
type ResponseData struct {
Data int `json:"data"`
}
func add(a, b int) int {
req := HttpRequest.NewRequest()
resp, _ := req.Get(fmt.Sprintf("http://127.0.0.1:8000/%v?a=%v&b=%v","add",a,b))
resBody, _ := resp.Body()
respData := &ResponseData{}
_ = json.Unmarshal(resBody, respData)
return respData.Data
}
func main() {
total := add(1,2)
fmt.Println(total)
}
go 原生RPC调用
- go的默认的rpc使用gob数据协议, 是go自己定义的一种数据传输协议,效率比json高,但是通用性较差, 无法跨语言调用
服务端代码
type HelloService struct {
}
func (receiver *HelloService) Hello(arg string, reply *string) error {
*reply = "hello " + arg
return nil
}
func main() {
listen, _ := net.Listen("tcp", ":1234")
_ = rpc.RegisterName("HelloService", &HelloService{})
for {
accept, _ := listen.Accept()
go rpc.ServeConn(accept)
}
}
客户端代码
func main() {
dial, err := rpc.Dial("tcp", "127.0.0.1:1234")
if err != nil {
panic(err)
}
var reply string
_ = dial.Call("HelloService.Hello", "rpc2", &reply)
fmt.Println(reply)
}
go原生rpc使用json数据协议进行调用示例
- 使用json数据协议是当前的一个示例, 也可以自定义其他数据协议
服务端代码
type HelloService struct {
}
func (receiver *HelloService) Hello(arg string, reply *string) error {
*reply = "hello " + arg
return nil
}
func main() {
listen, _ := net.Listen("tcp", ":1234")
_ = rpc.RegisterName("HelloService", &HelloService{})
for {
accept, _ := listen.Accept()
go rpc.ServeCodec(jsonrpc.NewServerCodec(accept))
}
}
客户端代码
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:1234")
if err != nil {
panic(err)
}
var reply string
client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))
_ = client.Call("HelloService.Hello", "rpc2", &reply)
fmt.Println(reply)
}