当前位置: 首页 > 工具软件 > gRPC-DLang > 使用案例 >

grpc简介及python grpc

华景同
2023-12-01

grpc

gRPC 一开始由 google 开发,开源的远程过程调用(RPC)系统

grpc 支持同步和异步调用
简单模式下, 调用函数就像本地调用一样, 直接传输数据
流式调用将信息以数据量的方式,发送或者接受,

gRPC 客户端调用成功的判断是独立的本地的, 因此可能存在客户端与服务端判断不一致的情况
例如,您可以在服务器端成功完成RPC,但在客户端失败。在客户端发送所有请求之前,服务器也可以决定完成

grpc是基于HTTP2协议的封装
gRPC 请求和应答消息流中一般的消息顺序:

请求 → 请求报头 *有定界符的消息 EOS
应答 → 应答报头 *有定界符的消息 EOS
应答 → (应答报头 *有定界符的消息 跟踪信息) / 仅仅跟踪时


proto文件

第一步是定义要在proto文件中序列化的数据的结构:这是一个带.proto扩展名的普通文本文件。协议缓冲区数据被构造为 消息,其中每个消息是包含一系列称为字段的名称 - 值对的信息的逻辑记录

message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}

二.定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型

相当于定义一个接口类型的服务

gRPC 默认使用 protocol buffers 作为接口定义语言,来描述服务接口和有效载荷消息结构

定义:

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  required string greeting = 1;
}

message HelloResponse {
  required string reply = 1;
}

两种类型调用

  1. 简单模式:
rpc SayHello(HelloRequest) returns (HelloResponse){
}
  1. 流式调用
  2. 服务端支持流式
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
  1. 客户端支持流式:
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
  1. 双向流式:双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照自己喜欢的顺序进行读写:例如,服务器可以在写入响应之前等待接收所有客户端消息,或者它可以交替地读取消息然后写入消息,或者其他一些读写组合
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}

超时:

grpc允许设置请求响应超时时间:
参数为: DEADLINE_EXCEEDED

安全认证:
grpc支持 SSL/TLS
支持OAuth 2.0


python:

备注: 示例可以参考 https://github.com/grpc/grpc/tree/master/examples/python

安装:
pip install grpcio

pip install grpcio-tools   # 安装grpc工具

pip install grpcio-reflection  #grpc-cli 调试工具

定义proto:
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  
  rpc Square(Number) returns (Number) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

message Number {
    float value = 1;
}


service 定义服务, 实现接口功能定义
rpc xxx 为具体接口方法

编译

定义完成后, 编译转换为python代码:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./rpc.proto

python_out和grpc_python_out 指定转换服务端与客户端的输出文件

rpc.proto 指定proto文件

命令生成了两个文件rpc_pb2.py 和rpc_pb2_grpc.py

实现server端:
from concurrent import futures
import logging
import math
import grpc
import time
import new_pb2
import new_pb2_grpc


def square(x):
  return math.sqrt(x)


class Greeter(new_pb2_grpc.GreeterServicer):
  def SayHello(self, request, context):
    return new_pb2.HelloReply(message='Hello, %s!' % request.name)

  def Square(self, request, context):
    x = square(request.value)
    return new_pb2.Number(value=x)


def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  new_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
  server.add_insecure_port('[::]:50051')
  server.start()
  try:
    while True:
      time.sleep(1)
  except:
    print "exit"
    # server.wait_for_termination()


if __name__ == '__main__':
  logging.basicConfig()
  serve()


实现client端:
from __future__ import print_function
import logging

import grpc

import new_pb2
import new_pb2_grpc


def run():
  # NOTE(gRPC Python Team): .close() is possible on a channel and should be
  # used in circumstances in which the with statement does not fit the needs
  # of the code.
  with grpc.insecure_channel('localhost:50051') as channel:
    stub = new_pb2_grpc.GreeterStub(channel)
    response = stub.SayHello(new_pb2.HelloRequest(name='you'))
    print("Greeter client received: " + response.message)

    res = stub.Square(new_pb2.Number(value=231))
    print("client Square 231 get :" + str(res.value))


if __name__ == '__main__':
  logging.basicConfig()
  run()


输出:

Greeter client received: Hello, you!
client Square 231 get :15.1986837387
文件操作

文件操作, 适用于小文件读写:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.mux";
option java_outer_classname = "muxsProto";
option objc_class_prefix = "HLW";

package muxs;

service FileController {
  rpc Read (Files) returns (Content) {}
  
  rpc Write(Files) returns (Status) {}
}

message  Content {
  string  text = 1;
}

message Files {
    string filename = 1;
	string text = 2;
}

message Status {
    int64 code = 1;
}


在server端实现:

class FileController(mux_pb2_grpc.FileControllerServicer):
  def Read(self, request, context):
    result = ""
    filename = request.filename
    try:
      with open(filename) as fd:
        for line in fd.readlines():
          result += line
      return mux_pb2.Content(text=result)
    except:
      return mux_pb2.Content(text="Error: read failed")

  def Write(self, request, context):
    filename = request.filename
    file_contxt = request.text
    try:
      with open(filename, "wb") as fd:
        fd.write(file_contxt)
        fd.flush()
      return mux_pb2.Status(code=0)
    except:
      return mux_pb2.Status(code=1)

serve

def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  mux_pb2_grpc.add_FileControllerServicer_to_server(FileController(), server)
  server.add_insecure_port('[::]:50051')
  server.start()
  try:
    while True:
      time.sleep(1)
  except:
    print "exit"

client 客户端实现调用:

def run():

  channel = grpc.insecure_channel('localhost:50051')
  stub = mux_pb2_grpc.GreeterStub(channel)

  stub2 = mux_pb2_grpc.FileControllerStub(channel)
  res2 = stub2.Read(mux_pb2.Files(filename="aaaa.txt", text=""))
  print("File Read: " + res2.text)

  res3 = stub2.Write(mux_pb2.Files(filename="aaaa2.txt", text="37183714ghgjh243"))
  print("File Write: " + str(res3.code))

  res2 = stub2.Read(mux_pb2.Files(filename="aaaa2.txt", text=""))
  print("File Read: " + res2.text)


结果:

File Read: Error: read failed
File Write: 0
File Read: 37183714ghgjh243

可以将两个服务合并, 一起定义在proto文件中:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.mux";
option java_outer_classname = "muxsProto";
option objc_class_prefix = "HLW";

package muxs;


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


service FileController {
  rpc Read (Files) returns (Content) {}
  
  rpc Write(Files) returns (Status) {}
}


message HelloRequest {
  string name = 1;
}


message HelloReply {
  string message = 1;
}

message Number {
    float value = 1;
}

message  Content {
  string  text = 1;
}

message Files {
    string filename = 1;
	string text = 2;
}

message Status {
    int64 code = 1;
}


 类似资料: