-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathclients-and-servers.html
358 lines (277 loc) · 21.9 KB
/
clients-and-servers.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="keywords" content="Erlang, OTP, behaviour, gen_server, abstraction, generic, server, process, callback, module" />
<meta name="description" content="A second visit of the client-server pattern and how to make it generic, this time by the use of the gen_server OTP behaviour." />
<meta name="google-site-verification" content="mi1UCmFD_2pMLt2jsYHzi_0b6Go9xja8TGllOSoQPVU" />
<link rel="stylesheet" type="text/css" href="static/css/screen.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/sh/shCore.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/sh/shThemeLYSE2.css" media="screen" />
<link rel="stylesheet" type="text/css" href="static/css/print.css" media="print" />
<link href="rss" type="application/rss+xml" rel="alternate" title="LYSE news" />
<link rel="icon" type="image/png" href="favicon.ico" />
<link rel="apple-touch-icon" href="static/img/touch-icon-iphone.png" />
<link rel="apple-touch-icon" sizes="72x72" href="static/img/touch-icon-ipad.png" />
<link rel="apple-touch-icon" sizes="114x114" href="static/img/touch-icon-iphone4.png" />
<title>Clients and Servers | Learn You Some Erlang for Great Good!</title>
</head>
<body>
<div id="wrapper">
<div id="header">
<h1>Learn you some Erlang</h1>
<span>for great good!</span>
</div> <!-- header -->
<div id="menu">
<ul>
<li><a href="content.html" title="Home">Home</a></li>
<li><a href="faq.html" title="Frequently Asked Questions">FAQ</a></li>
<li><a href="rss" title="Latest News">RSS</a></li>
<li><a href="static/erlang/learn-you-some-erlang.zip" title="Source Code">Code</a></li>
</ul>
</div><!-- menu -->
<div id="content">
<div class="noscript"><noscript>Hey there, it appears your Javascript is disabled. That's fine, the site works without it. However, you might prefer reading it with syntax highlighting, which requires Javascript!</noscript></div>
<h2>Clients and Servers</h2>
<h3><a class="section" name="callback-to-the-future">Callback to the Future</a></h3>
<img class="left" src="static/img/cbttf.png" width="260" height="238" alt="Weird version of Marty from Back to The Future" title="brrraaains (in the past)" />
<p>The first OTP behaviour we'll see is one of the most used ones. Its name is <code>gen_server</code> and it has an interface a bit similar to the one we've written with <code>my_server</code> in <a class="chapter" href="what-is-otp.html#the-basic-server">last chapter</a>; it gives you a few functions to use it and in exchange, your module has to already have a few functions <code>gen_server</code> will use.</p>
<h4>init</h4>
<p>The first one is an <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#init/1">init/1</a></code> function. It is similar to the one we've used with <code>my_server</code> in that it is used to initialize the server's state and do all of these one-time tasks that it will depend on. The function can return <code>{ok, State}</code>, <code>{ok, State, TimeOut}</code>, <code>{ok, State, hibernate}</code>, <code>{stop, Reason}</code> or <code>ignore</code>.</p>
<p>The normal <code>{ok, State}</code> return value doesn't really need explaining past saying that <var>State</var> will be passed directly to the main loop of the process as the state to keep later on. The <var>TimeOut</var> variable is meant to be added to the tuple whenever you need a deadline before which you expect the server to receive a message. If no message is received before the deadline, a special one (the atom <code>timeout</code>) is sent to the server, which should be handled with <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#handle_info/2">handle_info/2</a></code> (more on this later.)</p>
<p>On the other hand, if you do expect the process to take a long time before getting a reply and are worried about memory, you can add the <code>hibernate</code> atom to the tuple. Hibernation basically reduces the size of the process' state until it gets a message, at the cost of some processing power. If you are in doubt about using hibernation, you probably don't need it.</p>
<p>Returning <code>{stop, Reason}</code> should be done when something went wrong during the initialization.</p>
<div class="note">
<p><strong>Note:</strong> here's a more technical definition of process hibernation. It's no big deal if some readers do not understand or care about it. When the BIF <a class="docs" href="http://erldocs.com/17.3/erts/erlang.html#hibernate/3">erlang:hibernate(M,F,A)</a> is called, the call stack for the currently running process is discarded (the function never returns). The garbage collection then kicks in, and what's left is one continuous heap that is shrunken to the size of the data in the process. This basically compacts all the data so the process takes less place.</p>
<p>Once the process receives a message, the function <code>M:F</code> with <var>A</var> as arguments is called and the execution resumes.</p>
</div>
<div class="note">
<p><strong>Note:</strong> while <code>init/1</code> is running, execution is blocked in the process that spawned the server. This is because it is waiting for a 'ready' message sent automatically by the <code>gen_server</code> module to make sure everything went fine.</p>
</div>
<h4>handle_call</h4>
<p>The function <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#handle_call/3">handle_call/3</a></code> is used to work with synchronous messages (we'll see how to send them soon). It takes 3 arguments: <var>Request</var>, <var>From</var>, and <var>State</var>. It's pretty similar to how we programmed our own <code>handle_call/3</code> in <code>my_server</code>. The biggest difference is how you reply to messages. In our own abstraction of a server, it was necessary to use <code>my_server:reply/2</code> to talk back to the process. In the case of <code>gen_server</code>s, there are 8 different return values possible, taking the form of tuples.</p>
<p>Because there are many of them, here's a simple list instead:</p>
<pre class="expand">
{reply,Reply,NewState}
{reply,Reply,NewState,Timeout}
{reply,Reply,NewState,hibernate}
{noreply,NewState}
{noreply,NewState,Timeout}
{noreply,NewState,hibernate}
{stop,Reason,Reply,NewState}
{stop,Reason,NewState}
</pre>
<p>For all of these, <var>Timeout</var> and <code>hibernate</code> work the same way as for <code>init/1</code>. Whatever is in <var>Reply</var> will be sent back to whoever called the server in the first place. Notice that there are three possible <code>noreply</code> options. When you use <code>noreply</code>, the generic part of the server will assume you're taking care of sending the reply back yourself. This can be done with <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#reply/2">gen_server:reply/2</a></code>, which can be used in the same way as <code>my_server:reply/2</code>.</p>
<p>Most of the time, you'll only need the <code>reply</code> tuples. There are still a few valid reasons to use <code>noreply</code>: whenever you want another process to send the reply for you or when you want to send an acknowledgement ('hey! I received the message!') but still process it afterwards (without replying this time), etc. If this is what you choose to do, it is absolutely necessary to use <code>gen_server:reply/2</code> because otherwise the call will time out and cause a crash.</p>
<h4>handle_cast</h4>
<p>The <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#handle_cast/2">handle_cast/2</a></code> callback works a lot like the one we had in <code>my_server</code>: it takes the parameters <var>Message</var> and <var>State</var> and is used to handle asynchronous calls. You do whatever you want in there, in a manner quite similar to what's doable with <code>handle_call/3</code>. On the other hand, only tuples without replies are valid return values:</p>
<pre class="expand">
{noreply,NewState}
{noreply,NewState,Timeout}
{noreply,NewState,hibernate}
{stop,Reason,NewState}
</pre>
<h4>handle_info</h4>
<p>You know how I mentioned our own server didn't really deal with messages that do not fit our interface, right? Well <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#handle_info/2">handle_info/2</a></code> is the solution. It's very similar to <code>handle_cast/2</code> and in fact returns the same tuples. The difference is that this callback is only there for messages that were sent directly with the <code>!</code> operator and special ones like <code>init/1</code>'s <code>timeout</code>, monitors' notifications and <code>'EXIT'</code> signals. </p>
<h4>terminate</h4>
<p>The callback <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#terminate/2">terminate/2</a></code> is called whenever one of the three <code>handle_Something</code> functions returns a tuple of the form <code>{stop, Reason, NewState}</code> or <code>{stop, Reason, Reply, NewState}</code>. It takes two parameters, <var>Reason</var> and <var>State</var>, corresponding to the same values from the <code>stop</code> tuples.</p>
<p><code>terminate/2</code> will also be called when its parent (the process that spawned it) dies, if and only if the <code>gen_server</code> is trapping exits.</p>
<div class="note">
<p><strong>Note:</strong> if any reason other than <code>normal</code>, <code>shutdown</code> or <code>{shutdown, Term}</code> is used when <code>terminate/2</code> is called, the OTP framework will see this as a failure and start logging a bunch of stuff here and there for you.</p>
</div>
<p>This function is pretty much the direct opposite of <code>init/1</code> so whatever was done in there should have its opposite in <code>terminate/2</code>. It's your server's janitor, the function in charge of locking the door after making sure everyone's gone. Of course, the function is helped by the VM itself, which should usually delete all <a class="docs" href="http://erldocs.com/17.3/stdlib/ets.html">ETS tables</a>, close all <a class="external" href="http://www.erlang.org/doc/tutorial/c_port.html">ports</a>, etc. for you. Note that the return value of this function doesn't really matter, because the code stops executing after it's been called.</p>
<h4>code_change</h4>
<p>The function <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#code_change/3">code_change/3</a></code> is there to let you upgrade code. It takes the form <code>code_change(PreviousVersion, State, Extra)</code>. Here, the variable <var>PreviousVersion</var> is either the version term itself in the case of an upgrade (read <a class="chapter" href="modules.html#more-about-modules">More About Modules</a> again if you forget what this is), or <code>{down, Version}</code> in the case of a downgrade (just reloading older code). The <var>State</var> variable holds all of the current's server state so you can convert it.</p>
<p>Imagine for a moment that we used an orddict to store all of our data. However, as time goes on, the orddict becomes too slow and we decide to change it for a regular dict. In order to avoid the process crashing on the next function call, the conversion from one data structure to the other can be done in there, safely. All you have to do is return the new state with <code>{ok, NewState}</code>.</p>
<img class="right" src="static/img/kitty.png" width="277" height="323" alt="a cat with an eye patch" />
<p>The <var>Extra</var> variable isn't something we'll worry about for now. It's mostly used in larger OTP deployment, where specific tools exist to upgrade entire releases on a VM. We're not there yet.</p>
<p>So now we've got all the callbacks defined. Don't worry if you're a bit lost: the OTP framework is a bit circular sometimes, where to understand part <var>A</var> of the framework you have to understand part <var>B</var>, but then part <var>B</var> requires to see part <var>A</var> to be useful. The best way to get over that confusion is to actually implement a gen_server.</p>
<h3><a class="section" name="beam-me-up-scotty">.BEAM me up, Scotty!</a></h3>
<p>This is going to be the <code><a class="source" href="static/erlang/kitty_gen_server.erl">kitty_gen_server</a></code>. It's going to be mostly similar to <code>kitty_server2</code>, with only minimal API changes. First start a new module with the following lines in it:</p>
<pre class="brush:erl">
-module(kitty_gen_server).
-behaviour(gen_server).
</pre>
<p>And try to compile it. You should get something like this:</p>
<pre class="brush:eshell">
1> c(kitty_gen_server).
./kitty_gen_server.erl:2: Warning: undefined callback function code_change/3 (behaviour 'gen_server')
./kitty_gen_server.erl:2: Warning: undefined callback function handle_call/3 (behaviour 'gen_server')
./kitty_gen_server.erl:2: Warning: undefined callback function handle_cast/2 (behaviour 'gen_server')
./kitty_gen_server.erl:2: Warning: undefined callback function handle_info/2 (behaviour 'gen_server')
./kitty_gen_server.erl:2: Warning: undefined callback function init/1 (behaviour 'gen_server')
./kitty_gen_server.erl:2: Warning: undefined callback function terminate/2 (behaviour 'gen_server')
{ok,kitty_gen_server}
</pre>
<p>The compilation worked, but there are warnings about missing callbacks. This is because of the <code>gen_server</code> behaviour. A behaviour is basically a way for a module to specify functions it expects another module to have. The behaviour is the contract sealing the deal between the well-behaved generic part of the code and the specific, error-prone part of the code (yours).</p>
<div class="note">
<p><strong>Note:</strong> both 'behavior' and 'behaviour' are accepted by the Erlang compiler.</p>
</div>
<p>Defining your own behaviours is really simple. You just need to export a function called <code>behaviour_info/1</code> implemented as follows:</p>
<pre class="brush:erl">
-module(my_behaviour).
-export([behaviour_info/1]).
%% init/1, some_fun/0 and other/3 are now expected callbacks
behaviour_info(callbacks) -> [{init,1}, {some_fun, 0}, {other, 3}];
behaviour_info(_) -> undefined.
</pre>
<p>And that's about it for behaviours. You can just use <code>-behaviour(my_behaviour).</code> in a module implementing them to get compiler warnings if you forgot a function. Anyway, back to our third kitty server.</p>
<p>The first function we had was <code>start_link/0</code>. This one can be changed to the following:</p>
<pre class="brush:erl">
start_link() -> gen_server:start_link(?MODULE, [], []).
</pre>
<p>The first parameter is the callback module, the second one is the list of parameters to pass to <code>init/1</code> and the third one is about debugging options that won't be covered right now. You could add a <a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#start_link/4">fourth parameter</a> in the first position, which would be the name to register the server with. Note that while the previous version of the function simply returned a pid, this one instead returns <code>{ok, Pid}</code>.</p>
<p>Next functions now:</p>
<pre class="brush:erl">
%% Synchronous call
order_cat(Pid, Name, Color, Description) ->
gen_server:call(Pid, {order, Name, Color, Description}).
%% This call is asynchronous
return_cat(Pid, Cat = #cat{}) ->
gen_server:cast(Pid, {return, Cat}).
%% Synchronous call
close_shop(Pid) ->
gen_server:call(Pid, terminate).
</pre>
<p>All of these calls are a one-to-one change. Note that a third parameter can be passed to <code><a class="docs" href="http://erldocs.com/17.3/stdlib/gen_server.html#call/2">gen_server:call/2-3</a></code> to give a timeout. If you don't give a timeout to the function (or the atom <code>infinity</code>), the default is set to 5 seconds. If no reply is received before time is up, the call crashes.</p>
<p>Now we'll be able to add the gen_server callbacks. The following table shows the relationship we have between calls and callbacks:</p>
<table>
<thead>
<tr>
<td>gen_server</td>
<td>YourModule</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>start/3-4</code>
</td>
<td>
<code>init/1</code>
</td>
</tr>
<tr>
<td>
<code>start_link/3-4</code>
</td>
<td>
<code>init/1</code>
</td>
</tr>
<tr>
<td>
<code>call/2-3</code>
</td>
<td>
<code>handle_call/3</code>
</td>
</tr>
<tr>
<td>
<code>cast/2</code>
</td>
<td>
<code>handle_cast/2</code>
</td>
</tr>
</tbody>
</table>
<p>And then you have the other callbacks, those that are more about special cases:</p>
<ul>
<li><code>handle_info/2</code></li>
<li><code>terminate/2</code></li>
<li><code>code_change/3</code></li>
</ul>
<p>Let's begin by changing those we already have to fit the model: <code>init/1</code>, <code>handle_call/3</code> and <code>handle_cast/2</code>.</p>
<pre class="brush:erl">
%%% Server functions
init([]) -> {ok, []}. %% no treatment of info here!
handle_call({order, Name, Color, Description}, _From, Cats) ->
if Cats =:= [] ->
{reply, make_cat(Name, Color, Description), Cats};
Cats =/= [] ->
{reply, hd(Cats), tl(Cats)}
end;
handle_call(terminate, _From, Cats) ->
{stop, normal, ok, Cats}.
handle_cast({return, Cat = #cat{}}, Cats) ->
{noreply, [Cat|Cats]}.
</pre>
<p>Again, very little has changed there. In fact, the code is now shorter, thanks to smarter abstractions. Now we get to the new callbacks. The first one is <code>handle_info/2</code>. Given this is a toy module and we have no logging system pre-defined, just outputting the unexpected messages will be enough:</p>
<pre class="brush:erl">
handle_info(Msg, Cats) ->
io:format("Unexpected message: ~p~n",[Msg]),
{noreply, Cats}.
</pre>
<p>The next one is the <code>terminate/2</code> callback. It will be very similar to the <code>terminate/1</code> private function we had:</p>
<pre class="brush:erl">
terminate(normal, Cats) ->
[io:format("~p was set free.~n",[C#cat.name]) || C <- Cats],
ok.
</pre>
<p>And then the last callback, <code>code_change/3</code>:</p>
<pre class="brush:erl">
code_change(_OldVsn, State, _Extra) ->
%% No change planned. The function is there for the behaviour,
%% but will not be used. Only a version on the next
{ok, State}.
</pre>
<p>Just remember to keep in the <code>make_cat/3</code> private function:</p>
<pre class="brush:erl">
%%% Private functions
make_cat(Name, Col, Desc) ->
#cat{name=Name, color=Col, description=Desc}.
</pre>
<p>And we can now try the brand new code:</p>
<pre class="brush:eshell">
1> c(kitty_gen_server).
{ok,kitty_gen_server}
2> rr(kitty_gen_server).
[cat]
3> {ok, Pid} = kitty_gen_server:start_link().
{ok,<0.253.0>}
4> Pid ! <<"Test handle_info">>.
Unexpected message: <<"Test handle_info">>
<<"Test handle_info">>
5> Cat = kitty_gen_server:order_cat(Pid, "Cat Stevens", white, "not actually a cat").
#cat{name = "Cat Stevens",color = white,
description = "not actually a cat"}
6> kitty_gen_server:return_cat(Pid, Cat).
ok
7> kitty_gen_server:order_cat(Pid, "Kitten Mittens", black, "look at them little paws!").
#cat{name = "Cat Stevens",color = white,
description = "not actually a cat"}
8> kitty_gen_server:order_cat(Pid, "Kitten Mittens", black, "look at them little paws!").
#cat{name = "Kitten Mittens",color = black,
description = "look at them little paws!"}
9> kitty_gen_server:return_cat(Pid, Cat).
ok
10> kitty_gen_server:close_shop(Pid).
"Cat Stevens" was set free.
ok
</pre>
<img class="right" src="static/img/mittens.png" width="156" height="132" alt="pair of wool mittens" />
<p>Oh and hot damn, it works!</p>
<p>So what can we say about this generic adventure? Probably the same generic stuff as before: separating the generic from the specific is a great idea on every point. Maintenance is simpler, complexity is reduced, the code is safer, easier to test and less prone to bugs. If there are bugs, they are easier to fix. Generic servers are only one of the many available abstractions, but they're certainly one of the most used ones. We'll see more of these abstractions and behaviours in the next chapters.</p>
<ul class="navigation">
<li><a href="what-is-otp.html" title="Previous chapter">< Previous</a></li>
<li><a href="contents.html" title="Index">Index</a></li>
<li><a href="finite-state-machines.html" title="Next chapter">Next ></a></li>
</ul>
</div><!-- content -->
<div id="footer">
<a href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License Details"><img src="static/img/cc.png" width="88" height="31" alt="Creative Commons Attribution Non-Commercial No Derivative License" /></a>
<p>Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution Non-Commercial No Derivative License</p>
</div> <!-- footer -->
</div> <!-- wrapper -->
<div id="grass" />
<script type="text/javascript" src="static/js/shCore.js"></script>
<script type="text/javascript" src="static/js/shBrushErlang2.js%3F11"></script>
<script type="text/javascript">
SyntaxHighlighter.defaults.gutter = false;
SyntaxHighlighter.all();
</script>
</body>
</html>