RPC简介
Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应
进行信息交互的系统
gRPC开发步骤
- 编写
.proto
文件,生成指定语言源代码 - 编写服务端代码
- 编写客户端代码
安装
创建项目
mkdir grpc_demo
cd grpc_demo
go mod init grpc_demo
安装gRPC
go get -u google.golang.org/grpc
安装Protocol Buffers v3
安装用于生成gRPC服务代码的协议编译器。软件地址
- 解压下载好的文件
- 把
protoc
二进制文件的路径加到环境变量中 - 安装protoc的Go插件
go get -u github.com/golang/protobuf/protoc-gen-go
- 编译插件
protoc-gen-go
将会安装到$GOBIN
,默认是$GOPATH/bin
,它必须在你的$PATH
中以便协议编译器protoc
能够找到它。
- 编译插件
C:UsersAlbin>d:
D:>cd D:PracticeScriptsgosrcgitee.comliubstdgo
D:PracticeScriptsgosrcgitee.comliubstdgo>mkdir grpc_demo
D:PracticeScriptsgosrcgitee.comliubstdgo>cd grpc_demo
D:PracticeScriptsgosrcgitee.comliubstdgogrpc_demo>go mod init grpc_demo
go: creating new go.mod: module grpc_demo
D:PracticeScriptsgosrcgitee.comliubstdgogrpc_demo>go get -u google.golang.org/grpc
go: downloading golang.org/x/net v0.0.0-20190311183353-d8887717615a
go: extracting golang.org/x/net v0.0.0-20190311183353-d8887717615a
go: downloading golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
go: extracting golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
go: finding golang.org/x/sys latest
go: finding google.golang.org/genproto latest
go: finding golang.org/x/net latest
D:PracticeScriptsgosrcgitee.comliubstdgogrpc_demo>go get -u github.com/golang/protobuf/protoc-gen-go
示例
创建项目代码目录结构
mkdir helloworldpb
mkdir helloworldserver
mkdir helloworldclient
D:PracticeScriptsgosrcgitee.comliubstdgogrpc_demo>tree
文件夹 PATH 列表
卷序列号为 A021-AE06
D:.
└─helloworld
├─client
├─pb
└─server
编写proto代码
gRPC是基于Protocol Buffers。
Protocol Buffers
是一种与语言无关,平台无关的可扩展机制,用于序列化结构化数据。使用Protocol Buffers
可以一次定义结构化的数据,然后可以使用特殊生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。
编写文件
vi helloworld/pb/helloworld.proto
syntax = "proto3"; // 版本声明,使用Protocol Buffers v3版本
package pb; // 包名
// 定义一个打招呼服务
service Greeter {
// SayHello 方法
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 包含人名的一个请求消息
message HelloRequest {
string name = 1;
}
// 包含问候语的响应消息
message HelloReply {
string message = 1;
}
生成go语言源代码
protoc -I helloworld/ helloworld/pb/helloworld.proto --go_out=plugins=grpc:helloworld
在gRPC_demo/helloworld/pb
目录下会生成helloworld.pb.go
文件。
编写server端go代码
vi grpc_demo/helloworld/server/server.go
package main
import (
"fmt"
"net"
pb "grpc_demo/helloworld/pb"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type server struct{}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "hello " + in.Name}, nil
}
func main() {
// 监听本地的8972端口
lsnr, err := net.Listen("tcp", ":8972")
if err != nil {
fmt.Printf("failed to listen: %v", err)
return
}
s := grpc.NewServer() // 创建gRPC服务器
pb.RegisterGreeterServer(s, &server{}) // 在gRPC服务端注册服务
reflection.Register(s) // 在给定的gRPC服务器上注册服务器反射服务
// Serve方法在lsnr上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
err = s.Serve(lsnr)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
}
}
编译并执行
cd helloworld/server
go build
./server
编写客户端go代码
package main
import (
"context"
"fmt"
pb "grpc_demo/helloworld/pb"
"google.golang.org/grpc"
)
func main() {
// 连接服务器
conn, err := grpc.Dial(":8972", grpc.WithInsecure())
if err != nil {
fmt.Printf("faild to connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// 调用服务端的SayHello
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "biao"})
if err != nil {
fmt.Printf("could not greet: %v", err)
}
fmt.Printf("Greeting: %s !
", r.Message)
}
运行
gRPC跨语言调用
使用Python
语言编写Client
,然后向上面使用go
语言编写的server
发送RPC请求
安装相关python模块
pip3 install grpcio
pip3 install grpcio-tools
pip3 install protobuf
生成python代码
在gRPC_demo
目录执行命令
python -m grpc_tools.protoc -I helloworld/pb/ --python_out=helloworld/client/ --grpc_python_out=helloworld/client/ helloworld/pb/helloworld.proto
上面命令执行完成后,在grpc_demo/helloworld/client/
目录生成如下两个python文件
编写python版client代码
#!/usr/bin/env python
# coding=utf-8
import logging
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# 注意(gRPC Python Team): .close()方法在channel上是可用的。
# 并且应该在with语句不符合代码需求的情况下使用。
with grpc.insecure_channel('localhost:8972') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='标'))
print("Greeter client received: {}!".format(response.message))
if __name__ == '__main__':
logging.basicConfig()
run()