什么是rpc?grpc又是什么?
什么是RPC
远程过程调用协议RPC (Remote Procedure Call Protocol)
 RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据
 举例:在 a服务内,调用b服务的ticket服务中的get方法,那么在a服务里,直接调用b.ticket.get()来直接调用
客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
什么是grpc
 gRPC(Google Remote Procedure Call)是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf (Protocol Buffers)序列化协议开发,且支持众多开发语言(python,golang,javascript,C,C++、Node.js、Ruby、Objective-C、PHP和C#等)。
 gRPC提供了一种简单的方法来精确地定义服务和为iOS、Android和后台支持服务自动生成可靠性很强的客户端功能库。客户端充分利用高级流和链接功能,从而有助于节省带宽、降低的TCP链接次数、节省CPU使用、和电池寿命。
| RESTful | gRPC | |
|---|---|---|
| Full Name | REpresentational State Transfer | Google Remote Procedure Call | 
| Payload | Readable Data (typically JSON) | Unreadable Binary Data (Protobuf) | 
| HTTP | HTTP 1.1/HTTP 2 | HTTP/2 | 
| Performance | Slower | Faster | 
| Type Safety | No | Yes | 
| Cross Language | Yes | Yes | 
| Client Setup | Required | Not required | 
| Supported Functions | GET/PUT/DELETE/POST/... | Any function | 

// Client端 
 //    Student student = Call(ServerAddr, addAge, student)
 1. 将这个调用映射为Call ID。
 2. 将Call ID,student(params)序列化,以二进制形式打包
 3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
 4. 等待服务器返回结果
 5. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新
// Server端
 1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
 2. 等待客户端请求
 3. 得到一个请求后,将其数据包反序列化,得到Call ID
 4. 通过在callIdMap中查找,得到相应的函数指针
 5. 将student(params)反序列化后,在本地调用addAge()函数,得到结果
 6. 将student结果序列化后通过网络返回给Client
什么是protobuf
 protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍
 具有跨语言性:python golang java c++ javascript等等等等
 生成python客户端和服务器
 
安装依赖
安装依赖:
pip install grpcio
pip install grpcio-tools
pip install protobuf
转化proto文件成client和server
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. wenet.proto// 文件名 hello.proto,proto版本
syntax = "proto3";
package hello;
// 定义服务
service Greeter {
  // 发送数据
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// 请求消息,包含用户的姓名。
message HelloRequest {
  string name = 1;
  string age = 2;
  string note=3;
}
// 响应消息,包含问候语。
message HelloReply {
  string message = 1;
}
在命令行输入:
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. hello.proto

gRPC客户端(client.py):
import grpc  # 导入gRPC库
import hello_pb2  # 导入生成的proto文件中的消息类型
import hello_pb2_grpc  # 导入生成的proto文件中的服务类型
def run():
    channel = grpc.insecure_channel('localhost:50051')  # 创建与服务器的通信通道,参数为服务器的地址和端口
    stub = hello_pb2_grpc.GreeterStub(channel)  # 创建与服务器的Stub对象,参数为通信通道
    response = stub.SayHello(hello_pb2.HelloRequest(name='John', age='25', note='Hello gRPC'))  # 调用服务器上的方法,参数为请求消息对象
    print("Response received from server: " + response.message)  # 打印服务器返回的响应消息
if __name__ == '__main__':
    run()  # 运行客户端代码
gRPC服务器(server.py):
import grpc  # 导入gRPC库
from concurrent import futures  # 导入concurrent.futures库,用于多线程处理
import hello_pb2  # 导入生成的proto文件中的消息类型
import hello_pb2_grpc  # 导入生成的proto文件中的服务类型
class GreeterServicer(hello_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):  # 实现proto文件中定义的方法,参数为请求消息对象和上下文对象
        message = 'Hello, ' + request.name + '! You are ' + request.age + ' years old.'  # 构造响应消息
        return hello_pb2.HelloReply(message=message)  # 返回响应消息对象
def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  # 创建gRPC服务器对象,参数为线程池的最大工作线程数
    hello_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server)  # 将服务实现添加到服务器中,参数为服务实现对象和服务器对象
    server.add_insecure_port('[::]:50051')  # 指定服务器监听的地址和端口,参数为地址和端口
    server.start()  # 启动服务器
    print("Server started. Listening on port 50051.")  # 打印服务器启动信息
    server.wait_for_termination()  # 等待服务器终止
if __name__ == '__main__':
    serve()  # 运行服务器代码
常用protobuf数据类型和pb文件
常用protobuf数据类型
| .proto Type | Notes | C++ Type | Java Type | Python Type[2] | Go Type | Ruby Type | 
|---|---|---|---|---|---|---|
| double | double | double | float | float64 | Float | |
| float | float | float | float | float32 | Float | |
| int32 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 | int32 | int | int | int32 | Fixnum 或者 Bignum(根据需要) | 
| uint32 | 使用变长编码 | uint32 | int | int | uint32 | Fixnum 或者 Bignum(根据需要) | 
| uint64 | 使用变长编码 | uint64 | long | int | uint64 | Bignum | 
| sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int32 | int | int | int32 | Fixnum 或者 Bignum(根据需要) | 
| sint64 | 使用变长编码,有符号的整型值。编码时比通常的int64高效。 | int64 | long | int | int64 | Bignum | 
| fixed32 | 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 | uint32 | int | int | uint32 | Fixnum 或者 Bignum(根据需要) | 
| fixed64 | 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 | uint64 | long | int | uint64 | Bignum | 
| sfixed32 | 总是4个字节 | int32 | int | int | int32 | Fixnum 或者 Bignum(根据需要) | 
| sfixed64 | 总是8个字节 | int64 | long | int | int64 | Bignum | 
| bool | bool | boolean | bool | bool | TrueClass/FalseClass | |
| string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 | string | String | str/unicode | string | String (UTF-8) | 
| bytes | 可能包含任意顺序的字节数据。 | string | ByteString | str/b'' | []byte | String (ASCII-8BIT) | 
| repeated | 数组(列表) | RepeatedField<type> | list | []type | RepeatedField | 
| map | 字典 | Map<key_type, value_type> | dict | {} | Map | 
参考资料:grpc-python02--了解rpc与grpc以及创建一个简单的grpc-python_哔哩哔哩_bilibili
gRPC(一)入门:什么是RPC? | Go 技术论坛
https://www.youtube.com/watch?v=gnchfOojMk4
python grpc框架之一 简单示例_51CTO博客_python web 框架
Python gRPC 入门 - 掘金



















