[Ruby] Implement a gRPC client in Ruby

2 minute read

Write down the procedure for implementing the gRPC client in Ruby, also as a reminder

First insert a gem

  • Install grpc gem to use gRPC with Ruby -By including this, you can use the classes necessary for client implementation.
gem'grpc','~> X.XX.X'

Getting and compiling .proto files

  • There are several ways to manage .proto files, but for details, see this article
  • To compile .proto into rb file, use Gem grpc_tools
$ gem install grpc-tools
  • The compile command looks like
$ grpc_tools_ruby_protoc -I ./proto \--ruby_out=lib --grpc_out=lib ./proto/hoge_proto/*.proto
  • -I option specifies a directory to search for .proto files
  • Specify output directory after compilation with --ruby_out option -Anywhere but /lib seems to be a lot
  • Specify the output destination of the class file that becomes the stub after compilation in the first argument of the --grpc_out option, and the directory containing the .proto file to be compiled in the second argument
  • If the compilation is successful, pb.rb file corresponding to each proto and hoge_services_pb.rb file that imports all of them and modularizes them are generated. -There are rpc method definition and Stub definition in this hoge_services_pb.rb, and this class will be used for invocation

Client stub implementation

Now that the preparation is complete, we will actually implement it from here. First, create a stub instance required to make a gRPC call. Regarding stub instance creation, I often see a method of creating a file under /initializer and doing new there, but I didn’t like to plunge into a global variable, so this time cutting the model class and applying the singleton pattern Implemented with. To be honest, we are still looking for the best practices around here.

model/grpc/hoge_service_client.rb


require'proto/hoge_services_pb'
require'singleton'

# It is better to make here to get from the configuration file for each environment
END_POINT = "localhost:50051"

class Grpc::HogeServiceClient
  include Singleton

  attr_reader :stub

  def initialize
    @stub = HogeProto::HogeService::Stub.new(END_POINT, :this_channel_is_insecure)
  end
end
  • The part of Stub instance creation is creating an instance by new Stub described in hoge_services_pb.rb as described above. -first argument of new specifies the endpoint of gRPC server -The second argument is an argument for encryption during rpc communication, and the SSL certificate path etc. will be specified when operating properly

Actually call the gRPC server

Let’s actually call rpc using the Stub created above

grpc/hoge.rb


class Grpc::Hoge
  def get_name(params)
# Create an instance of model that has a Stub instance
    client = Grpc::HogeServiceClient.instance
    Create request data required for #rpc call
    req = get_name_request(params)
# Create metadata if needed
    meta = Hash.new
    meta["x-app-id"] = "hogeghoge"
    meta["x-app-password"] = "fugafuga"

    begin
  #Here rpc call
      client.stub.get_name(req, {metadata: meta})
      res = "success"
    rescue GRPC::BadStatus => ex
      res = "error"
    rescue ex
      res = "unexpected error"
    end

    res
  end

  private

  def get_name_request(params)
    HogeProto::GetNameRequest.new(
      id: params[:id]
    )
  end
  • rpc call in client.stub.get_name part -If the result of this call is an error (Status.Codes is not OK), an exception will occur, so enclose it in begin and rescue it in case of error
  • For meta, set the metadata for gRPC call -Here, ID and password are set, but it seems better to initialize common variables as instance variables on the model side that creates stubs. -It seems that you can normally set the value in Hash format -However, note that capital letters cannot be used for keys

About error handling

As I mentioned earlier, if an error status response is returned by calling rpc, an exception of GRPC::BadStatus class will occur. Therefore, it is necessary to begin~rescue. Also, if the server side returns error_details packed, it is necessary to implement to retrieve it. Reference: https://www.rubydoc.info/gems/grpc/GRPC/BadStatus