grpc入门
grpc无缝接入的数据序列化反序列化协议就是protobuf,所以先讲解一下protobuf
protobuf(protocol buffer)的优点和缺点
python下体验protobuf
- 安装
python -m pip install grpcio # 安装grpc
python -m pip install grpcio-tools # 安装grpc tools
protobuf3格式定义
syntax = "proto3";
message HelloRequest {
// 字段格式:限定修饰符 | 数据类型 | 字段名称 | = | 字段编码值 | [字段默认值]
string name = 1; // name表示名称,name的编号是1
}
生成proto的python文件
// -I. 表示:在当前目录下找helloworld.proto文件,并为我们生成protobuf要用的文件和grpc要用的文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. hello.proto
python中protobuf的序列化与反序列化
from proto import hello_pb2
# 生成的pb文件不要去改
request = hello_pb2.HelloRequest()
request.name = "bobby"
# 将对象序列化为protobuf类型的字符串
res_str = request.SerializeToString()
print(res_str) # b'\n\x05bobby'
# 如何将protobuf字符串反向生成对象
request2 = hello_pb2.HelloRequest()
request2.ParseFromString(res_str)
print(request2.name) # bobby
protobuf与json的压缩对比
from proto import hello_pb2
# 生成的pb文件不要去改
request = hello_pb2.HelloRequest()
request.name = "bobby"
# 将对象序列化为protobuf类型的字符串
res_str = request.SerializeToString()
print(res_str, len(res_str)) # b'\n\x05bobby' 7
res_json = {
"name": "bobby"
}
import json
json_str = json.dumps(res_json)
print(json_str, len(json_str)) # {"name": "bobby"} 17
python下体验grpc开发
- 目录结构图
- helloword.proto文件
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
- 执行命令生成protobuf文件和grpc文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
- server.py代码
from concurrent.futures import ThreadPoolExecutor
import grpc
from grpc_hello.proto import helloworld_pb2_grpc, helloworld_pb2
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message=f"你好, {request.name}")
if __name__ == '__main__':
# 1. 实例化server
server = grpc.server(ThreadPoolExecutor(max_workers=10))
# 2. 注册逻辑到server中
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
# 3. 运行server
server.add_insecure_port("127.0.0.1:50051")
server.start()
# 主线程等待所有子线程
server.wait_for_termination()
- client.py代码
import grpc
from grpc_hello.proto import helloworld_pb2, helloworld_pb2_grpc
if __name__ == '__main__':
with grpc.insecure_channel("127.0.0.1:50051") as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
rsp: helloworld_pb2.HelloReply = stub.SayHello(helloworld_pb2.HelloRequest(name="小王"))
print(rsp.message)
go下grpc快速体验-1
下载
-
下载protoc.exe
https://github.com/protocolbuffers/protobuf/releases/tag/v3.19.1
下载后解压,然后将解压的protoc.exe放在GOPATH/bin目录下(或者直接将protc.exe所在的bin目录配置到环境变量当中去也可以) -
下载protoc-gen-go.exe
go install github.com/golang/protobuf/protoc-gen-go
为什么使用go install 不使用go get, 参考文档
上面下载好后,会在GOPATH/bin下生成protoc-gen-go.exe
如果执行命令protoc -I . helloword.proto --go_out=plugins=grpc:.
报错,没有protoc-gen-go可执行文件,那么需要将生成的protoc.exe文件复制到
当前面目的GOPATH/bin目录下一份即可。 -
安装protobuf
go get google.golang.org/protobuf
-
安装grpc
go get google.golang.org/grpc
proto目录下的helloword.proto文件
syntax = "proto3";
option go_package = "../proto;helloword"; // 注意:../proto代表在哪一个目录生成,helloword代表包名称
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
生成helloword.pg.go文件
先切换到helloword.proto文件所在的proto目录下,执行如下命令
protoc -I . helloword.proto --go_out=plugins=grpc:.
注意:go语言中只生成一个文件.pb.go, 也就只有python中会生成两个文件,一个pb文件,一个grpc文件,其它的语言都生成一个文件
目录结构
go语言下的server代码
package main
import (
"context"
"goRPC/grpc_test/proto"
"google.golang.org/grpc"
"net"
)
type Greeter struct {}
func (g *Greeter) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply, error) {
rsp := &proto.HelloReply{Message: "hello " + request.Name}
return rsp, nil
}
func main() {
// 实例化一个grpc的server
server := grpc.NewServer()
// 注册rpc服务到grpc的server中
proto.RegisterGreeterServer(server, &Greeter{})
// 运行server
listener, _ := net.Listen("tcp", ":50051")
_ = server.Serve(listener)
}
go语言下的client代码
package main
import (
"context"
"goRPC/grpc_test/proto"
"google.golang.org/grpc"
)
func main() {
conn, _ := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
defer conn.Close()
client := proto.NewGreeterClient(conn)
rsp, _ := client.SayHello(context.Background(), &proto.HelloRequest{Name: "马艳娜"})
print(rsp.Message)
}
go和python互相调用
- 为了防止不一致,将go下的helloword.proto文件复制到python下重新生成protobuf文件和grpc文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
- 启动go服务端,然后用python客户端访问
- 启动Python服务端,然后用go客户端访问
grpc跨语言通信:so easy!
grpc的流模式的定义
- grpc的四种数据流
- 简单模式 simple rpc
- 服务端数据流模式 server-side streaming rpc
- 客户端数据流模式 client-side streaming rpc
- 双向数据流模式 bidirectional steaming rpc
- 流模式的proto文件定义
syntax = "proto3";
option go_package="../proto;";
service Greeter {
// 服务端流模式
rpc GetStream(StreamReqData) returns (stream StreamResData) {}
// 客户端流模式
rpc PutStream(stream StreamReqData) returns (StreamResData);
// 双向流模式
rpc AllStream(stream StreamReqData) returns (stream StreamResData);
}
message StreamReqData {
string data = 1;
}
message StreamResData {
string message = 1;
}
服务端流模式代码
- server.go
点击查看代码
package main
import (
"fmt"
"goRPC/stream_grpc_test/proto"
"google.golang.org/grpc"
"net"
"time"
)
type Greeter struct {}
func (g Greeter) GetStream(data *proto.StreamReqData, server proto.Greeter_GetStreamServer) error {
i := 0
for {
i++
if i > 3 {
break
}
_ = server.Send(&proto.StreamResData{Message: fmt.Sprintf("%v: %s", time.Now().Unix(), data.Data)})
time.Sleep(time.Second)
}
return nil
}
func (g Greeter) PutStream(server proto.Greeter_PutStreamServer) error {
return nil
}
func (g Greeter) AllStream(server proto.Greeter_AllStreamServer) error {
return nil
}
func main() {
// 实例化一个server
server := grpc.NewServer()
// 注册rpc服务到server中
proto.RegisterGreeterServer(server, &Greeter{})
// 运行server
listener, _ := net.Listen("tcp", ":50051")
_ = server.Serve(listener)
}
- client.go
点击查看代码
package main
import (
"context"
"fmt"
"goRPC/stream_grpc_test/proto"
"google.golang.org/grpc"
)
func main() {
grpcClient, _ := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
defer grpcClient.Close()
greeterClient := proto.NewGreeterClient(grpcClient)
greeterGetStreamClient, _ := greeterClient.GetStream(context.Background(), &proto.StreamReqData{Data: "马亚南"})
for {
data, err := greeterGetStreamClient.Recv()
if err != nil {
break
}
fmt.Println(data.Message)
}
}
客户端流模式代码
- server.go
点击查看代码
package main
import (
"fmt"
"goRPC/stream_grpc_test/proto"
"google.golang.org/grpc"
"net"
"time"
)
type Greeter struct {}
func (g Greeter) GetStream(data *proto.StreamReqData, server proto.Greeter_GetStreamServer) error {
// 服务端流模式
i := 0
for {
i++
if i > 3 {
break
}
_ = server.Send(&proto.StreamResData{Message: fmt.Sprintf("%v: %s", time.Now().Unix(), data.Data)})
time.Sleep(time.Second)
}
return nil
}
func (g Greeter) PutStream(server proto.Greeter_PutStreamServer) error {
// 客户端流模式
for {
streamReqData, err := server.Recv()
if err != nil {
print(err.Error())
break
}
fmt.Println(streamReqData.Data)
}
return nil
}
func (g Greeter) AllStream(server proto.Greeter_AllStreamServer) error {
return nil
}
func main() {
// 实例化一个server
server := grpc.NewServer()
// 注册rpc服务到server中
proto.RegisterGreeterServer(server, &Greeter{})
// 运行server
listener, _ := net.Listen("tcp", ":50051")
_ = server.Serve(listener)
}
- client.go
点击查看代码
package main
import (
"context"
"goRPC/stream_grpc_test/proto"
"google.golang.org/grpc"
"strconv"
"time"
)
func main() {
grpcClient, _ := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
defer grpcClient.Close()
greeterClient := proto.NewGreeterClient(grpcClient)
greeterPutClient, _ := greeterClient.PutStream(context.Background())
for i := 1; i <= 3; i++ {
_ = greeterPutClient.Send(&proto.StreamReqData{Data: strconv.Itoa(int(time.Now().Unix()))})
time.Sleep(time.Second)
}
}
双向流模式
- server.go
点击查看代码
package main
import (
"fmt"
"goRPC/stream_grpc_test/proto"
"google.golang.org/grpc"
"net"
"sync"
"time"
)
type Greeter struct {}
func (g Greeter) GetStream(data *proto.StreamReqData, server proto.Greeter_GetStreamServer) error {
// 服务端流模式
i := 0
for {
i++
if i > 3 {
break
}
_ = server.Send(&proto.StreamResData{Message: fmt.Sprintf("%v: %s", time.Now().Unix(), data.Data)})
time.Sleep(time.Second)
}
return nil
}
func (g Greeter) PutStream(server proto.Greeter_PutStreamServer) error {
// 客户端流模式
for {
streamReqData, err := server.Recv()
if err != nil {
print(err.Error())
break
}
fmt.Println(streamReqData.Data)
}
return nil
}
func (g Greeter) AllStream(server proto.Greeter_AllStreamServer) error {
// 双向流模式
var wg sync.WaitGroup
wg.Add(1)
// 接收客户端源源不断发来的数据
go func() {
defer wg.Done()
for {
streamReqData, err := server.Recv()
if err != nil {
print(err.Error())
break
}
fmt.Println("收到客户端的消息:", streamReqData.Data)
}
}()
// 源源不断的向客户端发送数据
for i := 1; i <= 3; i++ {
_ = server.Send(&proto.StreamResData{Message: fmt.Sprintf("我是服务器-%d", i)})
time.Sleep(time.Second)
}
wg.Wait()
return nil
}
func main() {
// 实例化一个server
server := grpc.NewServer()
// 注册rpc服务到server中
proto.RegisterGreeterServer(server, &Greeter{})
// 运行server
listener, _ := net.Listen("tcp", ":50051")
_ = server.Serve(listener)
}
- client.go
点击查看代码
package main
import (
"context"
"fmt"
"goRPC/stream_grpc_test/proto"
"google.golang.org/grpc"
"sync"
"time"
)
func main() {
grpcClient, _ := grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())
defer grpcClient.Close()
greeterClient := proto.NewGreeterClient(grpcClient)
greeterAllClient, _ := greeterClient.AllStream(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for i := 1; i <= 3; i++ {
_ = greeterAllClient.Send(&proto.StreamReqData{Data: fmt.Sprintf("我是客户端-%d", i)})
time.Sleep(time.Second)
}
}()
for {
streamResData, err := greeterAllClient.Recv()
if err != nil {
fmt.Println(err.Error())
break
}
fmt.Println("收到服务器的消息", streamResData.Message)
}
wg.Wait()
}