百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Python——gRPC详解及实战避坑方案(上)

ccwgpt 2024-10-13 01:31 53 浏览 0 评论

作者:henry_czh(经作者授权转载,勿二次转载)

来源:https://juejin.im/post/6854573212018147336

前言

什么是RPC服务 RPC,是Remote Procedure Call的简称,翻译成中文就是远程过程调用。RPC就是允许程序调用另一个地址空间(通常是另一台机器上)的类方法或函数的一种服务。 它是一种架设在计算机网络之上并隐藏底层网络技术,可以像调用本地服务一样调用远端程序,在编码代价不高的情况下提升吞吐的能力。

为什么要使用RPC服务 随着计算机技术的快速发展,单台机器运行服务的方案已经不足以支撑越来越多的网络请求负载,分布式方案开始兴起,一个业务场景可以被拆分在多个机器上运行,每个机器分别只完成一个或几个的业务模块。为了能让其他机器使用某台机器中的业务模块方法,就有了RPC服务,它是基于一种专门实现远程方法调用的协议上完成的服务。现如今很多主流语言都支持RPC服务,常用的有Java的Dubbo、Go的net/rpc & RPCX、谷歌的gRPC等。

关于gRPC 大部分RPC都是基于socket实现的,可以比http请求来的高效。gRPC是谷歌开发并开源的一款实现RPC服务的高性能框架,它是基于http2.0协议的,目前已经支持C、C++、Java、Node.js、Python、Ruby、Objective-C、PHP和C#等等语言。要将方法调用以及调用参数,响应参数等在两个服务器之间进行传输,就需要将这些参数序列化,gRPC采用的是protocol buffer的语法(检查proto),通过proto语法可以定义好要调用的方法、和参数以及响应格式,可以很方便地完成远程方法调用,而且非常利于扩展和更新参数。

快速上手gRPC

使用gRPC实现远程方法调用之前,我们需要了解protocol buffer语法,安装支持protocol buffer语法编译成.proto文件的工具,然后再完成gRPC的服务端(远程方法提供者)和客户端(调用者)的搭建和封装。

了解protocol buffer

Protocol Buffer是Google的跨语言,跨平台,可扩展机制的,用于序列化结构化数据 - 对比XML,但更小,更快,更简单的一种数据格式。您可以定义数据的结构化,例如方法的名字、参数和响应格式等,然后可以使用对应的语言工具生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。

语法使用

  1. 定义消息类型
Bash
  
  package test;
  syntax = "proto3";
  
  message SearchRequest {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
  }
  复制代码

上面的例子就是一个.proto文件,该文件的第一行指定包名,方便您在别的proto文件中import这个文件的定义,第二行是您正在使用proto3语法:如果您不这样做,protobuf 编译器将假定您正在使用proto2。这必须是文件的第一个非空的非注释行,目前建议使用proto3语法。 SearchRequest是消息体的名字,指定了三个字段,分别指定了字段的类型和顺序,顺序必须从1开始,并且不可重复;

  1. 指定字段规则 消息字段可以是以下之一:

单数(默认):格式良好的消息可以包含该字段中的零个或一个(但不超过一个)。 repeated:此字段可以在格式良好的消息中重复任意次数(包括零)。将保留重复值的顺序。例如:

Bash
  
  syntax = "proto3";
  ?
  message SearchRequest {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
    repeated Body body = 4;
  }
  ?
  message Body {
    int32 id = 1;
    string number = 2;
  }
  复制代码

上述例子其实就是定义了一个格式,用我们通常的json格式表示就是:

  
  {
      "query": str,
      "page_number":int,
      "result_per_page":int,
      "body":[
          {
              "id":int,
              "number":str
          }
      ],
  }
  复制代码
  1. 标量值类型 标量消息字段可以具有以下类型之一 - 该表显示.proto文件中指定的类型,以及自动生成的类中的相应类型:

.proto Type备注Python Typdoublefloatfloatfloatint32使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代intuint32使用变长编码int/longuint64使用变长编码int/longsint32使用变长编码,这些编码在负值时比int32高效的多intsint64使用变长编码,有符号的整型值。编码时比通常的int64高效。int/longfixed32总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。intfixed64总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。int/longsfixed32总是4个字节intsfixed64总是8个字节int/longbool布尔值boolstring一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。str/unicodebytes可能包含任意顺序的字节数据。str

  1. 默认值 解析消息时,如果编码消息不包含特定的单数元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:
  • 对于字符串,默认值为空字符串。
  • 对于字节,默认值为空字节。
  • 对于bools,默认值为false。
  • 对于数字类型,默认值为零。
  • 对于枚举,默认值是第一个定义的枚举值,该值必须为0。
  • 重复字段的默认值为空(通常是相应语言的空列表)
  1. 枚举类型
  
  message SearchRequest {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
    enum Corpus {
      UNIVERSAL = 0;
      WEB = 1;
      IMAGES = 2;
      LOCAL = 3;
      NEWS = 4;
      PRODUCTS = 5;
      VIDEO = 6;
    }
    Corpus corpus = 4;
  }
  复制代码

