【gRPC】grpc和protobuf快速入门 - 基于「Python」「Golang」

文章目录

  • 一、什么是grpc和protobuf
  • 二、python下体验protobuf
    • 1.安装
    • 2.先体验protobuf3
    • 3. 生成proto的python文件
    • 4. 查看protobuf生成的代码
    • 5. 对比一下protobuf生成的效果
  • 三、python体验grpc开发
  • 四、python下解决grpc import路径出错的bug
  • 五、go语言下grpc快速体验
    • 1. 下载工具
    • 2. 下载go的依赖包
    • 3. proto文件
    • 4. 生成go文件
    • 5. 服务端代码
    • 6. 客户端
  • 六、go和python互相调用
    • python当服务端,go当客户端
    • go当服务端,python当客户端
  • 七、grpc的流模式
    • 1.简单模式
    • 2.服务端数据流
    • 3.客户端数据流
    • 4.双向数据流
    • grpc的单向流、双向流代码实现


一、什么是grpc和protobuf

gRPC

官方github文档:github.com/grpc/grpc

  • gRPC是一个高性能、开源和通用的RPC框架,面向移动和HTTP/2设计。目前提供C、Java和Go语言版本,分别是: grpc, grpc-java, grpc-go.其中C版本支持C,C++,Node.js,Python, Ruby, Objective-C,PHP和C#支持.
  • 基于 IDL ( 接口定义语言(Interface Define Language))文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;
  • 通信协议基于标准的 HTTP/2 设计,支持·双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;
  • 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。
  • 安装简单,扩展方便(用该框架每秒可达到百万个RPC)

【gRPC】grpc和protobuf快速入门 - 基于「Python」「Golang」_第1张图片

protobuf

官方地址: https://developers.google.com/protocol-buffers/docs/proto3

  • 习惯用Json、XML数据存储格式的你们,相信大多都没听说过ProtocolBuffer
  • Protocol Buffer其实 是Google出品的一种轻量&高校的结构化数据存储格式,性能比Json、XML强太多
  • protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3

【gRPC】grpc和protobuf快速入门 - 基于「Python」「Golang」_第2张图片

详细参考protobuf:https://blog.csdn.net/the_shy_faker/article/details/128119379

二、python下体验protobuf

1.安装

python -m pip install grpcio #安装grpc
python -m pip install grpcio-tools #安装grpc tools

2.先体验protobuf3

只体验protobuf不需要安装grpcio

hello.proto

syntax = "proto3";

message HelloRequest {
  string name = 1; //name表示名称, name的编号是什么1 #可变长编码
}

3. 生成proto的python文件

python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto

4. 查看protobuf生成的代码

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)

5. 对比一下protobuf生成的效果

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等格式

三、python体验grpc开发

官方案例: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)

四、python下解决grpc import路径出错的bug

https://github.com/protocolbuffers/protobuf/issues/1491

将生成的grpc:

import helloworld_pb2 as helloworld__pb2

改成

from . import helloworld_pb2 as helloworld__pb2

五、go语言下grpc快速体验

1. 下载工具

https://github.com/protocolbuffers/protobuf/releases

注意:protoc的版本需要和golang/protobuf保持一致 (尽量自己去下载最新的版本)
下载完成后解压后记得将路径添加到环境变量中

2. 下载go的依赖包

go get github.com/golang/protobuf/protoc-gen-go

3. proto文件

syntax = "proto3";
option go_package = ".;proto";
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

4. 生成go文件

goland插件地址:https://plugins.jetbrains.com/plugin/14004-protocol-buffer-editor/versions

protoc -I . goods.proto --go_out=plugins=grpc:.

5. 服务端代码

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)
}

6. 客户端

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)
}

六、go和python互相调用

python当服务端,go当客户端

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当客户端

再来互换一下 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怎么简单的使用,这次讲讲 grpc 中的stream,srteam顾名思义就是一种流,可以源源不断的推送数据,很适合传输一些大数据,或者服务端和客户端长时间数据交互,比如客户端可以向服务端订阅一个数据,服务端就可以利用stream,源源不断地推送数据。

一般Rpc有这些模式:

  • 简单模式(Simple RPC)
  • 服务端数据流模式(Server-side streaming RPC)
  • 客户端数据流模式(Client-side streaming RPC)
  • 双向数据流模式(Bidirectional streaming RPC)

1.简单模式

这种模式最为传统,即客户端发起一次请求,服务端响应一个数据,这和大家平时熟悉的RPC没有什么大的区别,所以不再详细介绍。

2.服务端数据流

这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。

3.客户端数据流

与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据。比如一个设备实时监听屋里的温度 实时的向服务端发数据

4.双向数据流

顾名思义,这是客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。典型的例子是聊天机器人。

grpc的单向流、双向流代码实现

生成对应文件:

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()

}

你可能感兴趣的:(python,golang,开发语言,rpc)