Skip to content

Commit ddb217e

Browse files
sleipnirAdriano Santos
andauthored
Update documentation (#425)
* chore: release new version * doc: more information and tips --------- Co-authored-by: Adriano Santos <[email protected]>
1 parent 86736ae commit ddb217e

File tree

1 file changed

+131
-88
lines changed

1 file changed

+131
-88
lines changed

README.md

Lines changed: 131 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@
77
[![Total Download](https://img.shields.io/hexpm/dt/grpc.svg)](https://hex.pm/packages/elixir-grpc/grpc)
88
[![Last Updated](https://img.shields.io/github/last-commit/elixir-grpc/grpc.svg)](https://github.com/elixir-grpc/grpc/commits/master)
99

10-
An Elixir implementation of [gRPC](http://www.grpc.io/).
10+
**gRPC Elixir** is a full-featured Elixir implementation of the [gRPC](https://grpc.io) protocol, supporting unary and streaming RPCs, interceptors, HTTP transcoding, and TLS. This version adopts a unified stream-based model for all types of calls.
1111

1212
## Table of contents
1313

1414
- [Installation](#installation)
15-
- [Usage](#usage)
16-
- [Simple RPC](#simple-rpc)
17-
- [HTTP Transcoding](#http-transcoding)
18-
- [CORS](#cors)
19-
- [Start Application](#start-application)
15+
- [Protobuf Code Generation](#protobuf-code-generation)
16+
- [Server Implementation](#server-implementation)
17+
- [Unary RPC using Stream API](#unary-rpc-using-stream-api)
18+
- [Server-Side Streaming](#server-side-streaming)
19+
- [Bidirectional Streaming](#bidirectional-streaming)
20+
- [Application Startup](#application-startup)
21+
- [Client Usage](#client-usage)
22+
- [HTTP Transcoding](#http-transcoding)
23+
- [CORS](#cors)
2024
- [Features](#features)
2125
- [Benchmark](#benchmark)
2226
- [Contributing](#contributing)
@@ -28,12 +32,16 @@ The package can be installed as:
2832
```elixir
2933
def deps do
3034
[
31-
{:grpc, "~> 0.10"}
35+
{:grpc, "~> 0.10"},
36+
{:protobuf, "~> 0.14"}, # optional for import wellknown google types
37+
{:grpc_reflection, "~> 0.1"} # optional enable grpc reflection
3238
]
3339
end
3440
```
3541

36-
## Usage
42+
## Protobuf Code Generation
43+
44+
Use `protoc` with [protobuf elixir plugin](https://github.com/elixir-protobuf/protobuf) or using [protobuf_generate](https://hexdocs.pm/protobuf_generate/readme.html) hex package to generate the necessary files.
3745

3846
1. Write your protobuf file:
3947

@@ -53,51 +61,143 @@ message HelloReply {
5361
}
5462
5563
// The greeting service definition.
56-
service Greeter {
57-
// Greeting function
58-
rpc SayHello (HelloRequest) returns (HelloReply) {}
64+
service GreetingServer {
65+
rpc SayUnaryHello (HelloRequest) returns (HelloReply) {}
66+
rpc SayServerHello (HelloRequest) returns (stream HelloReply) {}
67+
rpc SayBidStreamHello (stream HelloRequest) returns (stream HelloReply) {}
5968
}
60-
6169
```
6270

63-
2. Then generate Elixir code from proto file as [protobuf-elixir](https://github.com/elixir-protobuf/protobuf#usage):
71+
2. Compile protos (protoc + elixir plugin):
6472

65-
```shell
73+
```bash
6674
protoc --elixir_out=plugins=grpc:./lib -I./priv/protos helloworld.proto
6775
```
6876

69-
In the following sections you will see how to implement gRPC server logic.
77+
## Server Implementation
7078

71-
### **Simple RPC**
79+
All RPC calls must be implemented using the stream-based API, even for unary requests.
7280

73-
1. Implement the server side code like below and remember to return the expected message types.
81+
### Unary RPC using Stream API
7482

7583
```elixir
76-
defmodule Helloworld.Greeter.Server do
77-
use GRPC.Server, service: Helloworld.Greeter.Service
84+
defmodule HelloworldStreams.Server do
85+
use GRPC.Server, service: Helloworld.GreetingServer.Service
86+
87+
alias GRPC.Stream
88+
89+
alias Helloworld.HelloRequest
90+
alias Helloworld.HelloReply
7891

79-
@spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) :: Helloworld.HelloReply.t
80-
def say_hello(request, _stream) do
81-
Helloworld.HelloReply.new(message: "Hello #{request.name}")
92+
@spec say_unary_hello(HelloRequest.t(), GRPC.Server.Stream.t()) :: any()
93+
def say_unary_hello(request, _materializer) do
94+
GRPC.Stream.unary(request)
95+
|> GRPC.Stream.map(fn %HelloReply{} = reply ->
96+
%HelloReply{message: "[Reply] #{reply.message}"}
97+
end)
98+
|> GRPC.Stream.run()
8299
end
83100
end
84101
```
85102

86-
2. Define gRPC endpoints
103+
### Server-Side Streaming
87104

88105
```elixir
89-
# Define your endpoint
90-
defmodule Helloworld.Endpoint do
91-
use GRPC.Endpoint
106+
def say_server_hello(request, materializer) do
107+
Stream.repeatedly(fn ->
108+
index = :rand.uniform(10)
109+
%HelloReply{message: "[#{index}] Hello #{request.name}"}
110+
end)
111+
|> Stream.take(10)
112+
|> GRPC.Stream.from()
113+
|> GRPC.Stream.run_with(materializer)
114+
end
115+
```
92116

93-
intercept GRPC.Server.Interceptors.Logger
94-
run Helloworld.Greeter.Server
117+
### Bidirectional Streaming
118+
119+
```elixir
120+
@spec say_bid_stream_hello(Enumerable.t(), GRPC.Server.Stream.t()) :: any()
121+
def say_bid_stream_hello(request, materializer) do
122+
output_stream =
123+
Stream.repeatedly(fn ->
124+
index = :rand.uniform(10)
125+
%HelloReply{message: "[#{index}] Server response"}
126+
end)
127+
128+
GRPC.Stream.from(request, join_with: output_stream)
129+
|> GRPC.Stream.map(fn
130+
%HelloRequest{name: name} -> %HelloReply{message: "Welcome #{name}"}
131+
other -> other
132+
end)
133+
|> GRPC.Stream.run_with(materializer)
95134
end
96135
```
136+
__💡__ The Stream API supports composable stream transformations via `ask`, `map`, `run` and others functions, enabling clean and declarative stream pipelines. For a complete list of available operators see [here](lib/grpc/stream/operators.ex).
137+
138+
## Application Startup
139+
140+
Add the server supervisor to your application's supervision tree:
141+
142+
```elixir
143+
defmodule Helloworld.Application do
144+
@ false
145+
use Application
146+
147+
@impl true
148+
def start(_type, _args) do
149+
children = [
150+
GrpcReflection,
151+
{
152+
GRPC.Server.Supervisor, [
153+
endpoint: Helloworld.Endpoint,
154+
port: 50051,
155+
start_server: true,
156+
# adapter_opts: [# any adapter-specific options like tls configuration....]
157+
]
158+
}
159+
]
160+
161+
opts = [strategy: :one_for_one, name: Helloworld.Supervisor]
162+
Supervisor.start_link(children, opts)
163+
end
164+
end
165+
```
166+
167+
# Client Usage
168+
169+
```elixir
170+
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051")
171+
iex> request = Helloworld.HelloRequest.new(name: "grpc-elixir")
172+
iex> {:ok, reply} = channel |> Helloworld.GreetingServer.Stub.say_unary_hello(request)
173+
174+
# With interceptors
175+
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
176+
...
177+
```
178+
179+
Check the [examples](examples) and [interop](interop) directories in the project's source code for some examples.
180+
181+
## Client Adapter and Configuration
182+
183+
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:
184+
185+
```elixir
186+
GRPC.Stub.connect("localhost:50051",
187+
# Use Mint adapter instead of default Gun
188+
adapter: GRPC.Client.Adapters.Mint
189+
)
190+
```
191+
192+
The `GRPC.Client.Adapters.Mint` adapter accepts custom configuration. To do so, you can configure it from your mix application via:
193+
194+
```elixir
195+
# File: your application's config file.
196+
config :grpc, GRPC.Client.Adapters.Mint, custom_opts
197+
```
97198

98-
We will use this module [in the gRPC server startup section](#start-application).
199+
The accepted options for configuration are the ones listed on [Mint.HTTP.connect/4](https://hexdocs.pm/mint/Mint.HTTP.html#connect/4-options)
99200

100-
**Note:** For other types of RPC call like streams see [here](interop/lib/interop/server.ex).
101201

102202
### **HTTP Transcoding**
103203

@@ -160,10 +260,7 @@ defmodule Helloworld.Greeter.Server do
160260
service: Helloworld.Greeter.Service,
161261
http_transcode: true
162262

163-
@spec say_hello(Helloworld.HelloRequest.t, GRPC.Server.Stream.t) :: Helloworld.HelloReply.t
164-
def say_hello(request, _stream) do
165-
%Helloworld.HelloReply{message: "Hello #{request.name}"}
166-
end
263+
# callback implementations...
167264
end
168265
```
169266

@@ -186,60 +283,6 @@ defmodule Helloworld.Endpoint do
186283
end
187284
```
188285

189-
### **Start Application**
190-
191-
1. Start gRPC Server in your supervisor tree or Application module:
192-
193-
```elixir
194-
# In the start function of your Application
195-
defmodule HelloworldApp do
196-
use Application
197-
def start(_type, _args) do
198-
children = [
199-
# ...
200-
{GRPC.Server.Supervisor, endpoint: Helloworld.Endpoint, port: 50051, start_server: true}
201-
]
202-
203-
opts = [strategy: :one_for_one, name: YourApp]
204-
Supervisor.start_link(children, opts)
205-
end
206-
end
207-
```
208-
209-
2. Call rpc:
210-
211-
```elixir
212-
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051")
213-
iex> request = Helloworld.HelloRequest.new(name: "grpc-elixir")
214-
iex> {:ok, reply} = channel |> Helloworld.Greeter.Stub.say_hello(request)
215-
216-
# With interceptors
217-
iex> {:ok, channel} = GRPC.Stub.connect("localhost:50051", interceptors: [GRPC.Client.Interceptors.Logger])
218-
...
219-
```
220-
221-
Check the [examples](examples) and [interop](interop) directories in the project's source code for some examples.
222-
223-
## Client Adapter and Configuration
224-
225-
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:
226-
227-
```elixir
228-
GRPC.Stub.connect("localhost:50051",
229-
# Use Mint adapter instead of default Gun
230-
adapter: GRPC.Client.Adapters.Mint
231-
)
232-
```
233-
234-
The `GRPC.Client.Adapters.Mint` adapter accepts custom configuration. To do so, you can configure it from your mix application via:
235-
236-
```elixir
237-
# File: your application's config file.
238-
config :grpc, GRPC.Client.Adapters.Mint, custom_opts
239-
```
240-
241-
The accepted options for configuration are the ones listed on [Mint.HTTP.connect/4](https://hexdocs.pm/mint/Mint.HTTP.html#connect/4-options)
242-
243286
## Features
244287

245288
- Various kinds of RPC:

0 commit comments

Comments
 (0)