最近花钱买了一些课程讲述了关于RPC的一些知识,觉得还不错,空余时间研究了点gRPC这个框架,觉得还是不做的,然后记录一下留作以后参考。
在网上http://doc.oschina.net/grpc找到了gRPC官网的中文文档说明,这里面介绍了gRPC以及一些安全认证,通讯协议和简单的例子。
刚开始还以为gPRC这个框架能够完成分布式部署的,后来才发现gRPC只能够完成服务器的部分,想要真正的完成分布式部署服务器还要借助一些其他的工具,也就是所谓的服务发现,例如zookeeper, etcd或者是consul等。常见的RPC一般包含三部分:
1.注册中心(用于服务端注册远程服务以及客户端发现服务)
2.服务端(对外提供后台服务,将自己的服务信息注册到注册中心)
3.客户端(从注册中心获取远程服务的注册信息,然后进行远程过程调用)
gRPC这里只是实现了服务端和客户端这两个功能,要实现注册中心还需要借助一些其他的工具。
本篇之讲述gPRC服务端和客户端的功能,关于注册中心的后续有机会在进行补充。
首先是安装gRPC在官方文档上有完整的介绍,这里不多赘述了。
第一步:定义服务
一个 RPC 服务通过参数和返回类型来指定可以远程调用的方法,gRPC 通过 protocol buffers 来实现。使用 protocol buffers 接口定义语言来定义服务方法,用 protocol buffer 来定义参数和返回类型。客户端和服务端均使用服务定义生成的接口代码。
下面是demo1.proto具体定于:
syntax = "proto3";
option java_package = "io.grpc.examples";
package demo1;
// 定义服务
service ImageRecognition {
rpc Ping (PingRequest) returns (PongReply){};
rpc ClientToServerImg (stream ImgRequest) returns (ImgSize) {};
}
// 客户端传的名字
message PingRequest{
string ping = 1;
}
// 服务端返回的消息
message PongReply{
string pong = 1;
}
message ImgRequest{
string filename = 1;//文件名
string filetype = 2;//文件类型
bytes img = 3;//以二进制传方式送文件(也可以是string的形式:字符串形式传送)
}
message ImgSize{
int32 imgzise = 1;//返回文件的大小(字节数)
}
说明:这里定义了两个服务。一个Ping(简单 RPC),一个是客户端上传一个文件到服务器上(请求流式 RPC);
第二步:生成客户端和服务端代码
执行这一步需要安装好一些工具如下:
sudo apt-get install protobuf-compiler-grpc
sudo apt-get install protobuf-compiler
然后执行命令(我的环境是ubuntu18上,python3.6):
protoc -I ./protos --python_out=./protos --grpc_out=./protos --plugin=protoc-gen-grpc=`which grpc_python_plugin` ./protos/demo1.proto
在对应的目录下回生成两个文件demo1_pb2_grpc.py和demo1_pb2.py。
第三步:实现服务端代码
如下(server.py):
# -*- coding:utf-8 -*-
import demo1_pb2
import demo1_pb2_grpc
import time
import os
_ONE_DAY_IN_SECONDS = 60*60*24
class ImageRecognition(demo1_pb2.BetaImageRecognitionServicer):
def Ping(self, request, context):
if request.ping == 'ping':
return demo1_pb2.PongReply(pong="pong")
else:
return demo1_pb2.PongReply(pong="sorry")
def ClientToServerImg(self, request_iterator, context):
count = 0
with open("./image/img_file", "wb") as f:
for line in request_iterator:
num = f.write(line.img)
count += num
filename = line.filename
filetype = line.filetype
tmp_file = "./image/" + filename + filetype
os.rename("./image/img_file", tmp_file)
return demo1_pb2.ImgSize(imgzise=count)
server = demo1_pb2.beta_create_ImageRecognition_server(ImageRecognition())
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop()
第四步:实现客户端代码
如下(client.py)
# -*- coding:utf-8 -*-
from __future__ import print_function
import grpc
import demo1_pb2
import demo1_pb2_grpc
import os
def get_img_data(filepath):
(filename, filetype) = os.path.splitext(os.path.basename(os.path.realpath(filepath)))
with open(filepath, "rb") as f:
for line in f.readlines():
yield demo1_pb2.ImgRequest(filename=filename,filetype=filetype,img=line)
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = demo1_pb2_grpc.ImageRecognitionStub(channel)
response = stub.Ping(demo1_pb2.PingRequest(ping='ping'))
print(response.pong)
print('--------------------------------')
retsize = stub.ClientToServerImg(get_img_data("./demo1_pb2.py"))
print(retsize.imgzise)
if __name__ == '__main__':
run()
特别说明:有些人可能会疑惑为什么传送文件的参数使用yield进行返回,因为使用了请求流式RPC,对应的参数需要是个迭代器。
然后运行server.py
在运行client.py
会看到运行在server端会有个文件生成,客户端打印出文件的大小。