grpc入门

grpc无缝接入的数据序列化反序列化协议就是protobuf,所以先讲解一下protobuf

protobuf(protocol buffer)的优点和缺点

python下体验protobuf

  1. 安装
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开发

  • 目录结构图
  1. helloword.proto文件
syntax = "proto3";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
  1. 执行命令生成protobuf文件和grpc文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
  1. 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()
  1. 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

下载

  1. 下载protoc.exe
    https://github.com/protocolbuffers/protobuf/releases/tag/v3.19.1
    下载后解压,然后将解压的protoc.exe放在GOPATH/bin目录下(或者直接将protc.exe所在的bin目录配置到环境变量当中去也可以)

  2. 下载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目录下一份即可。

  3. 安装protobuf
    go get google.golang.org/protobuf

  4. 安装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
  1. 启动go服务端,然后用python客户端访问
  2. 启动Python服务端,然后用go客户端访问
    grpc跨语言通信:so easy!

grpc的流模式的定义

  • grpc的四种数据流
  1. 简单模式 simple rpc
  2. 服务端数据流模式 server-side streaming rpc
  3. 客户端数据流模式 client-side streaming rpc
  4. 双向数据流模式 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;
}

服务端流模式代码

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

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

客户端流模式代码

  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 {
	// 客户端流模式
	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)
}

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

双向流模式

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

  1. 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()
}

posted @ 2021-12-26 16:00  专职  阅读(152)  评论(0编辑  收藏  举报