RPC远程过程调用
什么是RPC
Remote Procedure Call —— 远程过程调用
理解RPC:像调用本地函数一样,去调用远程函数
为什么微服务使用RPC:
- 每个服务都被封装成进程,彼此独立
- 进程和进程之可以使用不同语言来实现
RPC使用
- 服务端使用
- 注册rpc服务对象,给对象绑定方法(定义类、绑定类方法)
1 | rpc.RegisterName("服务名",回调对象) |
- 创建一个监听器
1 | listener,err := net.Listen() |
- 建立连接
1 | conn,err := listener.Accept() |
- 将连接绑定rpc服务
1 | rpc.ServerConn(conn) |
- 客户端使用
- 用rpc连接服务器
1 | conn ,err := rpc.Dial() |
- 调用远程函数
1 | conn.Call("服务名.方法名",传入参数,传出参数) |
RPC相关函数原型
- 注册rpc服务
1 | // 参数1:服务名 string类型 |
- 绑定rpc服务
1 | // 参数: conn 成功建立连接的socket |
- 调用远程函数
1 | // 参数1: serviceMethod:"服务名.方法名" |
Demo演示
- 服务端
1 | package main |
- 客户端
1 | package main |
RPC封装
- 服务端封装
上述代码中,如果Hello
函数没有按照rpc规定的格式书写,那么代码会在运行时报错。如何能让错误在编译期就被发现呢?
这里使用接口的方式进行实现。
1 | type MyInterface interface { |
这时原本注册rpc的程序需要先实现MyInterface
接口,向RegisterService
传递对象来进行rpc注册。如果方法没有实现该接口,程序会在编译期报错。
1 | func (w *World) Hello(name string, resp *string) (err error) { |
- 客户端封装
客户端同样应该对Call
函数进行封装,防止错误的调用。封装之后,客户端真正可以“像调用本地函数一样调用远程函数”
1 | type MyClient struct{ |
各种RPC框架实际上都将rpc协议进行了封装,从而很方便地进行远程调用
Protobuf
protocol buffer安装
- 下载官方release
下载完成之后解压
unzip xxx xxx
protobuf
的可执行文件protoc
在bin
文件夹下,进入文件夹执行protoc --version
可以查看版本信息
- 添加环境变量
这里有很多种方式,export
添加环境变量,修改配置文件,或者软链接等方式都可以,配置完之后在任何文件夹下都可以执行protoc
命令。
这里详细介绍一下软链接的方式。
- 使用
echo $PATH
命令查看系统环境变量,不出意外的话,都会包括/usr/bin/
、/usr/sbin/
等几个文件夹。 - 以
/usr/bin/
为例,假如解压之后得到的protoc
文件位于/xxx/bin/
文件夹下,使用ln
命令在/usr/bin/
文件夹下建立一个虚拟的protoc
文件,将虚拟文件链接到真实的/xxx/bin/protoc
。
1 | ln -s xxx/bin/protoc /usr/bin/protoc |
编写.proto
文件
例子:
1 | //默认是proto2 |
一些语法小结:
- message成员编号,可以不从1开始,但是不能重复,不能使用19000~19999;
- 可以使用message嵌套
- 定义数组、切片使用repeated关键字
- 可以使用枚举enum,enum第一个成员必须为0;
- 可以使用联合体,使用oneof关键字,成员编号不能重复。
proto文件编译
go语言中编译命令
protoc --go_out=./ *.proto
—->xxx.pb.go
protoc命令会将
.proto
文件编译成相应的代码文件,文件后缀名为.pb.go
对于go语言,这里的
protoc
命令会调用protoc-gen-go
插件生成go代码。protoc-gen-go
的安装非常方便,直接使用go get
即可1
go get -u github.com/golang/protobuf/protoc-gen-go
go get
安装的protoc-gen-go
默认在$GOPATH/bin
目录下,这里也需要添加到环境变量中,如果你之前已经将$GOPATH
添加进去了,这里就不需要进行操作。当然,同样也可以使用软链接的方式进行操作。但是经过实际测试,该插件会报错
protoc-gen-go: unable to determine Go import path for "test.proto"
,建议使用protoc-gen-gofast
。1
2
3
4
5//go get
go get github.com/gogo/protobuf/protoc-gen-gofast
//protoc
protoc --gofast_out=./ *.proto
添加RPC服务
- 语法
1 | service 服务名{ |
例:
1 | message People{ |
默认protobuf在编译期间不编译服务,要想使之编译,需要使用gRPC,使用的编译指令为:
protoc --gofast_out=plugins=grpc:./ *.proto
生成的xxx.pb.go与我们自己封装的rpc对比:
1 | type bj38Client struct{} ----- type MyClient struct{} |
gRPC
如前文所述,在.proto
文件中定义服务之后,可以使用protoc
工具生成对应的go文件。
使用gRPC定义服务端和客户端的方法如下:
- 服务端
- 定义类
按照接口绑定类方法
主函数中启动服务
- 初始化一个grpc对象
- 注册服务
- 设置监听
- 启动服务
- 客户端
- 连接gRPC服务
- 给grpc.Dial()传参,第一个参数为”ip:port”,第二个参数选用grpc.WithInsecure(),表示以安全的方式操作
- 初始化grpc客户端
- 调用远程服务
下面是一个例子:
1 | //proto_demo.proto |
1 | //proto_server.go |
1 | //proto_client.go |