7
7
[ ![ Total Download] ( https://img.shields.io/hexpm/dt/grpc.svg )] ( https://hex.pm/packages/elixir-grpc/grpc )
8
8
[ ![ Last Updated] ( https://img.shields.io/github/last-commit/elixir-grpc/grpc.svg )] ( https://github.com/elixir-grpc/grpc/commits/master )
9
9
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 .
11
11
12
12
## Table of contents
13
13
14
14
- [ 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 )
20
24
- [ Features] ( #features )
21
25
- [ Benchmark] ( #benchmark )
22
26
- [ Contributing] ( #contributing )
@@ -28,12 +32,16 @@ The package can be installed as:
28
32
``` elixir
29
33
def deps do
30
34
[
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
32
38
]
33
39
end
34
40
```
35
41
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.
37
45
38
46
1 . Write your protobuf file:
39
47
@@ -53,51 +61,143 @@ message HelloReply {
53
61
}
54
62
55
63
// 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) {}
59
68
}
60
-
61
69
```
62
70
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 ):
64
72
65
- ``` shell
73
+ ``` bash
66
74
protoc --elixir_out=plugins=grpc:./lib -I./priv/protos helloworld.proto
67
75
```
68
76
69
- In the following sections you will see how to implement gRPC server logic.
77
+ ## Server Implementation
70
78
71
- ### ** Simple RPC**
79
+ All RPC calls must be implemented using the stream-based API, even for unary requests.
72
80
73
- 1 . Implement the server side code like below and remember to return the expected message types.
81
+ ### Unary RPC using Stream API
74
82
75
83
``` 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
78
91
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 ()
82
99
end
83
100
end
84
101
```
85
102
86
- 2 . Define gRPC endpoints
103
+ ### Server-Side Streaming
87
104
88
105
``` 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
+ ```
92
116
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)
95
134
end
96
135
```
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
+ ```
97
198
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 )
99
200
100
- ** Note:** For other types of RPC call like streams see [ here] ( interop/lib/interop/server.ex ) .
101
201
102
202
### ** HTTP Transcoding**
103
203
@@ -160,10 +260,7 @@ defmodule Helloworld.Greeter.Server do
160
260
service: Helloworld .Greeter .Service ,
161
261
http_transcode: true
162
262
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...
167
264
end
168
265
```
169
266
@@ -186,60 +283,6 @@ defmodule Helloworld.Endpoint do
186
283
end
187
284
```
188
285
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
-
243
286
## Features
244
287
245
288
- Various kinds of RPC:
0 commit comments