ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-MP/MP/Intro.pod
Revision: 1.17
Committed: Thu Aug 27 15:50:50 2009 UTC (14 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.16: +111 -66 lines
Log Message:
rewrote first 3 sections of 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: Node C<A> (in
32 UNIX process 7066) with ports C<ABC> and C<DEF> and C<B> (in UNIX process 8321)
33 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 refering to an endpoint for
50 messages.
51
52 The next sections will explain the API of L<AnyEvent::MP>. First the API is
53 layed out by simple examples. Later some more complex idioms are introduced,
54 which are maybe useful to solve some realworld 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 =head1 The Chat Client
153
154 OK, lets start by implementing the "frontend" of the client. We will
155 develop the client first and postpone the server for later, as the most
156 complex things actually happen in the client.
157
158 We will use L<AnyEvent::Handle> to do non-blocking IO read on standard
159 input (all of this code deals with actually handling user input, no
160 message passing yet):
161
162 #!perl
163
164 use AnyEvent;
165 use AnyEvent::Handle;
166
167 sub send_message {
168 die "This is where we will send the messages to the server"
169 . "in the next step of this tutorial.\n"
170 }
171
172 # make an AnyEvent condition variable for the 'quit' condition
173 # (when we want to exit the client).
174 my $quit_cv = AnyEvent->condvar;
175
176 my $stdin_hdl = AnyEvent::Handle->new (
177 fh => *STDIN,
178 on_error => sub { $quit_cv->send },
179 on_read => sub {
180 my ($hdl) = @_;
181
182 $hdl->push_read (line => sub {
183 my ($hdl, $line) = @_;
184
185 if ($line =~ /^\/quit/) { # /quit will end the client
186 $quit_cv->send;
187 } else {
188 send_message ($line);
189 }
190 });
191 }
192 );
193
194 $quit_cv->recv;
195
196 This is now a very basic client. Explaining explicitly what
197 L<AnyEvent::Handle> does or what a I<condvar> is all about is out of scope
198 of this document, please consult L<AnyEvent::Intro> or the manual pages
199 for L<AnyEvent> and L<AnyEvent::Handle>.
200
201 =head1 First Steps Into Messaging
202
203 To supply the C<send_message> function we now take a look at
204 L<AnyEvent::MP>. This is an example of how it might look like:
205
206 ... # the use lines from the above snippet
207
208 use AnyEvent::MP;
209
210 sub send_message {
211 my ($msg) = @_;
212
213 snd $server_port, message => $msg;
214 }
215
216 ... # the rest of the above script
217
218 The C<snd> function is exported by L<AnyEvent::MP>, it stands for 'send
219 a message'. The first argument is the I<port> (a I<port> is something
220 that can receive messages, represented by a printable string) of the
221 server which will receive the message. How we get this port will be
222 explained in the next step.
223
224 The remaining arguments of C<snd> are C<message> and C<$msg>, the first
225 two elements of the I<message> (a I<message> in L<AnyEvent::MP> is a
226 simple list of values, which can be sent to a I<port>).
227
228 So all the function does is send the two values C<message> (a constant
229 string to tell the server what to expect) and the actual message string.
230
231 Thats all fine and simple so far, but where do we get the
232 C<$server_port>? Well, we need to get the unique I<port id> of the
233 server's port where it wants to receive all the incoming chat messages. A
234 I<port id> is unfortunately a very unique string, which we are unable to
235 know in advance. But L<AnyEvent::MP> supports the concept of 'registered
236 ports', which is basically a port on the server side registered under
237 a well known name.
238
239 For example, the server has a port for receiving chat messages with a
240 unique I<port id> and registers it under the name C<chatter>.
241
242 BTW, these "registered port names" should follow similar rules as Perl
243 identifiers, so you should prefix them with your package/module name to
244 make them unique, unless you use them in the main program.
245
246 As I<messages> can only be sent to a I<port id> and not just to some name
247 we have to ask the server node for the I<port id> of the port registered
248 as C<chatter>.
249
250 =head1 Finding The Chatter Port
251
252 Ok, lots of talk, now some code. Now we will actually get the
253 C<$server_port> from the backend:
254
255 ...
256
257 use AnyEvent::MP;
258
259 my $server_node = "127.0.0.1:1299";
260
261 my $client_port = port;
262
263 snd $server_node, lookup => "chatter", $client_port, "resolved";
264
265 my $resolved_cv = AnyEvent->condvar;
266 my $server_port;
267
268 # setup a receiver callback for the 'resolved' message:
269 rcv $client_port, resolved => sub {
270 my ($tag, $chatter_port_id) = @_;
271
272 print "Resolved the server port 'chatter' to $chatter_port_id\n";
273 $server_port = $chatter_port_id;
274
275 $resolved_cv->send;
276 1
277 };
278
279 # lets block the client until we have resolved the server port.
280 $resolved_cv->recv;
281
282 # now setup another receiver callback for the chat messages:
283 rcv $client_port, message => sub {
284 my ($tag, $msg) = @_;
285
286 print "chat> $msg\n";
287 0
288 };
289
290 # send a 'join' message to the server:
291 snd $server_port, join => "$client_port";
292
293 sub send_message { ...
294
295 Now that was a lot of new stuff:
296
297 First we define the C<$server_node>: In order to refer to another node
298 we need some kind of string to reference it - the node reference. The
299 I<noderef> is basically a comma separated list of C<address:port>
300 pairs. We assume in this tutorial that the server runs on C<127.0.0.1>
301 (localhost) on port 1299, which results in the noderef C<127.0.0.1:1299>.
302
303 Next, in order to receive a reply from the other node or the server we
304 need to have a I<port> that messages can be sent to. This is what the
305 C<port> function will do for us, it just creates a new local port and
306 returns it's I<port ID> that can then be used to receive messages.
307
308 When you look carefully, you will see that the first C<snd> uses the
309 C<$server_node> (a noderef) as destination port. Well, what I didn't
310 tell you yet is that each I<node> has a default I<port> to receive
311 messages. The ID of this port is the same as the noderef.
312
313 This I<default port> provides some special services for us, for example
314 resolving a registered name to a I<port id> (a-ha! finally!).
315
316 This is exactly what this line does:
317
318 snd $server_node, lookup => "chatter", $client_port, "resolved";
319
320 This sends a message with first element being C<lookup>, followed by the
321 (hopefully) registered port name that we want to resolve to a I<port
322 id>: C<chatter>. And in order for the server node to be able to send us
323 back the resolved I<port ID> we have to tell it where to send it: The
324 result message will be sent to C<$client_port> (the I<port id> of the
325 port we just created), and will have the string C<resolved> as the first
326 element.
327
328 When the node receives this message, it will look up the name, gobble up
329 all the extra arguments we passed, append the resolved name, and send the
330 resulting list as a message.
331
332 Next we register a receiver for this C<lookup>-request.
333
334 rcv $client_port, resolved => sub {
335 my ($tag, $chatter_port_id) = @_;
336 ...
337 1
338 };
339
340 This sets up a receiver on our own port for messages with the first
341 element being the string C<resolved>. Receivers can match the contents of
342 the messages before actually executing the specified callback.
343
344 B<Please note> that the every C<rcv> callback has to return either a true
345 or a false value, indicating whether it is B<successful>/B<done> (true) or
346 still wants to B<continue> (false) receiving messages.
347
348 In this case we tell the C<$client_port> to look into all the messages
349 it receives and look for the string C<resolved> in the first element of
350 the message. If it is found, the given callback will be called with the
351 message elements as arguments.
352
353 Using a string as the first element of the message is called I<tagging>
354 the message. It's common practise to code the 'type' of a message into
355 it's first element, as this allows for simple matching.
356
357 The result message will contain the I<port ID> of the well known port
358 C<chatter> as second element, which will be stored in C<$chatter_port_id>.
359
360 This port ID will then be stored in C<$server_port>, followed by calling
361 C<send> on $resolved_cv> so the program will continue.
362
363 The callback then returns a C<1> (a true value), to indicate that it has
364 done it's job and doesn't want to receive further C<resolved> messages.
365
366 After this the chat message receiver callback is registered with the port:
367
368 rcv $client_port, message => sub {
369 my ($tag, $msg) = @_;
370
371 print "chat> $msg\n";
372
373 0
374 };
375
376 We assume that all messages that are broadcast to the clients by the
377 server contain the string tag C<message> as first element, and the actual
378 message as second element. The callback returns a false value this time,
379 to indicate that it is not yet done and wants to receive further messages.
380
381 The last thing to do is to tell the server to send us new chat messages
382 from other clients. We do so by sending the message C<join> followed by
383 our own I<port ID>.
384
385 # send the server a 'join' message:
386 snd $server_port, join => $client_port;
387
388 This way the server knows where to send all the new messages to.
389
390 =head1 The Completed Client
391
392 This is the complete client script:
393
394 #!perl
395
396 use AnyEvent;
397 use AnyEvent::Handle;
398 use AnyEvent::MP;
399
400 my $server_node = "127.0.0.1:1299";
401
402 my $client_port = port;
403
404 snd $server_node, lookup => "chatter", $client_port, "resolved";
405
406 my $resolved_cv = AnyEvent->condvar;
407 my $server_port;
408
409 # setup a receiver callback for the 'resolved' message:
410 rcv $client_port, resolved => sub {
411 my ($tag, $chatter_port_id) = @_;
412
413 print "Resolved the server port 'chatter' to $chatter_port_id\n";
414 $server_port = $chatter_port_id;
415
416 $resolved_cv->send;
417 1
418 };
419
420 # lets block the client until we have resolved the server port.
421 $resolved_cv->recv;
422
423 # now setup another receiver callback for the chat messages:
424 rcv $client_port, message => sub {
425 my ($tag, $msg) = @_;
426
427 print "chat> $msg\n";
428 0
429 };
430
431 # send a 'join' message to the server:
432 snd $server_port, join => "$client_port";
433
434 sub send_message {
435 my ($msg) = @_;
436
437 snd $server_port, message => $msg;
438 }
439
440 # make an AnyEvent condition variable for the 'quit' condition
441 # (when we want to exit the client).
442 my $quit_cv = AnyEvent->condvar;
443
444 my $stdin_hdl = AnyEvent::Handle->new (
445 fh => *STDIN,
446 on_error => sub { $quit_cv->send },
447 on_read => sub {
448 my ($hdl) = @_;
449
450 $hdl->push_read (line => sub {
451 my ($hdl, $line) = @_;
452
453 if ($line =~ /^\/quit/) { # /quit will end the client
454 $quit_cv->send;
455 } else {
456 send_message ($line);
457 }
458 });
459 }
460 );
461
462 $quit_cv->recv;
463
464 =head1 The Server
465
466 Ok, we finally come to the server.
467
468 The server of course also needs to set up a port, and in addition needs to
469 I<register> it, so the clients can find it.
470
471 Again, let's jump directly into the code:
472
473 #!perl
474
475 use AnyEvent;
476 use AnyEvent::MP;
477
478 become_public "127.0.0.1:1299";
479
480 my $chatter_port = port;
481
482 reg $chatter_port, "chatter";
483
484 my %client_ports;
485
486 rcv $chatter_port,
487 join => sub {
488 my ($tag, $client_port) = @_;
489
490 print "got new client port: $client_port\n";
491 $client_ports{$client_port} = 1;
492
493 0
494 },
495 message => sub {
496 my ($tag, $msg) = @_;
497
498 print "message> $msg\n";
499
500 snd $_, message => $msg
501 for keys %client_ports;
502
503 0
504 };
505
506 AnyEvent->condvar->recv;
507
508 That is all. Looks much simpler than the client, doesn't it?
509
510 Let's quickly look over it, as C<rcv> has already been discussed in the
511 client part of this tutorial above.
512
513 First this:
514
515 become_public "127.0.0.1:1299";
516
517 This will tell our I<node> to become a I<public> node, which means that it
518 can be contacted via TCP. The first argument should be the I<noderef> the
519 server wants to be reachable at. In this case it's the TCP port 1299 on
520 C<127.0.0.1>.
521
522 Next we set up two receivers, one for the C<join> messages and another one
523 for the actual messages of type C<messsage>. This is done with a single
524 call to C<rcv>, which allows multiple C<< match => $callback >> pairs.
525
526 In the C<join> callback we receive the client port, which is simply
527 remembered in the C<%client_ports> hash. In the C<message> callback we
528 just iterate through all known C<%client_ports> and relay the message to
529 them.
530
531 That concludes the server.
532
533 =head1 The Remaining Problems
534
535 The implementation as shown still has some bugs. For instance: How does
536 the server know that the client isn't there anymore, so it can clean up
537 the C<%client_ports> hash? Also, the chat messages have no originator, so
538 we don't know who actually sent the message (which would be quite useful
539 for human-to-human interaction: to know who the other one is :).
540
541 But aside from these issues I hope this tutorial showed you the basics of
542 L<AnyEvent::MP> and explained some common idioms.
543
544 How to solve the reliability and C<%client_ports> cleanup problem will
545 be explained later in this tutorial (TODO).
546
547 =head1 Inside The Protocol
548
549 Now, for the interested parties, let me explain some details about the protocol
550 that L<AnyEvent::MP> nodes use to communicate to each other. If you are not
551 interested you can skip this section.
552
553 Usually TCP is used for communication. Each I<node>, if configured to be
554 a I<public> node with the C<initialise_node> function will listen on the
555 configured TCP port (default is 4040).
556
557 If then one I<node> wants to send a message to another I<node> it will
558 connect to the host and port given in the I<port ID>.
559
560 Then some handshaking occurs to check whether both I<nodes> know the
561 I<shared secret>. Optionally, TLS can be enabled (about how to do this
562 exactly please consult the L<AnyEvent::MP> man page, just a hint: It
563 should be enough to put the private key and (self signed) certificate in
564 the C<~/.aemp-secret> file of all nodes).
565
566 After the handshake, messages will be exchanged using a serialiser
567 (usually L<JSON> is used for this, but it is also possible to use other
568 serialization formats such as L<Storable>).
569
570 =head1 SEE ALSO
571
572 L<AnyEvent>
573
574 L<AnyEvent::Handle>
575
576 L<AnyEvent::MP>
577
578 =head1 AUTHOR
579
580 Robin Redeker <elmex@ta-sa.org>
581