gRPC
官方github文档:github.com/grpc/grpc
protobuf
官方地址: https://developers.google.com/protocol-buffers/docs/proto3
详细参考protobuf:https://blog.csdn.net/the_shy_faker/article/details/128119379
python -m pip install grpcio #安装grpc
python -m pip install grpcio-tools #安装grpc tools
只体验protobuf不需要安装grpcio
hello.proto
syntax = "proto3";
message HelloRequest {
string name = 1; //name表示名称, name的编号是什么1 #可变长编码
}
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
from grpc_test import helloworld_pb2
request = helloworld_pb2.HelloRequest()
request.name = "bobby"
req_str = request.SerializeToString()
print(req_str)
request2 = helloworld_pb2.HelloRequest()
request2.ParseFromString(req_str)
print(request2.name)
from protobuf_test.proto import hello_pb2
#生成的pb文件不要去改
request = hello_pb2.HelloRequest()
request.name = "bobby"
res_str = request.SerializeToString()
print(len(res_str))
res_json = {
"name":"bobby"
}
import json
print(len(json.dumps(res_json)))
#如何通过字符串反向生成对象
request2 = hello_pb2.HelloRequest()
request2.ParseFromString(res_str)
print(request2.name)
#和json对比一下
你可以将proto序列化成json dict等格式
官方案例:https://github.com/grpc/grpc/tree/master/examples/python/helloworld
proto文件:
syntax = "proto3";
option go_package = ".;proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
//go语言中是生成一个文件, 也就只有python会生成两个文件
server端
import grpc
from concurrent import futures
from grpc_hello.proto import helloworld_pb2, helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message=f"你好,{request.name},{request.id}")
if __name__ == '__main__':
# 实例化
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# 注册逻辑
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
# 启动server
server.add_insecure_port('[::]:50054')
server.start()
# 线程等待
server.wait_for_termination()
client端
import grpc
from grpc_hello.proto import helloworld_pb2, helloworld_pb2_grpc
if __name__ == '__main__':
with grpc.insecure_channel("localhost:50054") as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
hello_request = helloworld_pb2.HelloRequest()
hello_request.name = "bobby"
hello_request.id.extend([1, 2])
response: helloworld_pb2.HelloReply = stub.SayHello(hello_request)
print(response.message)
https://github.com/protocolbuffers/protobuf/issues/1491
将生成的grpc:
import helloworld_pb2 as helloworld__pb2
改成
from . import helloworld_pb2 as helloworld__pb2
https://github.com/protocolbuffers/protobuf/releases
注意
:protoc的版本需要和golang/protobuf保持一致 (尽量自己去下载最新的版本)
下载完成后解压后记得将路径添加到环境变量中
go get github.com/golang/protobuf/protoc-gen-go
syntax = "proto3";
option go_package = ".;proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
goland插件地址:https://plugins.jetbrains.com/plugin/14004-protocol-buffer-editor/versions
protoc -I . goods.proto --go_out=plugins=grpc:.
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpc_demo/hello"
"net"
)
type Server struct {
}
func (s *Server) SayHello(ctx context.Context,request *hello.HelloRequest)(*hello.HelloReply,error){
return &hello.HelloReply{Message:"Hello "+request.Name},nil
}
func main() {
g := grpc.NewServer()
s := Server{}
hello.RegisterGreeterServer(g,&s)
lis, err := net.Listen("tcp", fmt.Sprintf(":8080"))
if err != nil {
panic("failed to listen: "+err.Error())
}
g.Serve(lis)
}
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"grpc_demo/proto"
)
func main() {
conn,err := grpc.Dial("127.0.0.1:8080",grpc.WithInsecure())
if err!=nil{
panic(err)
}
defer conn.Close()
c := hello.NewGreeterClient(conn)
r,err := c.SayHello(context.Background(),&hello.HelloRequest{Name:"bobby"})
if err!=nil{
panic(err)
}
fmt.Println(r.Message)
}
proto文件必须一致
go当客户端或者当服务端都可以,python也一样 别忘了生成对应的文件
python生成对应文件:
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto.
go生成对应文件:
protoc -I . helloworld.proto --go_out=plugins=grpc:.
–带有验证器
protoc -I . --go_out=plugins=grpc:. --validate_out=“lang=go:.” helloworld.proto
helloworld.proto:
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
repeated int32 id = 2;
}
message HelloReply {
string message = 1;
}
比如python当服务端:
import grpc
from concurrent import futures
from grpc_hello.proto import helloworld_pb2, helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message=f"你好,{request.name},{request.id}")
if __name__ == '__main__':
# 实例化
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# 注册逻辑
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
# 启动server
server.add_insecure_port('[::]:50054')
server.start()
# 线程等待
server.wait_for_termination()
go当客户端:
package main
import (
"awesomeProject3/grpc_test/proto"
"context"
"fmt"
"google.golang.org/grpc"
)
func main() {
conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithInsecure())
if err != nil {
panic(err)
}
defer conn.Close()
c := proto.NewGreeterClient(conn)
r, err := c.SayHello(context.Background(), &proto.HelloRequest{Name: "bobby"})
if err != nil {
panic(err)
}
fmt.Println(r.Message)
}
再来互换一下 go当服务端,python当客户端:
proto还是一样
go服务端:
package main
import (
"awesomeProject3/grpc_test/proto"
"context"
"google.golang.org/grpc"
"net"
)
type Server struct{}
func (s *Server) SayHello(ctx context.Context, request *proto.HelloRequest) (*proto.HelloReply, error) {
return &proto.HelloReply{
Message: "hello" + request.Name,
}, nil
}
func main() {
g := grpc.NewServer()
proto.RegisterGreeterServer(g, &Server{})
lis, err := net.Listen("tcp", "0.0.0.0:8081")
if err != nil {
panic("失败:" + err.Error())
}
err = g.Serve(lis)
if err != nil {
panic("失败:" + err.Error())
}
}
python客户端:
import grpc
from grpc_hello.proto import helloworld_pb2, helloworld_pb2_grpc
if __name__ == '__main__':
with grpc.insecure_channel("localhost:50054") as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
hello_request = helloworld_pb2.HelloRequest()
hello_request.name = "bobby"
hello_request.id.extend([1, 2])
response: helloworld_pb2.HelloReply = stub.SayHello(hello_request)
print(response.message)
之前我们讲了grpc怎么简单的使用,这次讲讲 grpc 中的stream,srteam顾名思义就是一种流,可以源源不断的推送数据,很适合传输一些大数据,或者服务端和客户端长时间数据交互,比如客户端可以向服务端订阅一个数据,服务端就可以利用stream,源源不断地推送数据。
一般Rpc有这些模式:
这种模式最为传统,即客户端发起一次请求,服务端响应一个数据,这和大家平时熟悉的RPC没有什么大的区别,所以不再详细介绍。
这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。
与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据。比如一个设备实时监听屋里的温度 实时的向服务端发数据
顾名思义,这是客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。典型的例子是聊天机器人。
生成对应文件:
protoc -I . stream.proto --go_out=plugins=grpc:.
stream.proto:
syntax = "proto3";
option go_package = ".;proto";
//option go_package = "../../common/stream/proto/v1";
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 data = 1;
}
服务端:
package main
import (
"awesomeProject3/stream_grpc_test/proto"
"fmt"
"google.golang.org/grpc"
"net"
"sync"
"time"
)
const PORT = "127.0.0.1:50052"
type server struct{}
//服务流
func (s *server) GetStream(req *proto.StreamReqData, res proto.Greeter_GetStreamServer) error {
i := 0
for {
i++
//Send方法只要调用一次 客户端就会立马收到
_ = res.Send(&proto.StreamResData{
Data: fmt.Sprintf("%v", time.Now().Unix()),
})
time.Sleep(time.Second)
if i > 10 {
break
}
}
return nil
}
//客户流
func (s *server) PutStream(cliStr proto.Greeter_PutStreamServer) error {
for {
if a, err := cliStr.Recv(); err != nil {
fmt.Println(err)
break
} else {
fmt.Println(a.Data)
}
}
return nil
}
//双向流
func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
for {
data, _ := allStr.Recv()
fmt.Println("收到客户端消息:" + data.Data)
}
}()
go func() {
defer wg.Done()
i := 1
for {
i++
_ = allStr.Send(&proto.StreamResData{Data: "我是服务器"})
time.Sleep(time.Second)
if i > 10 {
break
}
}
}()
wg.Wait()
return nil
}
func main() {
lis, err := net.Listen("tcp", PORT)
if err != nil {
panic(err)
}
s := grpc.NewServer()
proto.RegisterGreeterServer(s, &server{})
err = s.Serve(lis)
if err != nil {
panic(err)
}
}
客户端:
package main
import (
"awesomeProject3/stream_grpc_test/proto"
"context"
"fmt"
"google.golang.org/grpc"
"sync"
"time"
)
var wg = sync.WaitGroup{}
func main() {
conn, err := grpc.Dial("127.0.0.1:50052", grpc.WithInsecure())
if err != nil {
panic(err)
}
defer conn.Close()
c := proto.NewGreeterClient(conn)
wg.Add(2)
//服务流
//go func() {
// wg.Done()
// //拿到通道
// res, _ := c.GetStream(context.Background(), &proto.StreamReqData{Data: "慕课网"})
// for {
// a, err := res.Recv()
// if err != nil {
// fmt.Println(err)
// break
// }
// fmt.Println(a.Data)
// }
//}()
客户流
//go func() {
// defer wg.Done()
// putS, _ := c.PutStream(context.Background())
// i := 0
// //死循环拿值
// for {
// i++
// putS.Send(&proto.StreamReqData{
// Data: fmt.Sprintf("慕课网%d", i),
// })
// time.Sleep(time.Second)
// if i > 10 {
// break
// }
// }
//}()
//双向流
allStr, _ := c.AllStream(context.Background())
go func() {
defer wg.Done()
for {
data, _ := allStr.Recv()
fmt.Println("收到服务端消息:" + data.Data)
}
}()
go func() {
defer wg.Done()
i := 1
for {
i++
_ = allStr.Send(&proto.StreamReqData{Data: fmt.Sprintf("慕课网:%d", i)})
time.Sleep(time.Second)
if i > 10 {
break
}
}
}()
wg.Wait()
}