Python3搭建gRPC服务(进阶版二)之Protocol Buffers语法与实战

文章导读

本文主要介绍gRPC中重要的部分Protocol Buffers语法,预计需要花费15分钟左右的时间,阅读后你将收获以下知识,

  1. Protocol Buffers语法规范。
  2. 通过Protocol Buffers进行数据结构定义。
  3. 通过Protocol Buffers进行服务定义
  4. 多种数据类型的gRPC服务搭建。

这是一篇进阶版本的文章,读者应具备基本的gRPC基础,在此之前已有文章讲解Python3搭建gRPC(入门版),如不了解gRPC建议先阅读

Python3搭建gRPC(入门版)

Python3搭建gRPC服务(进阶版一)之Protocol Buffers基础概念

同时提供以下进阶材料

  1. Python3搭建gRPC(入门版)
  2. 同步视频教程(后续会更新)
  3. 其他相关联文章,包括pytest进行gRPC测试,python3客户端测试java/golang grpc服务端(后续会更新)

如果大家觉得有收获,记得一键三连,后续继续为大家输出更多有价值的文章!!!

Protocal Buffers语法

Protocal Buffers是描述型语言,语法简单,本章将详细讲解

数据类型和各语言之间的关系

Protocal Buffers最终都是需要转换成各种语言的源码,需要了解在pb的数据类型和各语言之间的对应关系

.proto TypeNotesC++ TypeJava/Kotlin Type[1]Python Type[3]Go TypeRuby TypeC# TypePHP TypeDart Type
doubledoubledoublefloatfloat64Floatdoublefloatdouble
floatfloatfloatfloatfloat32Floatfloatfloatdouble
int32int32intintint32Fixnum or Bignum (as required)intintegerint
int64int64longint/long[4]int64Bignumlonginteger/string[6]Int64
uint32uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
uint64uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sint32int32intintint32Fixnum or Bignum (as required)intintegerint
sint64int64longint/long[4]int64Bignumlonginteger/string[6]Int64
fixed32uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
fixed64uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sfixed32int32intintint32Fixnum or Bignum (as required)intintegerint
sfixed64int64longint/long[4]int64Bignumlonginteger/string[6]Int64
boolboolbooleanboolboolTrueClass/FalseClassboolbooleanbool
stringstringStringstr/unicode[5]stringString (UTF-8)stringstringString
bytesstringByteStringstr (Python 2) bytes (Python 3)[]byteString (ASCII-8BIT)ByteStringstring
常见类型如何定义

pb包含有多种类型,包括int,float,string,bool,map等

pb没有list类型,该如何定义,在list类型章节介绍

基础类型
syntax = "proto3";

message IntRequest{
  int32 id=1;
}

message FloatRequest{
  float num=1;
}

message StringRequest{
  string name=1;
}

message BoolRequest{
  bool exist=1;
}

map类型

map类型需要定义内部的泛型,如下表示search为key和value都是string类型的map,在Python中叫字典

message MapRequest{
  map<string, string> search=1;
}

list类型

list在pb中没有对应的数据类型,需要使用repeated关键词的意思是可重复0到N个相同的值,和列表的定义是一致的,列表中有0到N元素

如下定义表示UsersRequest数据结构中,usernames是一个string类型的列表

message UsersRequest{
  repeated string usernames=1;
}

外部依赖的类型怎么处理

已有一个map_demo.proto

# map_demo.proto
syntax = "proto3";

message MapDemo{
  map<string, string> keyValue=1;
}

map_use_demo.proto需要引用map_demo.proto中定义的类型

# map_use_demo.proto

syntax = "proto3";

import "map_demo.proto";

service MapService{
  rpc MapHello(Request) returns (Response){}
}

message Request{
  MapDemo mapDemo=1;
}

message Response{
  string name=1;
}

需要在map_use_demo.proto中import "map_demo.proto",这样map_demo中定义的MapDemo数据类型就可以使用了

外部引用有2个注意点:

1、在第四章中讲到grpc_tools.protoc -I参数就是在这种情况下使用,-I需要指定为map_demo.proto所在的目录,如不指定,生成map_use_demo时会找不到map_demo.proto

2、map_demo.proto也需要编译成Python代码

具体demo实例请参考第6章

message定义

message定义一个数据结构,类似于Python的类

message User{
  string name=1;       # 从1开始依次往后
  string password=2;   # 2
}

message 类名{
  类型 变量名=1; # 这里1是固定值,从1开始依次往后
  类型 变量名=2; # 这里1是固定值,从1开始依次往后
}

service定义

定义服务相对简单,通过关键字service配合关键字rpc可以进行服务定义

returns定义接口返回的数据类型

service MapService{
  rpc MapHello(Request) returns (Response){}
}

service 服务名{
  rpc 接口名(请求类型) returns (返回类型){}
}

pb转成源码作用解释

常见gRPC实战

将通过2个实际案例讲解复杂的pb定义

实战一:多种数据类型的复杂接口
  1. 第一步proto定义,
# all_demo.proto
syntax = "proto3";

service UserService{
  rpc GetUserByKeyWord(Request) returns(Response){}
}