Corpus枚举的第一个常量映射为零:每个枚举定义必须包含一个映射到零的常量作为其第一个元素。这是因为:

  • 必须有一个零值,以便我们可以使用0作为数字默认值。
  • 零值必须是第一个元素,以便与proto2语义兼容,其中第一个枚举值始终是默认值。
  1. 定义方法
  
  service SearchService {
    rpc Search(SearchRequest)returns(SearchResponse);
  }
  复制代码

上面的语句就定义好了远程调用的方法名Search,待编译好对应语言的源代码之后就可以使用远程调用,例如在Python中初始化SearchService方法,则执行Search方法,就是采用SearchRequest的格式去调用远程机器的方法,然后按定义好的SearchResponse格式返回调用结果。根据proto的语法定义,甚至可以实现跨平台,跨语言使用这种远程调用。

使用工具生成对应语言的源代码

根据实际工作需要,生成以下对应语言的自定义消息类型Java,Python,C ++,Go, Ruby, Objective-C,或C#的.proto文件,你需要运行protobuf 编译器protoc上.proto。如果尚未安装编译器,请下载该软件包并按照自述文件中的说明进行操作。 Protobuf 编译器的调用如下:

  
  protoc --proto_path = IMPORT_PATH --cpp_out = DST_DIR --java_out = DST_DIR --python_out = DST_DIR --go_out = DST_DIR --ruby_out = DST_DIR --objc_out = DST_DIR --csharp_out = DST_DIR  path / to / file .proto
  ?
  复制代码

Python生成对应的源代码

  1. 安装Python的gRPC源码包grpcio,用于执行gRPC的各种底层协议和请求响应方法
  2. 安装Python基于gRPC的proto生成python源代码的工具grpcio-tools
  
  sudo python -m pip install grpcio
  ?
  python -m pip install grpcio-tools
  复制代码
  1. 执行编译生成python的proto序列化协议源代码:
  
  # 编译 proto 文件
  python -m grpc_tools.protoc --python_out=.  --grpc_python_out=.  -I. test.proto
  ?
  python -m grpc_tools.protoc: python 下的 protoc 编译器通过 python 模块(module) 实现, 所以说这一步非常省心
  --python_out=. : 编译生成处理 protobuf 相关的代码的路径, 这里生成到当前目录
  --grpc_python_out=. : 编译生成处理 grpc 相关的代码的路径, 这里生成到当前目录
  -I. test.proto : proto 文件的路径, 这里的 proto 文件在当前目录
  复制代码

编译后生成的源代码:

  • test_pb2.py: 用来和 protobuf 数据进行交互,这个就是根据proto文件定义好的数据结构类型生成的python化的数据结构文件
  • test_pb2_grpc.py: 用来和 grpc 进行交互,这个就是定义了rpc方法的类,包含了类的请求参数和响应等等,可用python直接实例化调用

搭建Python gRPC服务

生成好了python可以直接实例化和调用的gRPC类,我们就可以开始搭建RPC的服务端(远程调用提供者)和客户端(调用者)了。

  1. 搭建服务端server.py
  
  from concurrent import futures
  import time
  import grpc
  import test_pb2
  import test_pb2_grpc
  ?
  # 实现 proto 文件中定义的 SearchService
  class RequestRpc(test_pb2_grpc.SearchService):
      # 实现 proto 文件中定义的 rpc 调用
      def doRequest(self, request, context):
          return test_pb2.Search(query = 'hello {msg}'.format(msg = request.name)) # return的数据是符合定义的SearchResponse格式
  ?
  def serve():
      # 启动 rpc 服务,这里可定义最大接收和发送大小(单位M),默认只有4M
      server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), options=[
          ('grpc.max_send_message_length', 100 * 1024 * 1024),
          ('grpc.max_receive_message_length', 100 * 1024 * 1024)])
      
      test_pb2_grpc.add_SearchServiceServicer_to_server(RequestRpc(), server)
      server.add_insecure_port('[::]:50051')
      server.start()
      try:
          while True:
              time.sleep(60*60*24) # one day in seconds
      except KeyboardInterrupt:
          server.stop(0)
  ?
  if __name__ == '__main__':
      serve()
  复制代码
  1. 搭建客户端client.py
  
  import grpc
  import helloworld_pb2
  import helloworld_pb2_grpc
  ?
  def run():
      # 连接 rpc 服务器
      channel = grpc.insecure_channel('localhost:50051')
      # 调用 rpc 服务
      stub = test_pb2_grpc.SearchServiceStub(channel)
      response = stub.doRequest(test_pb2.SearchRequest(query='henry'))
      print("client received: ", response)
  ?
  if __name__ == '__main__':
      run()
  复制代码

