简介
rpc
- 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议
- 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程
- 如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用
golang 中实现rpc
- golang中实现RPC非常简单,官方提供了封装好的库,还有一些第三方的库
- golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp和http数据传输方式,由于其他语言不支持gob编解码方式,所以golang的RPC只支持golang开发的服务器与客户端之间的交互
- 官方还提供了net/rpc/jsonrpc库实现RPC方法,jsonrpc采用JSON进行数据编解码,因而支持跨语言调用,目前jsonrpc库是基于tcp协议实现的,暂不支持http传输方式
- 例题:golang实现RPC程序,实现求矩形面积和周长
server:
注意
- 结构体字段首字母要大写,可以别人调用
- 函数名必须首字母大写
- 函数第一参数是接收参数,第二个参数是返回给客户端的参数,必须是指针类型
- 函数还必须有一个返回值error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| type Param struct { High, Width int }
type Rect struct{}
func (rect *Rect) Area(param Param, ret *int) error { *ret = param.High * param.Width return nil }
func (rect *Rect) Parimeter(param Param, ret *int) error { *ret = 2 * (param.High + param.Width) return nil }
func main() { rect := new(Rect) rpc.Register(rect) rpc.HandleHTTP() err := http.ListenAndServe(":18000", nil) if err != nil { log.Panicln(err) } }
|
client:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| type Param struct { High, Width int }
func main() { client, err := rpc.DialHTTP("tcp", ":18000") if err != nil { log.Panicln(err) } param := &Param{50, 100} var ret int err = client.Call("Rect.Area", param, &ret) if err != nil { log.Panicln(err) } log.Println("Area:", ret) err = client.Call("Rect.Parimeter", param, &ret) if err != nil { log.Panicln(err) } log.Println("Parimeter:", ret)
}
|
另外,net/rpc/jsonrpc库通过json格式编解码,支持跨语言调用
rpc 的调用流程
- 微服务架构下数据交互一般是对内 RPC,对外 REST
- 将业务按功能模块拆分到各个微服务,具有提高项目协作效率、降低模块耦合度、提高系统可用性等优点,但是开发门槛比较高,比如 RPC 框架的使用、后期的服务监控等工作
- 一般情况下,我们会将功能代码在本地直接调用,微服务架构下,我们需要将这个函数作为单独的服务运行,客户端通过网络调用
网络传输数据格式
两端需要约定好数据包的格式,成熟的rpc 框架会有自定义的传输协议,这里网络传输格式如下,前面是固定的长度消息头,后面是变长消息体
![img](https://allinit-1317182407.cos.ap-nanjing.myqcloud.com/go/2.jpg)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package rpc
import ( "encoding/binary" "io" "net" )
type Session struct { conn net.Conn }
func NewSession(conn net.Conn) *Session { return &Session{conn: conn} }
func (s *Session) Write(data []byte) error { buf := make([]byte, 4+len(data)) binary.BigEndian.PutUint32(buf[:4], uint32(len(data))) copy(buf[4:], data) _, err := s.conn.Write(buf) if err != nil { return err } return nil }
func (s *Session) Read() ([]byte, error) { header := make([]byte, 4) _, err := io.ReadFull(s.conn, header) if err != nil { return nil, err } dataLen := binary.BigEndian.Uint32(header) data := make([]byte, dataLen) _, err = io.ReadFull(s.conn, data) if err != nil { return nil, err } return data, nil }
|
编写rpc 服务端
- 服务端接收到的数据需要包括什么?
- 调用的函数名、参数列表,还有一个返回值error类型
- 服务端需要解决的问题是什么?
- 服务端的核心功能有哪些?
- 维护函数map
- 客户端传来的东西进行解析
- 函数的返回值打包,传给客户端