ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Intro.pod
Revision: 1.18
Committed: Fri Aug 28 14:43:53 2009 UTC (14 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.17: +107 -6 lines
Log Message:
more work on 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 It's custom in L<AnyEvent::MP> to have a string which describes the type of the
14 message as first element (this is called a I<tag> in L<AnyEvent::MP>), as some
15 API functions (C<rcv>) support matching it directly. So supposedly you want to
16 send a ping message with your current time to something, this is how such a
17 message might look like (in Perl syntax):
18
19 ['ping', 1251381636]
20
21 And next you might ask: between which entities are those messages being
22 I<passed>? They are I<passed> between I<ports>. I<ports> are just sources and
23 destinations for messages. How do these ports relate to things you know? Well,
24 each I<port> belongs to a I<node>, and a I<node> is just the UNIX process that
25 runs your L<AnyEvent::MP> application.
26
27 Each I<node> is distinguished from other I<nodes> running on the same host or
28 multiple hosts in a network by it's I<node ID>. A I<node ID> can be manually
29 assigned or L<AnyEvent::MP> will assign one it self for you.
30
31 So, you might want to visualize it like this (setup is two nodes (more are of
32 course possible): Node C<A> (in UNIX process 7066) with ports C<ABC> and C<DEF>
33 and C<B> (in UNIX process 8321) with ports C<FOO> and C<BAR>).
34
35
36 |- PID: 7066 -| |- PID: 8321 -|
37 | | | |
38 | Node ID: A | | Node ID: B |
39 | | | |
40 | Port ABC =|= <----\ /-----> =|= Port FOO |
41 | | X | |
42 | Port DEF =|= <----/ \-----> =|= Port BAR |
43 | | | |
44 |-------------| |-------------|
45
46 The strings for the ports here are just for illustrative purposes. Even if in
47 reality I<ports> in L<AnyEvent::MP> are also identified by strings they can't
48 be choosen manually and are assigned randomly. These I<port ids> should also
49 not be used directly for other purposes than referring to an endpoint for
50 messages.
51
52 The next sections will explain the API of L<AnyEvent::MP>. First the API is
53 laid out by simple examples. Later some more complex idioms are introduced,
54 which are maybe useful to solve some real world purposes.
55
56 # In this tutorial I'll show you how to write a simple chat server based on
57 # L<AnyEvent::MP>. This example is used because it nicely shows how to organise a
58 # simple application, but keep in mind that every node trusts any other, so this
59 # chat cannot be used to implement a real public chat server and client system,
60 # but it can be used to implement a distributed chat server for example.
61
62 =head1 Passing Your First Message
63
64 As start lets have a look at the messaging API. The next example is just a
65 demo to show the basic elements of message passing with L<AnyEvent::MP>.
66 It shout just print: "Ending with: 123". So here the code:
67
68 use AnyEvent;
69 use AnyEvent::MP;
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 most functions of the essential L<AnyEvent::MP> API.
85
86 First there is the C<port> function which will create a I<port> and will return
87 it's I<port id>.
88
89 That I<port id> can be used to send and receive messages. That I<port id> is a
90 simple string and can be safely passed to other I<nodes> in the network to
91 refer to that specific port (usually used for RPC, where you need to
92 tell the other end which I<port> to send the reply to).
93
94 Next function is C<rcv>:
95
96 rcv $port, test => sub { ... };
97
98 It sets up a receiver callback on a specific I<port> which needs to be
99 specified as the first argument. The next argument, in this example C<test>, is
100 a I<tag> match. This means that whenever a message, with the first element
101 being the string C<tag>, is received the callback is called with the remaining
102 parts of that message.
103
104 Messages can be send with the C<snd> function, which looks like this in the
105 example above:
106
107 snd $port, test => 123;
108
109 This will send the message C<['test', 123]> to the I<port> with the I<port id>
110 in C<$port>. The receiver got a I<tag> match on C<test> and will call the
111 callback with the first argument being the number C<123>.
112
113 That callback then just passes that number on to the I<condition variable>
114 C<$end_cv> which will then pass the value to the print. But I<condition
115 variables> are out of the scope of this tutorial. So please consult the
116 L<AnyEvent::Intro> about them.
117
118 But passing messages inside one process is boring, but before we can continue
119 and take the next step to interprocess message passing we first have to make
120 sure some things have been setup.
121
122 =head1 System Requirements and System Setup
123
124 Before we can start with real IPC we have to make sure some things work on your
125 system.
126
127 First we have to setup a I<shared secret>: for two L<AnyEvent::MP> I<nodes> to
128 be able to communicate with each other and authenticate each other it is
129 necessary to setup the same I<shared secret> for both of them (or use TLS
130 certificates).
131
132 The easiest way is to set this up is to use the F<aemp> utility:
133
134 aemp gensecret
135
136 This creates a F<$HOME/.perl-anyevent-mp> config file and generates a random
137 shared secret. You can copy this file to any other system and then communicate
138 over the network (via TCP) with it. You can also select your own shared secret
139 (F<aemp setsecret>) and for increased security requirements you can even create
140 a TLS certificate (F<aemp gencert>), causing connections to not just be
141 authenticated, but also to be encrypted.
142
143 Connections will only be successful when the I<nodes> that want to connect to
144 each other have the same I<shared secret> (or successfully verify the TLS
145 certificate of the other side).
146
147 B<If something does not work as expected, and for example tcpdump shows
148 that the connections are closed almost immediately, you should make sure
149 that F<~/.perl-anyevent-mp> is the same on all hosts/user accounts that
150 you try to connect with each other!>
151
152 Thats all for now, there is more fiddling around with the C<aemp> utility
153 later.
154
155 =head1 Passing Messages Between Processes
156
157 =head2 The Receiver
158
159 Lets split the previous example up into two small programs. First the
160 receiver application:
161
162 #!/opt/perl/bin/perl
163 use AnyEvent;
164 use AnyEvent::MP;
165 use AnyEvent::MP::Global;
166
167 initialise_node "eg_simple_receiver";
168
169 my $port = port;
170
171 AnyEvent::MP::Global::register $port, "eg_receivers";
172
173 rcv $port, test => sub {
174 my ($data, $reply_port) = @_;
175
176 print "Received data: " . $data . "\n";
177 };
178
179 AnyEvent->condvar->recv;
180
181 =head3 AnyEvent::MP::Global
182
183 Now, that wasn't too bad, was it? Ok, lets step through the new functions
184 and modules that have been used. For starters there is now an additional
185 module loaded: L<AnyEvent::MP::Global>.
186
187 That module provides us with a I<global registry>, which lets us share data
188 among all I<nodes> in a network. Why do we need it you might ask?
189
190 The I<port ids> are just random strings, assigned by L<AnyEvent::MP>. We can't
191 know those I<port ids> in advance, so we don't know which I<port id> to send
192 messages to if the message is to be passed between I<nodes> (or UNIX
193 processes). To find the right I<port> of another I<node> in the network we will
194 need to communicate that somehow to the sender. And exactly that is what
195 L<AnyEvent::MP::Global> provides.
196
197 =head3 initialise_node And The Network
198
199 Now, lets have a look at the next new thing, the C<initialise_node>:
200
201 initialise_node "eg_simple_receiver";
202
203 Before we are able to send messages to other nodes we have to initialise
204 ourself. The first argument, the string C<"eg_simple_receiver">, is called the
205 I<profile> of this node. A profile holds some information about the application
206 that is going to be a node in an L<AnyEvent::MP> network.
207
208 Most importantly the profile allows you to set the I<node id> that your
209 application will use. You can also set I<binds> in the profile, meaning that
210 you can define TCP ports that the application will listen on for incoming
211 connections from other nodes of the network.
212
213 Next you can configure I<seeds> in profile. A I<seed> is just a TCP endpoint
214 which tells the application where to find other nodes of it's network. To
215 explain this a bit more detailed we have to look at the topology of an
216 L<AnyEvent::MP> network. The topology is called a I<fully connected mesh>, here
217 an example with 4 nodes:
218
219 N1--N2
220 | \/ |
221 | /\ |
222 N3--N4
223
224 Now imagine another I<node> C<N5>. wants to connect itself to that network:
225
226 N1--N2
227 | \/ | N5
228 | /\ |
229 N3--N4
230
231 The new node needs to know the I<binds> of all of those 4 already connected
232 nodes. And exactly this is what the I<seeds> are for. Now lets assume that
233 the new node C<N5> has as I<seed> the TCP endpoint of the node C<N2>.
234 It then connects to C<N2>:
235
236 N1--N2____
237 | \/ | N5
238 | /\ |
239 N3--N4
240
241 C<N2> then tells C<N5> the I<binds> of the other nodes it is connected to,
242 and C<N5> builds up the rest of the connections:
243
244 /--------\
245 N1--N2____|
246 | \/ | N5
247 | /\ | /|
248 N3--N4--- |
249 \________/
250
251 Finished. C<N5> is now happily connected to the rest of the network.
252
253 =head1 The Chat Client
254
255 OK, lets start by implementing the "frontend" of the client. We will
256 develop the client first and postpone the server for later, as the most
257 complex things actually happen in the client.
258
259 We will use L<AnyEvent::Handle> to do non-blocking IO read on standard
260 input (all of this code deals with actually handling user input, no
261 message passing yet):
262
263 #!perl
264
265 use AnyEvent;
266 use AnyEvent::Handle;
267
268 sub send_message {
269 die "This is where we will send the messages to the server"
270 . "in the next step of this tutorial.\n"
271 }
272
273 # make an AnyEvent condition variable for the 'quit' condition
274 # (when we want to exit the client).
275 my $quit_cv = AnyEvent->condvar;
276
277 my $stdin_hdl = AnyEvent::Handle->new (
278 fh => *STDIN,
279 on_error => sub { $quit_cv->send },
280 on_read => sub {
281 my ($hdl) = @_;
282
283 $hdl->push_read (line => sub {
284 my ($hdl, $line) = @_;
285
286 if ($line =~ /^\/quit/) { # /quit will end the client
287 $quit_cv->send;
288 } else {
289 send_message ($line);
290 }
291 });
292 }
293 );
294
295 $quit_cv->recv;
296
297 This is now a very basic client. Explaining explicitly what
298 L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope
299 of this document, please consult L<AnyEvent::Intro> or the manual pages
300 for L<AnyEvent> and L<AnyEvent::Handle>.
301
302 =head1 First Steps Into Messaging
303
304 To supply the C<send_message> function we now take a look at
305 L<AnyEvent::MP>. This is an example of how it might look like:
306
307 ... # the use lines from the above snippet
308
309 use AnyEvent::MP;
310
311 sub send_message {
312 my ($msg) = @_;
313
314 snd $server_port, message => $msg;
315 }
316
317 ... # the rest of the above script
318
319 The C<snd> function is exported by L<AnyEvent::MP>, it stands for 'send
320 a message'. The first argument is the I<port> (a I<port> is something
321 that can receive messages, represented by a printable string) of the
322 server which will receive the message. How we get this port will be
323 explained in the next step.
324
325 The remaining arguments of C<snd> are C<message> and C<$msg>, the first
326 two elements of the I<message> (a I<message> in L<AnyEvent::MP> is a
327 simple list of values, which can be sent to a I<port>).
328
329 So all the function does is send the two values C<message> (a constant
330 string to tell the server what to expect) and the actual message string.
331
332 Thats all fine and simple so far, but where do we get the
333 C<$server_port>? Well, we need to get the unique I<port id> of the
334 server's port where it wants to receive all the incoming chat messages. A
335 I<port id> is unfortunately a very unique string, which we are unable to
336 know in advance. But L<AnyEvent::MP> supports the concept of 'registered
337 ports', which is basically a port on the server side registered under
338 a well known name.
339
340 For example, the server has a port for receiving chat messages with a
341 unique I<port id> and registers it under the name C<chatter>.
342
343 BTW, these "registered port names" should follow similar rules as Perl
344 identifiers, so you should prefix them with your package/module name to
345 make them unique, unless you use them in the main program.
346
347 As I<messages> can only be sent to a I<port id> and not just to some name
348 we have to ask the server node for the I<port id> of the port registered
349 as C<chatter>.
350
351 =head1 Finding The Chatter Port
352
353 Ok, lots of talk, now some code. Now we will actually get the
354 C<$server_port> from the backend:
355
356 ...
357
358 use AnyEvent::MP;
359
360 my $server_node = "127.0.0.1:1299";
361
362 my $client_port = port;
363
364 snd $server_node, lookup => "chatter", $client_port, "resolved";
365
366 my $resolved_cv = AnyEvent->condvar;
367 my $server_port;
368
369 # setup a receiver callback for the 'resolved' message:
370 rcv $client_port, resolved => sub {
371 my ($tag, $chatter_port_id) = @_;
372
373 print "Resolved the server port 'chatter' to $chatter_port_id\n";
374 $server_port = $chatter_port_id;
375
376 $resolved_cv->send;
377 1
378 };
379
380 # lets block the client until we have resolved the server port.
381 $resolved_cv->recv;
382
383 # now setup another receiver callback for the chat messages:
384 rcv $client_port, message => sub {
385 my ($tag, $msg) = @_;
386
387 print "chat> $msg\n";
388 0
389 };
390
391 # send a 'join' message to the server:
392 snd $server_port, join => "$client_port";
393
394 sub send_message { ...
395
396 Now that was a lot of new stuff:
397
398 First we define the C<$server_node>: In order to refer to another node
399 we need some kind of string to reference it - the node reference. The
400 I<noderef> is basically a comma separated list of C<address:port>
401 pairs. We assume in this tutorial that the server runs on C<127.0.0.1>
402 (localhost) on port 1299, which results in the noderef C<127.0.0.1:1299>.
403
404 Next, in order to receive a reply from the other node or the server we
405 need to have a I<port> that messages can be sent to. This is what the
406 C<port> function will do for us, it just creates a new local port and
407 returns it's I<port ID> that can then be used to receive messages.
408
409 When you look carefully, you will see that the first C<snd> uses the
410 C<$server_node> (a noderef) as destination port. Well, what I didn't
411 tell you yet is that each I<node> has a default I<port> to receive
412 messages. The ID of this port is the same as the noderef.
413
414 This I<default port> provides some special services for us, for example
415 resolving a registered name to a I<port id> (a-ha! finally!).
416
417 This is exactly what this line does:
418
419 snd $server_node, lookup => "chatter", $client_port, "resolved";
420
421 This sends a message with first element being C<lookup>, followed by the
422 (hopefully) registered port name that we want to resolve to a I<port
423 id>: C<chatter>. And in order for the server node to be able to send us
424 back the resolved I<port ID> we have to tell it where to send it: The
425 result message will be sent to C<$client_port> (the I<port id> of the
426 port we just created), and will have the string C<resolved> as the first
427 element.
428
429 When the node receives this message, it will look up the name, gobble up
430 all the extra arguments we passed, append the resolved name, and send the
431 resulting list as a message.
432
433 Next we register a receiver for this C<lookup>-request.
434
435 rcv $client_port, resolved => sub {
436 my ($tag, $chatter_port_id) = @_;
437 ...
438 1
439 };
440
441 This sets up a receiver on our own port for messages with the first
442 element being the string C<resolved>. Receivers can match the contents of
443 the messages before actually executing the specified callback.
444
445 B<Please note> that the every C<rcv> callback has to return either a true
446 or a false value, indicating whether it is B<successful>/B<done> (true) or
447 still wants to B<continue> (false) receiving messages.
448
449 In this case we tell the C<$client_port> to look into all the messages
450 it receives and look for the string C<resolved> in the first element of
451 the message. If it is found, the given callback will be called with the
452 message elements as arguments.
453
454 Using a string as the first element of the message is called I<tagging>
455 the message. It's common practise to code the 'type' of a message into
456 it's first element, as this allows for simple matching.
457
458 The result message will contain the I<port ID> of the well known port
459 C<chatter> as second element, which will be stored in C<$chatter_port_id>.
460
461 This port ID will then be stored in C<$server_port>, followed by calling
462 C<send> on $resolved_cv> so the program will continue.
463
464 The callback then returns a C<1> (a true value), to indicate that it has
465 done it's job and doesn't want to receive further C<resolved> messages.
466
467 After this the chat message receiver callback is registered with the port:
468
469 rcv $client_port, message => sub {
470 my ($tag, $msg) = @_;
471
472 print "chat> $msg\n";
473
474 0
475 };
476
477 We assume that all messages that are broadcast to the clients by the
478 server contain the string tag C<message> as first element, and the actual
479 message as second element. The callback returns a false value this time,
480 to indicate that it is not yet done and wants to receive further messages.
481
482 The last thing to do is to tell the server to send us new chat messages
483 from other clients. We do so by sending the message C<join> followed by
484 our own I<port ID>.
485
486 # send the server a 'join' message:
487 snd $server_port, join => $client_port;
488
489 This way the server knows where to send all the new messages to.
490
491 =head1 The Completed Client
492
493 This is the complete client script:
494
495 #!perl
496
497 use AnyEvent;
498 use AnyEvent::Handle;
499 use AnyEvent::MP;
500
501 my $server_node = "127.0.0.1:1299";
502
503 my $client_port = port;
504
505 snd $server_node, lookup => "chatter", $client_port, "resolved";
506
507 my $resolved_cv = AnyEvent->condvar;
508 my $server_port;
509
510 # setup a receiver callback for the 'resolved' message:
511 rcv $client_port, resolved => sub {
512 my ($tag, $chatter_port_id) = @_;
513
514 print "Resolved the server port 'chatter' to $chatter_port_id\n";
515 $server_port = $chatter_port_id;
516
517 $resolved_cv->send;
518 1
519 };
520
521 # lets block the client until we have resolved the server port.
522 $resolved_cv->recv;
523
524 # now setup another receiver callback for the chat messages:
525 rcv $client_port, message => sub {
526 my ($tag, $msg) = @_;
527
528 print "chat> $msg\n";
529 0
530 };
531
532 # send a 'join' message to the server:
533 snd $server_port, join => "$client_port";
534
535 sub send_message {
536 my ($msg) = @_;
537
538 snd $server_port, message => $msg;
539 }
540
541 # make an AnyEvent condition variable for the 'quit' condition
542 # (when we want to exit the client).
543 my $quit_cv = AnyEvent->condvar;
544
545 my $stdin_hdl = AnyEvent::Handle->new (
546 fh => *STDIN,
547 on_error => sub { $quit_cv->send },
548 on_read => sub {
549 my ($hdl) = @_;
550
551 $hdl->push_read (line => sub {
552 my ($hdl, $line) = @_;
553
554 if ($line =~ /^\/quit/) { # /quit will end the client
555 $quit_cv->send;
556 } else {
557 send_message ($line);
558 }
559 });
560 }
561 );
562
563 $quit_cv->recv;
564
565 =head1 The Server
566
567 Ok, we finally come to the server.
568
569 The server of course also needs to set up a port, and in addition needs to
570 I<register> it, so the clients can find it.
571
572 Again, let's jump directly into the code:
573
574 #!perl
575
576 use AnyEvent;
577 use AnyEvent::MP;
578
579 become_public "127.0.0.1:1299";
580
581 my $chatter_port = port;
582
583 reg $chatter_port, "chatter";
584
585 my %client_ports;
586
587 rcv $chatter_port,
588 join => sub {
589 my ($tag, $client_port) = @_;
590
591 print "got new client port: $client_port\n";
592 $client_ports{$client_port} = 1;
593
594 0
595 },
596 message => sub {
597 my ($tag, $msg) = @_;
598
599 print "message> $msg\n";
600
601 snd $_, message => $msg
602 for keys %client_ports;
603
604 0
605 };
606
607 AnyEvent->condvar->recv;
608
609 That is all. Looks much simpler than the client, doesn't it?
610
611 Let's quickly look over it, as C<rcv> has already been discussed in the
612 client part of this tutorial above.
613
614 First this:
615
616 become_public "127.0.0.1:1299";
617
618 This will tell our I<node> to become a I<public> node, which means that it
619 can be contacted via TCP. The first argument should be the I<noderef> the
620 server wants to be reachable at. In this case it's the TCP port 1299 on
621 C<127.0.0.1>.
622
623 Next we set up two receivers, one for the C<join> messages and another one
624 for the actual messages of type C<messsage>. This is done with a single
625 call to C<rcv>, which allows multiple C<< match => $callback >> pairs.
626
627 In the C<join> callback we receive the client port, which is simply
628 remembered in the C<%client_ports> hash. In the C<message> callback we
629 just iterate through all known C<%client_ports> and relay the message to
630 them.
631
632 That concludes the server.
633
634 =head1 The Remaining Problems
635
636 The implementation as shown still has some bugs. For instance: How does
637 the server know that the client isn't there anymore, so it can clean up
638 the C<%client_ports> hash? Also, the chat messages have no originator, so
639 we don't know who actually sent the message (which would be quite useful
640 for human-to-human interaction: to know who the other one is :).
641
642 But aside from these issues I hope this tutorial showed you the basics of
643 L<AnyEvent::MP> and explained some common idioms.
644
645 How to solve the reliability and C<%client_ports> cleanup problem will
646 be explained later in this tutorial (TODO).
647
648 =head1 Inside The Protocol
649
650 Now, for the interested parties, let me explain some details about the protocol
651 that L<AnyEvent::MP> nodes use to communicate to each other. If you are not
652 interested you can skip this section.
653
654 Usually TCP is used for communication. Each I<node>, if configured to be
655 a I<public> node with the C<initialise_node> function will listen on the
656 configured TCP port (default is 4040).
657
658 If then one I<node> wants to send a message to another I<node> it will
659 connect to the host and port given in the I<port ID>.
660
661 Then some handshaking occurs to check whether both I<nodes> know the
662 I<shared secret>. Optionally, TLS can be enabled (about how to do this
663 exactly please consult the L<AnyEvent::MP> man page, just a hint: It
664 should be enough to put the private key and (self signed) certificate in
665 the C<~/.aemp-secret> file of all nodes).
666
667 After the handshake, messages will be exchanged using a serialiser
668 (usually L<JSON> is used for this, but it is also possible to use other
669 serialization formats such as L<Storable>).
670
671 =head1 SEE ALSO
672
673 L<AnyEvent>
674
675 L<AnyEvent::Handle>
676
677 L<AnyEvent::MP>
678
679 =head1 AUTHOR
680
681 Robin Redeker <elmex@ta-sa.org>
682