message User{
  string name=1;
  int32 id=2;
  float age=3;
  bool isBoy=4;
}

message UserList{
  repeated User user=1;
}

message Response{
  UserList userList=1;
}

message Request{
  map<string,string> keyWord=1;
  bool fuzzy=2;
}

2.转成Python源码

python3 -m  grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. all_demo.proto

3.开发服务端

import grpc
from concurrent import futures

from grpc_demo.pb.all_demo_pb2_grpc import UserServiceServicer, add_UserServiceServicer_to_server
from grpc_demo.pb.all_demo_pb2 import Request, Response, User, UserList


class UserService(UserServiceServicer):

    def GetUserByKeyWord(self, request, context):
        user1 = User(name="zhangsan", id=1, age=24.3, isBoy=False)
        user2 = User(name="zhangsan1", id=1, age=24.3, isBoy=False)
        user_list = UserList(user=[user1, user2])
        response = Response(userList=user_list)
        return response


def serve():
    port = "50000"
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    add_UserServiceServicer_to_server(UserService(), server)
    server.add_insecure_port('[::]:' + port)
    server.start()
    print("Server started, listening on " + port)
    server.wait_for_termination()


if __name__ == '__main__':
    serve()

4.开发客户端

import grpc
from grpc_demo.pb.all_demo_pb2_grpc import UserServiceStub
from grpc_demo.pb.all_demo_pb2 import Request


def run():
    with grpc.insecure_channel("localhost:50000") as channel:
        stub = UserServiceStub(channel)
        key_word = {"name": "zhang"}
        request = Request(keyWord=key_word, fuzzy=False)
        resp = stub.GetUserByKeyWord(request)
        print(resp)


if __name__ == '__main__':
    run()

5.通过客户端调试

image.png

实战二:存在外部依赖的场景

1、定义第一个proto,map_demo.proto

这个proto定义了MapDemo的类

#map_demo.proto
syntax = "proto3";

message MapDemo{
  map<string, string> keyValue=1;
}

2、定义第二个proto,这个proto会引用第一个proto中定义的类

这个proto定义了接口,以及Request类,Request类引用了第一个proto中的MapDemo

syntax = "proto3";

import "map_demo.proto";

service MapService{
  rpc MapHello(Request) returns (Response){}
}

message Request{
  MapDemo mapDemo=1;
}

message Response{
  string name=1;
}

3、将map_demo.proto转换成python代码

这一步很重要,虽然map_demo是被use_map_demo引用,但也需要生成python代码,否则会报错

python3 -m  grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. map_demo.proto

4、将use_map_demo.proto转换成python代码

python3 -m  grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. map_use_demo.proto

注意:这行完成上述操作后会生成如下python代码

image.png

5、gRPC服务代码

from concurrent import futures

import grpc

from grpc_demo.pb.map_use_demo_pb2_grpc import MapServiceServicer, add_MapServiceServicer_to_server
from grpc_demo.pb.map_use_demo_pb2 import Response


class MapUseDemoServer(MapServiceServicer):

    def MapHello(self, request, context):
        print(request)
        return Response(name="hello map")


def serve():
    port = "40001"
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    add_MapServiceServicer_to_server(MapUseDemoServer(), server)
    server.add_insecure_port('[::]:' + port)
    server.start()
    print("Server started, listening on " + port)
    server.wait_for_termination()


if __name__ == '__main__':
    serve()

6、客户端代码

import grpc
from grpc_demo.pb.map_use_demo_pb2_grpc import MapServiceStub
from grpc_demo.pb.map_use_demo_pb2 import Request
from grpc_demo.pb.map_demo_pb2 import MapDemo

def run():
    with grpc.insecure_channel("localhost:40001") as channel:
        stub = MapServiceStub(channel)
        map_demo = MapDemo(keyValue={"hello": "a"})
        response = stub.MapHello(Request(mapDemo=map_demo))
        print(response.name)

if __name__ == '__main__':
    run()

7、启动服务端,通过客户端调用查看结果

客户端打印hello,表示调用成功

image.png

服务端打印request信息

image.png

常见错误

外部依赖没有转换

错误信息:ModuleNotFoundError: No module named ‘grpc_demo.pb.map_demo_pb2’

image.png 这个错误原因是use_map_demo.proto引用了map_demo.proto,但map_demo.proto没有转换成pyton代码导致引入失败

解决方案:将map_demo.proto转换成python代码

python3 -m  grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. map_demo.proto

客户端入参类型错误

ValueError: Protocol message MapDemo has no “xxxx” field.

image.png

错误演示:Request的参数应该是mapDemo类型,不是一个字典

image.png

正确做法:定义map_demo的对象,入参给Request

image.png

本文转自 https://juejin.cn/post/7210375037122101308,如有侵权,请联系删除。

---------------------------END---------------------------

题外话

当下这个大数据时代不掌握一门编程语言怎么跟的上脚本呢?当下最火的编程语言Python前景一片光明!如果你也想跟上时代提升自己那么请看一下.

在这里插入图片描述

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。


👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述

👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值