2016-09-27

gRPCを触ってみた

gRPCはRPCのフレームワークの一つです。SOAPと同じ分類になりますが、SOAPとの違いは以下になります。

ということで今回はRubyとPythonでgRPCなサーバ、クライアントを作成してみました。

Rubyで試す

以下のコマンドでgemをインストール
$ gem install grpc
$ gem install grpc-tools

protoファイルというインターフェース定義ファイルを作成

syntax = "proto3";

package helloworld;

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

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

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

コンパイル

$ mkdir lib
$ grpc_tools_ruby_protoc -I .--ruby_out=lib --grpc_out=lib ./helloworld.proto

サーバ側

#!/usr/bin/env ruby

this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(this_dir, 'lib')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

require 'grpc'
require 'helloworld_services_pb'

# GreeterServer is simple server that implements the Helloworld Greeter server.
class GreeterServer < Helloworld::Greeter::Service
  # say_hello implements the SayHello rpc method.
  def say_hello(hello_req, _unused_call)
    Helloworld::HelloReply.new(message: "Hello #{hello_req.name}")
  end

  def say_hello_again(hello_req, _unused_call)
    Helloworld::HelloReply.new(message: "Hello again, #{hello_req.name}")
  end
end

# main starts an RpcServer that receives requests to GreeterServer at the sample
# server port.
def main
  s = GRPC::RpcServer.new
  s.add_http2_port('0.0.0.0:{PORT}', :this_port_is_insecure)
  s.handle(GreeterServer)
  s.run_till_terminated
end

main

クライアント側

#!/usr/bin/env ruby

this_dir = File.expand_path(File.dirname(__FILE__))
lib_dir = File.join(this_dir, 'lib')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

require 'grpc'
require 'helloworld_services_pb'

def main
  stub = Helloworld::Greeter::Stub.new('{HOST_NAME}:{PORT}', :this_channel_is_insecure)
  user = ARGV.size > 0 ?  ARGV[0] : 'world'
  message = stub.say_hello(Helloworld::HelloRequest.new(name: user)).message
  p "Greeting: #{message}"
  message = stub.say_hello_again(Helloworld::HelloRequest.new(name: user)).message
  p "Greeting: #{message}"
end

main

これだけでRPCが可能です。SOAPよりかなり簡単な印象。利用可能な言語もC++、Go、C#、Java、Java(Android向け)、Node、Ruby、Python、PHP、Objective-Cと一通り揃ってます。

Pythonで試す

ライブラリをインストール
$ pip install grpcio
$ pip install grpcio-tools

Pythonの場合はコンパイルは以下のようなジェネレータスクリプトを書く必要があります

from grpc.tools import protoc

protoc.main(
    (
        '',
        '-I.',
        '--python_out=./lib',
        '--grpc_python_out=./lib',
        './helloworld.proto',
    )
)

あとはこのスクリプト(run_codegen.py)を起動させればコンパイルしてくれます

$ python ./run_codegen.py

サーバ側スクリプト

"""The Python implementation of the GRPC helloworld.Greeter server."""

from concurrent import futures
import time
import grpc
import sys

sys.path.append('lib')
import helloworld_pb2

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class Greeter(helloworld_pb2.GreeterServicer):

  def SayHello(self, request, context):
    return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)

  def SayOK(self, request, context):
    return helloworld_pb2.HelloReply(message='OK! %s!' % request.name)


def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  helloworld_pb2.add_GreeterServicer_to_server(Greeter(), server)
  server.add_insecure_port('[::]:50051')
  server.start()
  try:
    while True:
      time.sleep(_ONE_DAY_IN_SECONDS)
  except KeyboardInterrupt:
    server.stop(0)

if __name__ == '__main__':
  serve()

クライアント側スクリプト

"""The Python implementation of the GRPC helloworld.Greeter client."""

from __future__ import print_function
import grpc
import sys

sys.path.append('lib')
import helloworld_pb2


def run():
  channel = grpc.insecure_channel('localhost:50051')
  stub = helloworld_pb2.GreeterStub(channel)
  response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
  print("Greeter client received: " + response.message)
  response = stub.SayOK(helloworld_pb2.HelloRequest(name='you'))
  print("Greeter client received: " + response.message)


if __name__ == '__main__':
  run()

このエントリーをはてなブックマークに追加