最佳实践

  1. 编写proto文件的时候,注意定义好数据的格式,要多考虑可扩张性,例如可以定义api_version等用于区分版本,防止未来的版本有大的数据格式更新的时候可以兼容;
  2. 对于不可变类型,建议使用枚举,例如请求一个字段type,取值是固定的时候,可以用枚举类型;
  3. 对于服务端和客户端的编写,建议指定好最大接收和发送大小,避免出现数据溢出的异常;
  4. gRPC偶尔会出现断线重连的情况,所以要增加异常处理机制,捕获到由于重连时引发远程调用失败的问题,则可以执行重试(会在接下来的文章中详细说明);
  5. gRPC可以采用SSL或TLS的协议,实现http2.0加密传输,提高系统的安全性(会在接下来的文章中详细说明);
  6. 对于流量、并发较大的服务,可以通过微服务的一些应用或组件(如istio)等实现流量的熔断、限流等等,提高稳定性。

gRPC的优势

性能

gRPC消息使用一种有效的二进制消息格式protobuf进行序列化。Protobuf在服务器和客户机上的序列化非常快。Protobuf序列化后的消息体积很小,能够有效负载,在移动应用程序等有限带宽场景中显得很重要。

gRPC是为HTTP/2而设计的,它是HTTP的一个主要版本,与HTTP 1.x相比具有显著的性能优势:

  • 二进制框架和压缩。HTTP/2协议在发送和接收方面都很紧凑和高效。
  • 通过单个TCP连接复用多个HTTP/2调用。多路复用消除了线头阻塞。

代码生成

所有gRPC框架都为代码生成提供了一流的支持。gRPC开发的核心文件是*.proto文件 ,它定义了gRPC服务和消息的约定。根据这个文件,gRPC框架将生成服务基类,消息和完整的客户端代码。

通过在服务器和客户端之间共享*.proto文件,可以从端到端生成消息和客户端代码。客户端的代码生成消除了客户端和服务器上的重复消息,并为您创建了一个强类型的客户端。无需编写客户端代码,可在具有许多服务的应用程序中节省大量开发时间。

严格的规范

不存在具有JSON的HTTP API的正式规范。开发人员不需要讨论URL,HTTP动词和响应代码的最佳格式。(想想,是用Post还是Get好?使用Get还是用Put好?一想到有选择恐惧症的你是不是又开了纠结,然后浪费了大量的时间)

该gRPC规范是规定有关gRPC服务必须遵循的格式。gRPC消除了争论并节省了开发人员的时间,因为gPRC在各个平台和实现之间是一致的。

HTTP/2为长期的实时通信流提供了基础。gRPC通过HTTP/2为流媒体提供一流的支持。

gRPC服务支持所有流组合:

  • 一元(没有流媒体)
  • 服务器到客户端流
  • 客户端到服务器流
  • 双向流媒体 截至时间/超时和取消 gRPC允许客户端指定他们愿意等待RPC完成的时间。该期限被发送到服务端,服务端可以决定在超出了限期时采取什么行动。例如,服务器可能会在超时时取消正在进行的gRPC / HTTP /数据库请求。

通过子gRPC调用截至时间和取消操作有助于实施资源使用限制。

推荐使用gRPC的场景

  • 微服务 - gRPC设计为低延迟和高吞吐量通信。gRPC非常适用于效率至关重要的轻型微服务。 点对点实时通信 - gRPC对双向流媒体提供出色的支持。gRPC服务可以实时推送消息而无需轮询。 多语言混合开发环境 - gRPC工具支持所有流行的开发语言,使gRPC成为多语言开发环境的理想选择。
  • 网络受限环境 - 使用Protobuf(一种轻量级消息格式)序列化gRPC消息。gRPC消息始终小于等效的JSON消息。

参考文献

  1. https://juejin.im/post/6844903687089831944
  2. https://doc.oschina.net/grpc?t=58008
  3. https://juejin.im/post/6844903794350751757
  4. https://www.jianshu.com/p/43fdfeb105ff

相关推荐

想快速上手Python网络爬虫?这份实战指南你不能错过!

