[Ruby] Introduction of gRPC client to rails

3 minute read

History

In the implementation of the product (Rails) that is in charge of PM at the company, it is necessary to obtain information from other services, and that service was not OpenAPI, but the API interface was defined with gRPC, so implement the gRPC client. Did. In my previous job, I was touching around gRPC with rails a little, so I decided that I would do more development work for the team, so I decided to do it myself. The output about it.

Implementation procedure

1. Introduce gem
  • I used the library gruf that I used in my previous job.

Gemfile



gem "google-protobuf"
gem "grpc-tools"
gem "gruf"
  • We are developing in a container called web
docker-compose run --rm web bundle install
2. Register the repository where the proto file is managed as a subdirectory of the application with submodule, and fetch the proto file from the original repository
$ git submodule add [web URL or ssh key] proto
$ git submodule init
$ cd proto
$ git submodule update
3. Compile the proto file into a ruby file
docker-compose run --rm web grpc_tools_ruby_protoc -I [proto directory to compile] --ruby_out=[directory to save files after compile] --grpc_out=[directory to save files after compile] [compile to Target files in the proto directory]
4. Read setting of ruby file (*_pb.rb) after compilation

The file name after compilation does not match the class name and the file name, and it does not follow the Rails loading rules and is not automatically loaded, so it is necessary to specify it.

config/initializers/gruf.rb


require "gruf"

Gruf.configure do
  Dir.glob(Rails.root.join("[directory where files are saved after compilation]/*_pb.rb")).each do |file|
    require file
  end
end

In the file after compilation, it is specified automatically as follows, and it is necessary to add it to auto_load_path. From e.g. gruf-demo require'Products_pb'

*Because the files after compilation are not basically modified.

config/application.rb


  class Application <Rails::Application
    config.paths.add [ruby file directory after compilation], eager_load: true
  end
5. Implementation of the part where the client calls the server

By now, all compiled ruby files can be used. Client implementation.

I was worried about whether to use module, but in the existing implementation the client processing was put together in the service layer, so I decided to learn from it also this time.

*Note that around metadata depends on the implementation on the server side. gruf wiki,whentheclientisinitialized(Gruf::Client.new) I was a little worried because this area is different from this implementation, such as putting in a username with the key of the options argument of.

app/services/grpc_client_service.rb



class GrpcClientService
  def initialize
    @metadata = {
      login: ENV["GRPC_CLIENT"],
      password: ENV["GRPC_PASSWORD"]
    }
  end

  def run(service_klass, method, request)
    client = Gruf::Client.new(
      service: service_klass,
      options: {
        hostname: ENV["GRPC_HOST"],
        channel_credentials: :this_channel_is_insecure
      }
    )

    client.call(method, request.to_h, @metadata)
  end
end

What do you think of the introduction?

I used gruf in my previous job, so I thought I could afford it, but I thought that just using it was quite different from introducing it. I regret that I should have read the code around the settings.

Also, this time the gRPC server is written in go, and I had to give up because I had a hard time reading the code when the call from the client did not go well, so I also thought that I would like to study go.

Also, I was a little confused that when I initialize the client (Gruf::Client.new), I misunderstand that it is a mystery to put metadata (it is actually put in the argument at the time of call), and write it in the Wiki of the library. After all, I realized once again that I had to read the code carefully to see what I didn’t have.