百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

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

connygpt 2024-09-29 11:04 13 浏览

作者: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. 定义消息类型
  
  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:此字段可以在格式良好的消息中重复任意次数(包括零)。将保留重复值的顺序。例如:

  
  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

相关推荐

3分钟让你的项目支持AI问答模块,完全开源!

hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...

干货|程序员的副业挂,12个平台分享

1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...

Github标星超200K,这10个可视化面板你知道几个

在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...

开箱即用的炫酷中后台前端开源框架第二篇

#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...

搭建React+AntDeign的开发环境和框架

搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...

基于.NET 5实现的开源通用权限管理平台

??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...

StreamPark - 大数据流计算引擎

使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...

教你使用UmiJS框架开发React

1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...

简单在线流程图工具在用例设计中的运用

敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...

【开源分享】神奇的大数据实时平台框架,让Flink&Spark开发更简单

这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...

聊聊规则引擎的调研及实现全过程

摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...

【开源推荐】Diboot 2.0.5 发布,自动化开发助理

一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...

微软推出Copilot Actions,使用人工智能自动执行重复性任务

IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...

Electron 使用Selenium和WebDriver

本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...

Quick 'n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具

一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...