以下是关于Python网络爬虫实战的详细指南,涵盖基础知识、常用工具、实战案例及注意事项:一、爬虫基础概念1.什么是网络爬虫?o通过自动化程序从网页上抓取并提取数据的工具。o核心步骤:请求网...

python爬虫怎么副业接单

其实这个问题也挺重要的,花了时间花了经历去学了python爬虫,本想靠着这个技能去补贴家用或者挣点零花钱,但是发现有时候的单子是自己力所不能及的,有的东西真的是不会,又或者不知从何下手。那么这篇文章主...

用Python写了一个图像文字识别OCR工具

人生苦短,快学Python!在之前的文章里,我们多次尝试用Python实现文本OCR识别!今天我们要搞一个升级版:直接写一个图像文字识别OCR工具!引言最近在技术交流群里聊到一个关于图像文字识别的...

taskPyro:为 Python 任务与爬虫插上自动化翅膀的开源利器

在数据驱动的时代,无论是数据采集、ETL流程,还是定期的系统维护脚本,高效、可靠的任务调度成为了许多开发者和运维人员的刚需。特别是对于Python开发者而言,如何优雅地管理和调度日益增多的爬虫任...

网络爬虫:Python动态网页爬虫2种技术方式及示例

作者:糖甜甜甜https://mp.weixin.qq.com/s/5Dwh5cbfjpDfm_FRcpw1Ug这一讲,我将会为大家讲解稍微复杂一点的爬虫,即动态网页的爬虫。动态网页技术介绍动态网页爬...

30个小时搞定Python网络爬虫(全套详细版)

【课程介绍】适用人群1、零基础对Python网络爬虫感兴趣的学员2、想从事Python网络爬虫工程师相关工作的学员3、想学习Python网络爬虫作为技术储备的学员课程目标1、本课程的目标是将大家培养成...

python爬虫常用工具库总结

说起爬虫,大家可能第一时间想到的是python,今天就简单为大家介绍下pyhton常用的一些库。请求库:实现基础Http操作urllib:python内置基本库,实现了一系列用于操作url的功能。...

玛森:Python爬虫书籍推荐

  Python爬虫书籍推荐什么?玛森科技徐老师介绍,网络爬虫现在很火,不管业内人士或业外人士,大家对爬虫或多或少都有一些了解,网络爬虫通俗的讲,就是通过程序去互联网上面爬取想要的内容,并且爬取的过程...

如何入门python爬虫?

1.很多人一上来就要爬虫,其实没有弄明白要用爬虫做什么,最后学完了却用不上。大多数人其实是不需要去学习爬虫的,因为工作所在的公司里有自己的数据库,里面就有数据来帮助你完成业务分析。什么时候要用到爬虫呢...

爬虫修炼手册,Python爬虫学习入门Scrapy

爬虫就如同江湖中的神秘侠客,应运而生,成为了我们获取数据的得力助手。爬虫,正式名称是网络爬虫(WebCrawler),也被叫做网页蜘蛛、网络机器人,它是一段神奇的计算机代码,能够自动在互联网的信息...

如何入门 Python 爬虫?

1.很多人一上来就要爬虫,其实没有弄明白要用爬虫做什么,最后学完了却用不上。大多数人其实是不需要去学习爬虫的,因为工作所在的公司里有自己的数据库,里面就有数据来帮助你完成业务分析。什么时候要用到爬虫呢...

有了这4张思维导图,带你Python(爬虫)轻松入门

刚接触Python爬虫,该怎么学更有效?指南君给大家带来了这四张思维导图。非常适合刚开始学Python爬虫的同学用于回顾知识点、巩固学习情况等。话不多说,快来学习Python爬虫入门的最强干货吧!P...

python爬虫教程之爬取当当网 Top 500 本五星好评书籍

我们使用requests和re来写一个爬虫作为一个爱看书的你(说的跟真的似的)怎么能发现好书呢?所以我们爬取当当网的前500本好五星评书籍怎么样?ok接下来就是学习python的正确姿...

超实用!Python 在爬虫和自动化领域的 8 类工具与技术大盘点

Python在爬虫和自动化领域拥有丰富的工具库和框架,以下是一些常用工具和技术的分类整理,帮助你高效实现数据抓取和自动化任务:1.基础HTTP请求库oRequestso简洁的HTTP库...

学习Python的第四天之网络爬虫

30岁程序员学习Python的第四天之网络爬虫的Scrapy库Scrapy库的基本信息Scrapy库的安装在windows系统中通过管理员权限打开cmd。运行pipinstallscrapy即可安...

取消回复欢迎 发表评论: