Python使用grpc以及与C++进行互相调用

本文介绍了gRPC的安装与使用,包括使用pip安装相关组件,用protoc和gRPC插件生成Python代码,编写服务端和客户端代码。还阐述了Python和C++互相调用gRPC服务的方法,指出gRPC跨语言通讯效率高,适用于追求效率的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

安装grpc

使用pip安装 gRPC 和 Protocol Buffers (protobuf) 编译器(最好在虚拟环境下安装,防止干扰系统环境)

pip install grpcio grpcio-tools

在这里插入图片描述

使用grpc

  1. 使用 protoc(Protocol Buffers 编译器)和 gRPC 插件来从 .proto 文件生成 Python 代码。

    device.proto文件内容

    syntax = "proto3";
    
    package device_service;
    
    service DeviceService {
      // 返回一维字符串数组
      rpc GetDeviceStringList (DeviceNameListRequest) returns (DeviceNameListResponse) {}
      // 返回单个int值
      rpc GetDeviceSlaveCnt (DeviceSlaveCntRequest) returns (DeviceSlaveCntResponse) {}
      // 返回自定义结构体类型
      rpc GetDeviceInfo (DeviceInfoRequest) returns (DeviceInfoResponse) {}
      // 返回二维字符串数组
      rpc GetDeviceTableBySlaveId (DeviceTableBySlaveIdRequest) returns (DeviceTableBySlaveIdResponse) {}
    }
    
    // 设备列表信息
    message DeviceNameListRequest {
      string device_name = 1;
    }
    
    message DeviceNameListResponse {
      repeated string device_names = 1;
    }
    
    message DeviceInfoDetail {
      string ip = 1;
      int32 port = 2;
      string type = 3;
      bool server_status = 4;
      bool simulate_status = 5;
      bool plan_status = 6;
    }
    
    // 设备详细信息
    message DeviceInfoRequest {
      // 查询的设备名称
      string device_name = 1;
    }
    
    message DeviceInfoResponse {
      DeviceInfoDetail info = 1;
    }
    
    // 获取设备从机数量
    message DeviceSlaveCntRequest {
      string device_name = 1;
    }
    
    message DeviceSlaveCntResponse {
      int32 slave_cnt = 1;
    }
    
    // 根据设备名称和从机id和测点信息 获取设备详细信息,回复是二维List
    message DeviceTableBySlaveIdRequest {
      string device_name = 1;
      int32 slave_id = 2;
      string point_name = 3;
    }
    
    message DeviceTableRow{
      repeated string row = 1;
    }
    
    message DeviceTableBySlaveIdResponse {
      DeviceTableRow head_data = 1;
      repeated DeviceTableRow table_data = 2;
    }
    

    在命令行中运行以下命令:

    python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. device.proto
    

    在这里插入图片描述

  2. 编写服务端和客户端代码

    编写服务端代码

    1. 检查设备是否存在

      def checkDevice(self, request, context) -> bool:
          device = self.device_map.get(request.device_name)
          if device is None:
              # 处理设备不存在的情况,例如返回一个错误消息或抛出一个自定义异常
              context.set_code(grpc.StatusCode.NOT_FOUND)
              context.set_details(f'未找到{request.device_name}设备')
              print(f'未找到{request.device_name}设备')
              return False
          else:
              return True
      
    2. 返回一维字符串列表

      proto文件内容

      service DeviceService {
        // 返回一维字符串数组
        rpc GetDeviceStringList (DeviceNameListRequest) returns (DeviceNameListResponse) {}
      }
      
      // 设备列表信息
      message DeviceNameListRequest {
        string device_name = 1;
      }
      
      message DeviceNameListResponse {
        repeated string device_names = 1;
      }
      

      python服务端代码

      def GetDeviceStringList(self, request, context):
          device_name_list = ["PCS1", "PCS2", "BMS1", "BMS2"]
          return device_pb2.DeviceNameListResponse(device_names=device_name_list)
      
    3. 返回单个int类型

      proto文件内容

      service DeviceService {
        // 返回单个int值
        rpc GetDeviceSlaveCnt (DeviceSlaveCntRequest) returns (DeviceSlaveCntResponse) {}
      }
      
      // 获取设备从机数量
      message DeviceSlaveCntRequest {
        string device_name = 1;
      }
      
      message DeviceSlaveCntResponse {
        int32 slave_cnt = 1;
      }
      

      python服务端代码

      def GetDeviceSlaveCnt(self, request, context):
          if not self.checkDevice(request, context):
              return device_pb2.DeviceSlaveCntResponse(slave_cnt=0)
          try:
              # 创建并返回响应
              response = device_pb2.DeviceSlaveCntResponse(slave_cnt=1)
              return response
          except Exception as e:
              print(e)
              return device_pb2.DeviceSlaveCntResponse(slave_cnt=0)
      
    4. 返回自定义结构体类型

      proto文件内容

      service DeviceService {
        // 返回自定义结构体类型
        rpc GetDeviceInfo (DeviceInfoRequest) returns (DeviceInfoResponse) {}
      }
      
      message DeviceInfoDetail {
        string ip = 1;
        int32 port = 2;
        string type = 3;
        bool server_status = 4;
        bool simulate_status = 5;
        bool plan_status = 6;
      }
      
      // 设备详细信息
      message DeviceInfoRequest {
        // 查询的设备名称
        string device_name = 1;
      }
      
      // 返回值是自定义结构体类型DeviceInfo
      message DeviceInfoResponse {
        DeviceInfoDetail info = 1;
      }
      

      python服务端代码

      def GetDeviceInfo(self, request, context):
          if not self.checkDevice(request, context):
              return device_pb2.DeviceInfoResponse()
          try:
              info_detail = device_pb2.DeviceInfoDetail(
                  ip="127.0.0.1",
                  port=502,
                  type="tcp",
                  server_status=True,
                  simulate_status=True,
                  plan_status=False
              )
              # 创建并返回响应
              response = device_pb2.DeviceInfoResponse(info=info_detail)
              return response
          except Exception as e:
              print(e)
              return device_pb2.DeviceInfoResponse()
      
    5. 返回二维字符串列表

      proto文件内容

      service DeviceService {
        // 返回二维字符串数组
        rpc GetDeviceTableBySlaveId (DeviceTableBySlaveIdRequest) returns (DeviceTableBySlaveIdResponse) {}
      }
      
      // 根据设备名称和从机id和测点信息 获取设备详细信息,回复是二维List
      message DeviceTableBySlaveIdRequest {
        string device_name = 1;
        int32 slave_id = 2;
        string point_name = 3;
      }
      
      message DeviceTableRow{
        repeated string row = 1;
      }
      
      message DeviceTableBySlaveIdResponse {
        DeviceTableRow head_data = 1;
        repeated DeviceTableRow table_data = 2;
      }
      

      python服务端代码

      def GetDeviceTableBySlaveId(self, request, context):
          if not self.checkDevice(request, context):
              return device_pb2.DeviceTableBySlaveIdResponse(head_data=device_pb2.DeviceTableRow(row=[]),
                                                              table_data=[device_pb2.DeviceTableRow(row=[])])
          try:
              # 创建并返回响应
              head_data = ["设备名称","设备类型","设备IP","设备端口","设备状态","模拟状态","计划状态"]
              table_data = [["PCS1", "tcp", "127.0.0.1", "502", "true", "true", "false"],
                              ["PCS2", "tcp", "127.0.0.1", "503", "true", "true", "false"],
                              ["BMS1", "tcp", "127.0.0.1", "504", "true", "true", "false"],
                              ["BMS2", "tcp", "127.0.0.1", "505", "true", "true", "false"]]
              # 封装成rpc格式
              head_row = device_pb2.DeviceTableRow(row=head_data)
              response = device_pb2.DeviceTableBySlaveIdResponse(
                  head_data=head_row,
                  table_data=[device_pb2.DeviceTableRow(row=row_data) for row_data in table_data]
              )
              return response
          except Exception as e:
              print(e)
              return device_pb2.DeviceTableBySlaveIdResponse(head_data=device_pb2.DeviceTableRow(row=[]),
                                                              table_data=[device_pb2.DeviceTableRow(row=[])])
      
    6. 启动服务端

      def serve():
          server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
          device_pb2_grpc.add_DeviceServiceServicer_to_server(DeviceServiceServicer(), server)
          server.add_insecure_port('[::]:50051')	# 端口号可以自定义
          server.start()
          server.wait_for_termination()
      
      if __name__ == '__main__':
          serve()
      

    编写客户端代码

    1. 初始化客户端类

      class DeviceServiceClient:
          def __init__(self, channel_address='localhost:50051'):
              self.channel = grpc.insecure_channel(channel_address)
              self.stub = device_pb2_grpc.DeviceServiceStub(self.channel)
      
    2. 获取一维字符串列表

      def getDeviceStringList(self) -> List[str]:
          device_names_response = self.stub.GetDeviceStringList(device_pb2.DeviceNameListRequest())
          device_names = [name for name in device_names_response.device_names]
          return device_names
      
    3. 获取单个int值

      def getDeviceSlaveCnt(self, device_name: str) -> int:
          device_slave_cnt_response = self.stub.GetDeviceSlaveCnt(
              device_pb2.DeviceSlaveCntRequest(device_name=device_name))
          slave_cnt = device_slave_cnt_response.slave_cnt
          return slave_cnt
      
    4. 获取自定义结构体信息

      def getDeviceInfo(self, device_name: str) -> Dict:
          device_info_response = self.stub.GetDeviceInfo(device_pb2.DeviceInfoRequest(device_name=device_name))
          device_info_dict = {
              "ip": device_info_response.info.ip,
              "port": device_info_response.info.port,
              "type": device_info_response.info.type,
              "server_status": device_info_response.info.server_status,
              "simulate_status": device_info_response.info.simulate_status,
              "plan_status": device_info_response.info.plan_status
          }
          return device_info_dict
      
    5. 获取二维字符串列表

      def getDeviceTableBySlaveId(self, device_name: str, slave_id: int, point_name: str = "") -> Dict:
          response = self.stub.GetDeviceTableBySlaveId(
              device_pb2.DeviceTableBySlaveIdRequest(device_name=device_name, slave_id=slave_id, point_name=point_name))
          head_data = [head for head in response.head_data.row]
          # 处理响应中的二维数组数据
          table_data = []
          for value in response.table_data:
              row_data = [item for item in value.row]
              table_data.append(row_data)
              data_dict = {
                  "head_data": head_data,
                  "table_data": table_data
              }
              return data_dict
      
    6. 关闭通道

      def close(self):
          self.channel.close()
      
    7. 启动客户端

      if __name__ == "__main__":
          client = DeviceServiceClient()
      
          device_name_list = client.getDeviceStringList()
          print(device_name_list)
      
          device_info_dict = client.getDeviceInfo("PCS1")
          print(device_info_dict)
      
          slave_cnt = client.getDeviceSlaveCnt("PCS1")
          print(slave_cnt)
      
          table_data_dict = client.getDeviceTableBySlaveId("PCS1", 1, "")
          print(table_data_dict)
      
          client.close()
      
  3. grpc服务端和客户端通信效果展示
    在这里插入图片描述

