ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Intro.pod
Revision: 1.16
Committed: Wed Aug 26 14:02:11 2009 UTC (14 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.15: +55 -9 lines
Log Message:
added another chapter to the intro.

File Contents

# Content
1 =head1 Message Passing for the Non-Blocked Mind
2
3 =head1 Introduction and Terminology
4
5 This is a tutorial about how to get the swing of the new L<AnyEvent::MP>
6 module, which allows us to transparently pass messages to our own process
7 and to other processes on another or the same host.
8
9 What kind of messages? Well, basically a message here means a list of Perl
10 strings, numbers, hashes and arrays, anything that can be expressed as a
11 L<JSON> text (as JSON is used by default in the protocol).
12
13 And next you might ask: between which entities are those messages being
14 "passed"? Physically within or between I<nodes>: a nodes is basically a
15 process/program that use L<AnyEvent::MP> and can run either on the same or
16 different hosts.
17
18 To make this more manageable, every node can contain any number of
19 I<ports>: Ports are ultimately the receivers of your messages.
20
21 In this tutorial I'll show you how to write a simple chat server based on
22 L<AnyEvent::MP>. This example is used because it nicely shows how to organise a
23 simple application, but keep in mind that every node trusts any other, so this
24 chat cannot be used to implement a real public chat server and client system,
25 but it can be used to implement a distributed chat server for example.
26
27 =head1 System Requirements and System Setup
28
29 Before we can start we have to make sure some things work on your
30 system.
31
32 You should of course first make sure that L<AnyEvent> and L<AnyEvent::MP>
33 are installed. But how to do that is out of scope of this tutorial.
34
35 Then we have to setup a I<shared secret>: for two L<AnyEvent::MP> nodes to
36 be able to communicate with each other and authenticate each other it is
37 necessary to setup the same I<shared secret> for both of them (or use TLS
38 certificates).
39
40 The easiest way is to set this up is to use the F<aemp> utility:
41
42 aemp gensecret
43
44 This creates a F<$HOME/.perl-anyevent-mp> config file and generates a
45 random shared secret. You can copy this file to any other system and then
46 communicate with it. You can also select your own shared secret (F<aemp
47 setsecret>) and for increased security requirements you can even create
48 a TLS certificate (F<aemp gencert>), causing connections to not just be
49 authenticated, but also to be encrypted.
50
51 Connections will only be successful when the nodes that want to connect to
52 each other have the same I<shared secret> (or successfully verify the TLS
53 certificate of the other side).
54
55 B<If something does not work as expected, and for example tcpdump shows
56 that the connections are closed almost immediately, you should make sure
57 that F<~/.perl-anyevent-mp> is the same on all hosts/user accounts that
58 you try to connect with each other!>
59
60 =head1 Passing Your First Message
61
62 As start lets have a look at the messaging API. The next example is just a
63 demo to show the basic elements of message passing with L<AnyEvent::MP>.
64 It shout just print: "Ending with: 123". So here the code:
65
66 use AnyEvent;
67 use AnyEvent::MP;
68
69 initialise_node;
70
71 my $end_cv = AnyEvent->condvar;
72
73 my $port = port;
74
75 rcv $port, test => sub {
76 my ($data) = @_;
77 $end_cv->send ($data);
78 };
79
80 snd $port, test => 123;
81
82 print "Ending with: " . $end_cv->recv . "\n";
83
84 It already contains lots of the API that we are going to use. First there
85 is C<initialise_node>, which will initialise the L<AnyEvent::MP> node for that
86 process.
87
88 Next there is the C<port> function which will create a I<port id> for us, where
89 we can wait for messages and send messages to. The port id is a simple string,
90 which acts as identifier for a port, with the form C<noderef#portname>. The
91 I<noderef> is basically a string that refers to the node and also contains
92 enough information to contact the node from the outside. The I<portname> is
93 usually just a random string.
94
95 Next the call to C<rcv> sets up a receiver callback. The first element in
96 every message is usually denoting it's I<type> or I<tag>. Which should be a
97 simple string, the second argument to C<rcv> lets us match the tag of a
98 message. If it matches, the callback will be called, with the remaining
99 elements of the message as arguments.
100
101 C<snd> sends a message. The message consists of two elements: The string
102 C<'test'> and the number C<123>.
103
104 The message arrives in the callback we setup with C<rcv> and will trigger the
105 condition variable C<$end_cv> to deliver the result value and end the program.
106
107 =head1 The Chat Client
108
109 OK, lets start by implementing the "frontend" of the client. We will
110 develop the client first and postpone the server for later, as the most
111 complex things actually happen in the client.
112
113 We will use L<AnyEvent::Handle> to do non-blocking IO read on standard
114 input (all of this code deals with actually handling user input, no
115 message passing yet):
116
117 #!perl
118
119 use AnyEvent;
120 use AnyEvent::Handle;
121
122 sub send_message {
123 die "This is where we will send the messages to the server"
124 . "in the next step of this tutorial.\n"
125 }
126
127 # make an AnyEvent condition variable for the 'quit' condition
128 # (when we want to exit the client).
129 my $quit_cv = AnyEvent->condvar;
130
131 my $stdin_hdl = AnyEvent::Handle->new (
132 fh => *STDIN,
133 on_error => sub { $quit_cv->send },
134 on_read => sub {
135 my ($hdl) = @_;
136
137 $hdl->push_read (line => sub {
138 my ($hdl, $line) = @_;
139
140 if ($line =~ /^\/quit/) { # /quit will end the client
141 $quit_cv->send;
142 } else {
143 send_message ($line);
144 }
145 });
146 }
147 );
148
149 $quit_cv->recv;
150
151 This is now a very basic client. Explaining explicitly what
152 L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope
153 of this document, please consult L<AnyEvent::Intro> or the manual pages
154 for L<AnyEvent> and L<AnyEvent::Handle>.
155
156 =head1 First Steps Into Messaging
157
158 To supply the C<send_message> function we now take a look at
159 L<AnyEvent::MP>. This is an example of how it might look like:
160
161 ... # the use lines from the above snippet
162
163 use AnyEvent::MP;
164
165 sub send_message {
166 my ($msg) = @_;
167
168 snd $server_port, message => $msg;
169 }
170
171 ... # the rest of the above script
172
173 The C<snd> function is exported by L<AnyEvent::MP>, it stands for 'send
174 a message'. The first argument is the I<port> (a I<port> is something
175 that can receive messages, represented by a printable string) of the
176 server which will receive the message. How we get this port will be
177 explained in the next step.
178
179 The remaining arguments of C<snd> are C<message> and C<$msg>, the first
180 two elements of the I<message> (a I<message> in L<AnyEvent::MP> is a
181 simple list of values, which can be sent to a I<port>).
182
183 So all the function does is send the two values C<message> (a constant
184 string to tell the server what to expect) and the actual message string.
185
186 Thats all fine and simple so far, but where do we get the
187 C<$server_port>? Well, we need to get the unique I<port id> of the
188 server's port where it wants to receive all the incoming chat messages. A
189 I<port id> is unfortunately a very unique string, which we are unable to
190 know in advance. But L<AnyEvent::MP> supports the concept of 'registered
191 ports', which is basically a port on the server side registered under
192 a well known name.
193
194 For example, the server has a port for receiving chat messages with a
195 unique I<port id> and registers it under the name C<chatter>.
196
197 BTW, these "registered port names" should follow similar rules as Perl
198 identifiers, so you should prefix them with your package/module name to
199 make them unique, unless you use them in the main program.
200
201 As I<messages> can only be sent to a I<port id> and not just to some name
202 we have to ask the server node for the I<port id> of the port registered
203 as C<chatter>.
204
205 =head1 Finding The Chatter Port
206
207 Ok, lots of talk, now some code. Now we will actually get the
208 C<$server_port> from the backend:
209
210 ...
211
212 use AnyEvent::MP;
213
214 my $server_node = "127.0.0.1:1299";
215
216 my $client_port = port;
217
218 snd $server_node, lookup => "chatter", $client_port, "resolved";
219
220 my $resolved_cv = AnyEvent->condvar;
221 my $server_port;
222
223 # setup a receiver callback for the 'resolved' message:
224 rcv $client_port, resolved => sub {
225 my ($tag, $chatter_port_id) = @_;
226
227 print "Resolved the server port 'chatter' to $chatter_port_id\n";
228 $server_port = $chatter_port_id;
229
230 $resolved_cv->send;
231 1
232 };
233
234 # lets block the client until we have resolved the server port.
235 $resolved_cv->recv;
236
237 # now setup another receiver callback for the chat messages:
238 rcv $client_port, message => sub {
239 my ($tag, $msg) = @_;
240
241 print "chat> $msg\n";
242 0
243 };
244
245 # send a 'join' message to the server:
246 snd $server_port, join => "$client_port";
247
248 sub send_message { ...
249
250 Now that was a lot of new stuff:
251
252 First we define the C<$server_node>: In order to refer to another node
253 we need some kind of string to reference it - the node reference. The
254 I<noderef> is basically a comma separated list of C<address:port>
255 pairs. We assume in this tutorial that the server runs on C<127.0.0.1>
256 (localhost) on port 1299, which results in the noderef C<127.0.0.1:1299>.
257
258 Next, in order to receive a reply from the other node or the server we
259 need to have a I<port> that messages can be sent to. This is what the
260 C<port> function will do for us, it just creates a new local port and
261 returns it's I<port ID> that can then be used to receive messages.
262
263 When you look carefully, you will see that the first C<snd> uses the
264 C<$server_node> (a noderef) as destination port. Well, what I didn't
265 tell you yet is that each I<node> has a default I<port> to receive
266 messages. The ID of this port is the same as the noderef.
267
268 This I<default port> provides some special services for us, for example
269 resolving a registered name to a I<port id> (a-ha! finally!).
270
271 This is exactly what this line does:
272
273 snd $server_node, lookup => "chatter", $client_port, "resolved";
274
275 This sends a message with first element being C<lookup>, followed by the
276 (hopefully) registered port name that we want to resolve to a I<port
277 id>: C<chatter>. And in order for the server node to be able to send us
278 back the resolved I<port ID> we have to tell it where to send it: The
279 result message will be sent to C<$client_port> (the I<port id> of the
280 port we just created), and will have the string C<resolved> as the first
281 element.
282
283 When the node receives this message, it will look up the name, gobble up
284 all the extra arguments we passed, append the resolved name, and send the
285 resulting list as a message.
286
287 Next we register a receiver for this C<lookup>-request.
288
289 rcv $client_port, resolved => sub {
290 my ($tag, $chatter_port_id) = @_;
291 ...
292 1
293 };
294
295 This sets up a receiver on our own port for messages with the first
296 element being the string C<resolved>. Receivers can match the contents of
297 the messages before actually executing the specified callback.
298
299 B<Please note> that the every C<rcv> callback has to return either a true
300 or a false value, indicating whether it is B<successful>/B<done> (true) or
301 still wants to B<continue> (false) receiving messages.
302
303 In this case we tell the C<$client_port> to look into all the messages
304 it receives and look for the string C<resolved> in the first element of
305 the message. If it is found, the given callback will be called with the
306 message elements as arguments.
307
308 Using a string as the first element of the message is called I<tagging>
309 the message. It's common practise to code the 'type' of a message into
310 it's first element, as this allows for simple matching.
311
312 The result message will contain the I<port ID> of the well known port
313 C<chatter> as second element, which will be stored in C<$chatter_port_id>.
314
315 This port ID will then be stored in C<$server_port>, followed by calling
316 C<send> on $resolved_cv> so the program will continue.
317
318 The callback then returns a C<1> (a true value), to indicate that it has
319 done it's job and doesn't want to receive further C<resolved> messages.
320
321 After this the chat message receiver callback is registered with the port:
322
323 rcv $client_port, message => sub {
324 my ($tag, $msg) = @_;
325
326 print "chat> $msg\n";
327
328 0
329 };
330
331 We assume that all messages that are broadcast to the clients by the
332 server contain the string tag C<message> as first element, and the actual
333 message as second element. The callback returns a false value this time,
334 to indicate that it is not yet done and wants to receive further messages.
335
336 The last thing to do is to tell the server to send us new chat messages
337 from other clients. We do so by sending the message C<join> followed by
338 our own I<port ID>.
339
340 # send the server a 'join' message:
341 snd $server_port, join => $client_port;
342
343 This way the server knows where to send all the new messages to.
344
345 =head1 The Completed Client
346
347 This is the complete client script:
348
349 #!perl
350
351 use AnyEvent;
352 use AnyEvent::Handle;
353 use AnyEvent::MP;
354
355 my $server_node = "127.0.0.1:1299";
356
357 my $client_port = port;
358
359 snd $server_node, lookup => "chatter", $client_port, "resolved";
360
361 my $resolved_cv = AnyEvent->condvar;
362 my $server_port;
363
364 # setup a receiver callback for the 'resolved' message:
365 rcv $client_port, resolved => sub {
366 my ($tag, $chatter_port_id) = @_;
367
368 print "Resolved the server port 'chatter' to $chatter_port_id\n";
369 $server_port = $chatter_port_id;
370
371 $resolved_cv->send;
372 1
373 };
374
375 # lets block the client until we have resolved the server port.
376 $resolved_cv->recv;
377
378 # now setup another receiver callback for the chat messages:
379 rcv $client_port, message => sub {
380 my ($tag, $msg) = @_;
381
382 print "chat> $msg\n";
383 0
384 };
385
386 # send a 'join' message to the server:
387 snd $server_port, join => "$client_port";
388
389 sub send_message {
390 my ($msg) = @_;
391
392 snd $server_port, message => $msg;
393 }
394
395 # make an AnyEvent condition variable for the 'quit' condition
396 # (when we want to exit the client).
397 my $quit_cv = AnyEvent->condvar;
398
399 my $stdin_hdl = AnyEvent::Handle->new (
400 fh => *STDIN,
401 on_error => sub { $quit_cv->send },
402 on_read => sub {
403 my ($hdl) = @_;
404
405 $hdl->push_read (line => sub {
406 my ($hdl, $line) = @_;
407
408 if ($line =~ /^\/quit/) { # /quit will end the client
409 $quit_cv->send;
410 } else {
411 send_message ($line);
412 }
413 });
414 }
415 );
416
417 $quit_cv->recv;
418
419 =head1 The Server
420
421 Ok, we finally come to the server.
422
423 The server of course also needs to set up a port, and in addition needs to
424 I<register> it, so the clients can find it.
425
426 Again, let's jump directly into the code:
427
428 #!perl
429
430 use AnyEvent;
431 use AnyEvent::MP;
432
433 become_public "127.0.0.1:1299";
434
435 my $chatter_port = port;
436
437 reg $chatter_port, "chatter";
438
439 my %client_ports;
440
441 rcv $chatter_port,
442 join => sub {
443 my ($tag, $client_port) = @_;
444
445 print "got new client port: $client_port\n";
446 $client_ports{$client_port} = 1;
447
448 0
449 },
450 message => sub {
451 my ($tag, $msg) = @_;
452
453 print "message> $msg\n";
454
455 snd $_, message => $msg
456 for keys %client_ports;
457
458 0
459 };
460
461 AnyEvent->condvar->recv;
462
463 That is all. Looks much simpler than the client, doesn't it?
464
465 Let's quickly look over it, as C<rcv> has already been discussed in the
466 client part of this tutorial above.
467
468 First this:
469
470 become_public "127.0.0.1:1299";
471
472 This will tell our I<node> to become a I<public> node, which means that it
473 can be contacted via TCP. The first argument should be the I<noderef> the
474 server wants to be reachable at. In this case it's the TCP port 1299 on
475 C<127.0.0.1>.
476
477 Next we set up two receivers, one for the C<join> messages and another one
478 for the actual messages of type C<messsage>. This is done with a single
479 call to C<rcv>, which allows multiple C<< match => $callback >> pairs.
480
481 In the C<join> callback we receive the client port, which is simply
482 remembered in the C<%client_ports> hash. In the C<message> callback we
483 just iterate through all known C<%client_ports> and relay the message to
484 them.
485
486 That concludes the server.
487
488 =head1 The Remaining Problems
489
490 The implementation as shown still has some bugs. For instance: How does
491 the server know that the client isn't there anymore, so it can clean up
492 the C<%client_ports> hash? Also, the chat messages have no originator, so
493 we don't know who actually sent the message (which would be quite useful
494 for human-to-human interaction: to know who the other one is :).
495
496 But aside from these issues I hope this tutorial showed you the basics of
497 L<AnyEvent::MP> and explained some common idioms.
498
499 How to solve the reliability and C<%client_ports> cleanup problem will
500 be explained later in this tutorial (TODO).
501
502 =head1 Inside The Protocol
503
504 Now, for the interested parties, let me explain some details about the protocol
505 that L<AnyEvent::MP> nodes use to communicate to each other. If you are not
506 interested you can skip this section.
507
508 Usually TCP is used for communication. Each I<node>, if configured to be
509 a I<public> node with the C<initialise_node> function will listen on the
510 configured TCP port (default is 4040).
511
512 If then one I<node> wants to send a message to another I<node> it will
513 connect to the host and port given in the I<port ID>.
514
515 Then some handshaking occurs to check whether both I<nodes> know the
516 I<shared secret>. Optionally, TLS can be enabled (about how to do this
517 exactly please consult the L<AnyEvent::MP> man page, just a hint: It
518 should be enough to put the private key and (self signed) certificate in
519 the C<~/.aemp-secret> file of all nodes).
520
521 After the handshake, messages will be exchanged using a serialiser
522 (usually L<JSON> is used for this, but it is also possible to use other
523 serialization formats such as L<Storable>).
524
525 =head1 SEE ALSO
526
527 L<AnyEvent>
528
529 L<AnyEvent::Handle>
530
531 L<AnyEvent::MP>
532
533 =head1 AUTHOR
534
535 Robin Redeker <elmex@ta-sa.org>
536