XUtils

grpc-elixir

The Elixir implementation of gRPC.


Simple RPC

  1. Implement the server side code like below and remember to return the expected message types.
defmodule Helloworld.Greeter.Server do
  use GRPC.Server, service: Helloworld.Greeter.Service

  @spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) :: Helloworld.HelloReply.t
  def say_hello(request, _stream) do
    Helloworld.HelloReply.new(message: "Hello #{request.name}")
  end
end
  1. Define gRPC endpoints
# Define your endpoint
defmodule Helloworld.Endpoint do
  use GRPC.Endpoint

  intercept GRPC.Server.Interceptors.Logger
  run Helloworld.Greeter.Server
end

We will use this module in the gRPC server startup section.

Note: For other types of RPC call like streams see here.

HTTP Transcoding

  1. Adding grpc-gateway annotations to your protobuf file definition:
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }

  rpc SayHelloFrom (HelloRequestFrom) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/greeter"
      body: "*"
    };
  }
}
  1. Add protoc plugin dependency and compile your protos using protobuf_generate hex package:

In mix.exs:

def deps do
  [
    {:grpc, "~> 0.7"},
    {:protobuf_generate, "~> 0.1.1"}
  ]
end

And in your terminal:

mix protobuf.generate \
  --include-path=priv/proto \
  --include-path=deps/googleapis \
  --generate-descriptors=true \
  --output-path=./lib \
  --plugins=ProtobufGenerate.Plugins.GRPCWithOptions \
  google/api/annotations.proto google/api/http.proto helloworld.proto
  1. Enable http_transcode option in your Server module
defmodule Helloworld.Greeter.Server do
  use GRPC.Server, 
    service: Helloworld.Greeter.Service,
    http_transcode: true

  @spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) :: Helloworld.HelloReply.t
  def say_hello(request, _stream) do
    %Helloworld.HelloReply{message: "Hello #{request.name}"}
  end
end

See full application code in helloworld_transcoding example.

Start Application

  1. Start gRPC Server in your supervisor tree or Application module:
# In the start function of your Application
defmodule HelloworldApp do
  use Application
  def start(_type, _args) do
    children = [
      # ...
      {GRPC.Server.Supervisor, endpoint: Helloworld.Endpoint, port: 50051, start_server: true}
    ]

    opts = [strategy: :one_for_one, name: YourApp]
    Supervisor.start_link(children, opts)
  end
end
  1. Call rpc:
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051")
iex> request = Helloworld.HelloRequest.new(name: "grpc-elixir")
iex> {:ok, reply} = channel |> Helloworld.Greeter.Stub.say_hello(request)

# With interceptors
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
...

Check the examples and interop directories in the project’s source code for some examples.

Client Adapter and Configuration

The default adapter used by GRPC.Stub.connect/2 is GRPC.Client.Adapter.Gun. Another option is to use GRPC.Client.Adapters.Mint instead, like so:

GRPC.Stub.connect("localhost:50051",
  # Use Mint adapter instead of default Gun
  adapter: GRPC.Client.Adapters.Mint
)

The GRPC.Client.Adapters.Mint adapter accepts custom configuration. To do so, you can configure it from your mix application via:

# File: your application's config file.
config :grpc, GRPC.Client.Adapters.Mint, custom_opts

The accepted options for configuration are the ones listed on Mint.HTTP.connect/4

Benchmark

  1. Simple benchmark by using ghz

  2. Benchmark followed by official spec


Articles

  • coming soon...