Python和C++互相调用grpc服务

项目源码仓库地址

https://gitee.com/chen-dongyu123/grpc_example

  1. python和c++需要共同使用同一份.proto文件,生产各自的grpc服务端和客户端代码

  2. 根据需求确定python和c++谁当客户端和服务端,编写完成各自的服务端和客户端代码

    这里是我的一个例子

    python当服务端,c++当客户端(c++使用grpc的例子可以看我上一篇文章)

    在这里插入图片描述

    c++当服务端,python当客户端
    在这里插入图片描述

总结

python中使用grpc就比在c++简单多了,我们可以使用grpc轻松的跨语言通讯,相比传统的webserver通讯,grpc的效率更高。对于追求效率的场景下,我们可以使用c++编写,然后业务方面我们可以使用Java或者python。我现在遇到的一个场景就是需要使用modbus实时采集数据,然后将数据传递给web后台或者客户端,对于这种场景,grpc就比webserver优势大多了。

<think>嗯,用户想了解如何使用Python调用gRPC服务来获取数据的示例教程。我需要先回忆一下gRPC的基本概念和流程。首先,gRPC使用Protocol Buffers作为接口定义语言,所以用户需要先定义.proto文件,然后生成对应的Python代码。接着,编写服务端和客户端代码。客户端部分需要创建通道,调用生成的存根方法,处理响应。 用户提供的引用中,引用[2]提到了Python客户端传输CSV文件的例子,这可能对用户有帮助。需要根据这个引用,整理出一个步骤性的教程。另外,用户可能需要安装必要的库,比如grpcio、protobuf和grpcio-tools,这些在引用[2]中也有提到。 接下来,我需要确认用户是否已经了解proto文件的定义,或者是否需要提供一个完整的示例。比如,定义一个简单的服务,包含一个获取数据的方法。然后,展示如何生成代码,编写客户端代码,处理请求和响应。同时,注意错误处理和连接管理,比如使用with语句确保通道关闭。 还要注意引用格式,在回答中适当添加引用标识,比如引用[2]中的安装步骤和示例结构。可能用户会遇到Python版本的问题,比如引用[1]中提到Python 2和3的环境问题,但这里用户的问题是关于Python调用gRPC,而引用[2]中的示例使用的是Python 3,所以需要确认代码兼容性。 最后,生成相关问题,可能涉及性能优化、错误处理、REST的比较等,参考其他引用内容,比如引用[3]提到RESTGraphQL的比较,可以生成相关问题关于gRPCREST的选择。</think>以下是如何使用Python调用gRPC服务获取数据的完整步骤说明,包含代码示例和关键注意事项: ### 一、环境准备 1. 安装必要依赖(需Python 3.5+) ```bash pip install grpcio grpcio-tools protobuf ``` 引用[2]中已验证该安装方式的有效性[^2] ### 二、proto文件定义 创建`data_service.proto`文件: ```protobuf syntax = "proto3"; service DataService { rpc GetDataset(DatasetRequest) returns (DatasetResponse) {} } message DatasetRequest { string query = 1; int32 page_size = 2; } message DatasetResponse { repeated bytes records = 1; string status = 2; } ``` ### 三、生成代码桩 执行生成命令: ```bash python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. data_service.proto ``` 将生成`data_service_pb2.py`(数据结构)和`data_service_pb2_grpc.py`(服务桩) ### 四、客户端实现 ```python import grpc import data_service_pb2 import data_service_pb2_grpc def fetch_data(): # 创建安全通道(根据服务端配置选择加密方式) with grpc.insecure_channel('localhost:50051') as channel: stub = data_service_pb2_grpc.DataServiceStub(channel) # 构建请求对象 request = data_service_pb2.DatasetRequest( query="sales_data", page_size=100 ) # 发起RPC调用 response = stub.GetDataset(request) # 处理响应数据 if response.status == "SUCCESS": for record in response.records: process_data(record) # 自定义数据处理函数 return True return False def process_data(binary_data): # 实现具体的数据转换逻辑 # 例如:将bytes转换为pandas DataFrame pass if __name__ == '__main__': fetch_data() ``` ### 五、关键配置说明 1. **连接安全**: - 生产环境应使用SSL/TLS加密: ```python credentials = grpc.ssl_channel_credentials() channel = grpc.secure_channel('myservice.example.com:443', credentials) ``` 2. **超时控制**: ```python response = stub.GetDataset(request, timeout=10) # 10秒超时 ``` 3. **元数据传递**: ```python metadata = [('auth-token', 'my_secret_token')] response = stub.GetDataset(request, metadata=metadata) ``` ### 六、调试工具推荐 1. **grpcurl**:用于测试gRPC服务的命令行工具 2. **BloomRPC**:图形化gRPC调试工具(类似Postman)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MC皮蛋侠客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值