2018-02-12

gRPC

触ったことがないのもどうかと思うのでRubyから使ってみた。リソースは次のあたりのもの。

gRPCのgemがまだRuby 2.5に対応してなかった。

grpc-1.8.7-universal-darwin requires ruby version < 2.5, which is incompatible with the current version, ruby 2.5.0p0

次のようなprotoファイルを書く。

syntax = "proto3";

package cat;

service CatService {
  rpc GetMyCat (GetMyCatMessage) returns (MyCatResponse) {}
}

message GetMyCatMessage {
  string target_cat = 1;
}

message MyCatResponse {
  string name = 1;
  string kind = 2;
}

bundle exec grpc_tools_ruby_protoc -I . --ruby_out=lib --grpc_out=lib cat.protoで次のファイルが生成される。

# lib/cat_services_pb.rb

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# Source: cat.proto for package 'cat'

require 'grpc'
require 'cat_pb'

module Cat
  module CatService
    class Service

      include GRPC::GenericService

      self.marshal_class_method = :encode
      self.unmarshal_class_method = :decode
      self.service_name = 'cat.CatService'

      rpc :GetMyCat, GetMyCatMessage, MyCatResponse
    end

    Stub = Service.rpc_stub_class
  end
end
# lib/cat_pb.rb

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: cat.proto

require 'google/protobuf'

Google::Protobuf::DescriptorPool.generated_pool.build do
  add_message "cat.GetMyCatMessage" do
    optional :target_cat, :string, 1
  end
  add_message "cat.MyCatResponse" do
    optional :name, :string, 1
    optional :kind, :string, 2
  end
end

module Cat
  GetMyCatMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("cat.GetMyCatMessage").msgclass
  MyCatResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("cat.MyCatResponse").msgclass
end

これらを使って、サーバとクライアントをそれぞれ実装できる。

# cat_server.rb

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 'cat_services_pb'

class CatServer < Cat::CatService::Service
  def get_my_cat(my_cat_req, _unused_call)
    Cat::MyCatResponse.new(name: "#{my_cat_req.target_cat}", kind: 'neko')
  end
end

def main
  s = GRPC::RpcServer.new
  s.add_http2_port('0.0.0.0:50051', :this_port_is_insecure)
  s.handle(CatServer)
  s.run_till_terminated
end

main
# cat_client.rb

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 'cat_services_pb'

def main
  stub = Cat::CatService::Stub.new('localhost:50051', :this_channel_is_insecure)
  message = stub.get_my_cat(Cat::GetMyCatMessage.new(target_cat: 'waiwai'))
  p "#{message.name} #{message.kind}"
end

main

ruby cat_server.rbでサーバを立ち上げて、クライアントをruby cat_client.rbで実行すると次の出力が得られる。

$ bundle exec ruby cat_client.rb
"waiwai neko"

Rubyだと、基本はGRPC::RpcServerでサーバを立てる。サーバ側のロジックの実装はどうしようと自由だが、こういうのもある。

bigcommerce/gruf: gRPC Ruby Framework

また、REST APIとしてリクエストを受けられるgRPCのプロキシサーバもある。

grpc-ecosystem/grpc-gateway: gRPC to JSON proxy generator following the gRPC HTTP spec

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です