grpc:google 官方的 rpc 框架

WesFiedler 3年前
   <p>protobuf是在项目中经常会用到的一个库,它提供了方便的工具和接口,可以对结构化数据进行序列化和反序列化,便于网络传输。</p>    <p>其实,如果将一个函数调用用结构化数据表示出来,利用protobuf序列化后通过网络传递到远端,在远端进行反序列化解析,就自然地实现了rpc(Remote Procedure Call)的功能。</p>    <p>protobuf中保留了关键字rpc,并且提供了一个RpcChannel的类,供开发者自己实现rpc框架。实现这个rpc框架,其实主要是实现RpcChannel::CallMethod这个接口。我们自己的项目中,就使用了一套自己实现的基于ansyncore的RpcChannel,而某度最近也开源了其基于protobuf的 <a href="/misc/goto?guid=4959716956567576429" rel="nofollow,noindex">rpc框架</a> ,网络部分是使用的Boost::Asio,有兴趣的读者可以自行前往其github wiki页面学习。</p>    <p>那grpc呢,则是google自己基于protobuf(也是google自己开发的库)实现的一套rpc框架。这里使用一张官网的图,表示下其基础架构:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/749fd5af5a72779463f3f0f467152912.jpg"></p>    <h2><strong>一个简单的例子</strong></h2>    <p>可能光说不练有点抽象,那么下面用一个例子说明下grpc的基本用法。</p>    <p>说到网络相关的例子,简单而又实用的当属EchoServer了。</p>    <p>定义一个EchoServer只需一个接口Echo,它接受一条字符串消息,并原样返回一条字符串消息。因此,echo_server.proto文件定义如下:</p>    <pre>  <code class="language-python">syntax = "proto3";    package echo_server;    service EchoServer  {      rpc Echo (EchoRequest) returns (EchoReply) {}  }    message EchoRequest  {      string msg = 1;  }    message EchoReply  {      string msg = 1;  }  </code></pre>    <p>首先,grpc使用protobuf3.x版本,因此需要在开头声明syntax=”proto3”,剩下的部分和c语言的语法很类似,基本上有了例子之后,照猫画虎很容易就可以写出来自己需要的proto文件。</p>    <p>有了proto文件之后,需要使用protoc将其编译生成对应的py文件。这里grpc提供了一个grpc_tools的库,可以将这一过程程序化:</p>    <pre>  <code class="language-python">from grpc.tools import protoc    protoc.main(      (          '',          '-I.',          '--python_out=.',          '--grpc_python_out=.',          './echo_server.proto',      )  )  </code></pre>    <p>生成的echo_server_pb2.py文件中,就定义了我们实现这个EchoServer所需的Servicer类和Stub类。</p>    <h3><strong>Server</strong></h3>    <p>先看server的实现。</p>    <p>首先,需要定义一个类,继承自xxxServicer(这里是EchoServerServicer),并重写其Echo方法。</p>    <pre>  <code class="language-python">class EchoServer(echo_server_pb2.EchoServerServicer):      def Echo(self, request, context):          return echo_server_pb2.EchoReply(msg='echo:%s' % request.msg)  </code></pre>    <p>可以看到,其中的request和return值,都是按照我们在proto文件中的定义生成的python类型,非常直观。</p>    <p>如何将这个EchoServer类和rpc服务绑定在一起,也是有套路的:</p>    <pre>  <code class="language-python">server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  echo_server_pb2.add_EchoServerServicer_to_server(EchoServer(), server)  server.add_insecure_port('[::]:50015')  server.start()  try:      while True:          time.sleep(60*60)  except KeyboardInterrupt:      server.stop(0)  </code></pre>    <p>由于server.start()是一个非阻塞式调用,因此需要在后面用一个死循环来防止程序终止/GC导致rpc服务不可用。</p>    <h3><strong>Client</strong></h3>    <p>Client的实现就更简单了,只需通过ip和port创建一个channel,然后利用这个channel创建一个本地的Stub,然后就可以直接Stub.Echo调用远端的Echo方法了,Stub会帮你处理好一切其他事务(构造调用的结构化数据,序列化,网络传输等等)。</p>    <pre>  <code class="language-python">from __future__ import print_function  import grpc  import echo_server_pb2  import sys    if sys.version_info.major == 3:      raw_input = input  else:      raw_input = raw_input      def run():      channel = grpc.insecure_channel('localhost:50015')      stub = echo_server_pb2.EchoServerStub(channel)      while True:          msg = raw_input('you say:')          reply = stub.Echo(echo_server_pb2.EchoRequest(msg=msg))          print(reply.msg)      pass    if __name__ == "__main__":      run()  </code></pre>    <p>这段代码中对print和raw_input这两个py2和py3不兼容的调用打了Monkey Patch,从而使得这段程序可以同时运行在py2和py3的环境中~</p>    <h2><strong>总结</strong></h2>    <p>至此,grpc的最基础的应用就说的差不多了。除了上面的阻塞式调用,grpc还提供了非阻塞式调用(future)。另外,对于传递的参数和返回值,grpc还支持流式参数(stream、yield)。具体的相关例子,有兴趣的读者可以前往grpc的官网查询。</p>    <p>接下来,我将会使用grpc做一个message hub的应用,功能上应该可以替代目前项目中使用的hub(性能上就不指望替代了,毕竟当前hub使用c++实现的),当作一个练手的实际项目。</p>    <p> </p>    <p> </p>    <p>来自:http://blog.guoyb.com/2016/10/15/grpc/</p>    <p> </p>