| 1 |
root |
1.1 |
=encoding utf-8 |
| 2 |
|
|
|
| 3 |
|
|
=head1 AnyEvent - Eine Welt ohne Religion |
| 4 |
|
|
|
| 5 |
|
|
In diesem Vortrag möchte ich die neuesten Entwicklungen in Sachen |
| 6 |
|
|
AnyEvent vorstellen (asynchrones IPv4/IPv6, asynchrones DNS, TLS uvm.), sowie |
| 7 |
|
|
einige Hintergrundinformationen zu bestehenden Event-Modulen auf CPAN, |
| 8 |
|
|
sowie Benchmarks, vorstellen. |
| 9 |
|
|
|
| 10 |
|
|
Doch zuerst: der Titel klingt sicherlich etwas seltsam - was bedeutet er? |
| 11 |
|
|
|
| 12 |
root |
1.3 |
Nun, AnyEvent ist, wie im Namensteil "Event" steht, ein Event-Modul |
| 13 |
root |
1.1 |
bzw. eine Event-Bibliothek. Event-Module oder -Bibliotheken stellen |
| 14 |
root |
1.3 |
im allgemeinen Funktionalität bereit, mit der man auf Ereignisse |
| 15 |
root |
1.1 |
(Events), warten kann, speziell Ereignisse der Art "Zeitpunkt soundso |
| 16 |
|
|
ist erreicht" und "File Descriptor soundso kann jetzt nichtblockierend |
| 17 |
|
|
gelesen/geschrieben werden." |
| 18 |
|
|
|
| 19 |
|
|
Derartige Event-Bibliotheken gibt es zuhauf (Tk, Glib, Event, EV uvm.), |
| 20 |
|
|
doch trotz der offensichtlichen Vorteile Event-basierter Programmierung |
| 21 |
|
|
(z.B. beinahe automatische Parallelisierbarkeit) werden sie kaum in |
| 22 |
|
|
CPAN-Modulen eingesetzt. |
| 23 |
|
|
|
| 24 |
|
|
Meiner Meinung nach liegt der Grund schlicht darin, daß alle diese |
| 25 |
|
|
Event-Bibliotheken exklusiv sind: sie dulden niemanden neben sich. Wie |
| 26 |
|
|
bei den meisten Religionen auch, muss man sich einem dieser Modelle |
| 27 |
|
|
ausliefern. Konkret bedeutet dies für Modul-Autoren: benutzt man |
| 28 |
|
|
z.B. Glib in seinem Modul, muss auch jeder Benutzer dieses Modules Glib |
| 29 |
|
|
benutzen, was natürlich eine starke Einschränkung darstellt, vor allem, |
| 30 |
|
|
wenn das Modul selbst eigentlich nur "irgendwelche" Events benötigt, die |
| 31 |
|
|
im Prinzip jede beliebige Event-Bibliothek liefert. |
| 32 |
|
|
|
| 33 |
|
|
Einige Module auf CPAN haben versucht, aus dieser Not eine Tugend zu |
| 34 |
|
|
machen: POE z.B. kann andere Event-Module als Implementation benutzen (in |
| 35 |
|
|
der Praxis funktioniert das zwar nur mit einigen wenigen, aber die Idee |
| 36 |
|
|
zählt). Lieder verschlimmert dies das Problem nur: POE z.B. verlangt |
| 37 |
|
|
beinahe religiöse Glaubensbekenntnisse und ist kein bisschen kompatibler, |
| 38 |
|
|
im Gegenteil: obwohl POE Backends für Gtk+ und Event besitzt, kann man |
| 39 |
|
|
POE-Module nicht in einem Gtk+ oder Event-basiertem Programm benutzen: |
| 40 |
|
|
setzt man POE in seinem Modul ein, so muss auch jeder Benutzer des Moduls |
| 41 |
|
|
POE benutzen, sowie auch das Hauptprogramm selbst. |
| 42 |
|
|
|
| 43 |
|
|
AnyEvent dagegen ist I<grundsätzlich> anders: Im Stile von anderen |
| 44 |
|
|
C<Any*>-Modulen ist es lediglich eine Schnittstelle zu diesen. Dabei passt |
| 45 |
|
|
es sich an jedes unterstützte Event-Modell an, ohne daß sich die API |
| 46 |
|
|
ändert. Das bedeutet, daß man getrost Module benutzen kann, die AnyEvent |
| 47 |
|
|
benötigen und intern auch benutzen, aber der Benutzer des Moduls sieht |
| 48 |
|
|
davon erstmal nichts und kann jede beliebige Event-Bibliothek benutzen. |
| 49 |
|
|
|
| 50 |
|
|
AnyEvent ist 100% Pure-Perl, und kommt mit eigener, schneller, |
| 51 |
|
|
Event-Bibliothek, so daß auch auf Systemen, wo kein anderes Event-Modell |
| 52 |
root |
1.3 |
verfügbar ist, eventbasiert programmiert werden kann. |
| 53 |
root |
1.1 |
|
| 54 |
|
|
=head2 AnyEvent am Beispiel AnyEvent::CouchDB |
| 55 |
|
|
|
| 56 |
|
|
Ein gutes Beispiel dafür ist C<AnyEvent::CouchDB>. Außer im Namen, |
| 57 |
|
|
kommt AnyEvent in der API dieses Moduls nicht vor. Eine einfache |
| 58 |
|
|
Datenbankabfrage sieht dementsprechend auch ganz langweilig aus: |
| 59 |
|
|
|
| 60 |
root |
1.6 |
my $data = $db->view('users/all', { key => 'b' })->recv; |
| 61 |
root |
1.1 |
|
| 62 |
root |
1.3 |
Obwohl die Abfrage selbst ereignisgesteuert ist, muss man als Benutzer |
| 63 |
root |
1.1 |
des Moduls kaum darunter leiden, lediglich die Zweiteilung der Aufgabe in |
| 64 |
|
|
"Start-Phase" (C<view>-Methode) und "Resultat-Phase" (C<recv>-Methode) ist |
| 65 |
|
|
typisch. Daß intern AnyEvent werkelt, fällt nicht auf, außer, daß die |
| 66 |
|
|
Abfrage in einem laufenden Tk/Gtk usf.-Programm dieses nicht anhält. |
| 67 |
|
|
|
| 68 |
root |
1.3 |
Im Code werde die Vorteile sichtbar, wenn man mehrere Abfragen "gleichzeitig" |
| 69 |
root |
1.1 |
(genauer: miteinander verflochten) ausführt: |
| 70 |
|
|
|
| 71 |
|
|
my @data = map $_->recv, |
| 72 |
|
|
map $db->view ('users/al', { key => $_ }), |
| 73 |
|
|
qw(key1 key2 key3 key4); |
| 74 |
|
|
|
| 75 |
|
|
Auch hierbei muss man nicht darauf achten, welches Event-Modul benutzt |
| 76 |
|
|
wird (oder ob man überhaupt ein spezielles benutzt) - es funktioniert |
| 77 |
|
|
einfach. |
| 78 |
|
|
|
| 79 |
|
|
Richtig Spaß macht es, wenn man realisiert, daß man praktisch alle |
| 80 |
|
|
AnyEvent-benutzenden Module beliebig kombinieren kann, und Aufgaben |
| 81 |
|
|
quasi "im Hintergrund" erledigen kann, z.B. mit C<AnyEvent::CouchDB> und |
| 82 |
|
|
C<AnyEvent::HTTP>: |
| 83 |
|
|
|
| 84 |
|
|
$db->view('users/all', { key => 'b' })->cb (sub { |
| 85 |
|
|
my $data = shift->revc; |
| 86 |
|
|
# daten erhalten |
| 87 |
|
|
}); |
| 88 |
|
|
|
| 89 |
|
|
AnyEvent::HTTP::http_get "http://www.porttracker.co.uk", sub { |
| 90 |
|
|
my $html = $_[1]; |
| 91 |
|
|
# daten erhalten |
| 92 |
|
|
}; |
| 93 |
|
|
|
| 94 |
|
|
# hier kann man weitere Dinge erledigen |
| 95 |
|
|
|
| 96 |
|
|
Und all das kann man in den eigenen Modulen nutzen, ohne sich irgendwie |
| 97 |
|
|
auf ein bestimmtes Event-Modell festzulegen. |
| 98 |
|
|
|
| 99 |
|
|
|
| 100 |
|
|
=head2 AnyEvent - Grundlegendes |
| 101 |
|
|
|
| 102 |
|
|
AnyEvent selbst wurde letztes Jahr an dieser Stelle ausführlich |
| 103 |
|
|
vorgestellt, daher möchte ich hier nur eine kurze Übersicht |
| 104 |
|
|
bzw. Einführung in AnyEvent als rein Event-Bibliothek geben: |
| 105 |
|
|
|
| 106 |
root |
1.3 |
Wie viele andere Bibliotheken auch, benutzt AnyEvent das Konzept eines |
| 107 |
|
|
"Watchers", eines Objekts, das wie ein Wachhund auf ein bestimmtes |
| 108 |
|
|
Ereignis reagiert (watch/watcher = Wache/Beobachter) und bei Eintreten |
| 109 |
|
|
eine Aktion auslöst, z.B. Aufrufen eines Callbacks. |
| 110 |
root |
1.1 |
|
| 111 |
|
|
Diese Watcher gibt es für die folgenden Ereignistypen: |
| 112 |
|
|
|
| 113 |
|
|
=over 4 |
| 114 |
|
|
|
| 115 |
|
|
=item Zeit - timer |
| 116 |
|
|
|
| 117 |
|
|
my $timer = AnyEvent->timer (after => 7, cb => sub { |
| 118 |
root |
1.6 |
warn "Sieben Sekunden sind abgelaufen!"; |
| 119 |
root |
1.1 |
}); |
| 120 |
|
|
|
| 121 |
|
|
Timer können nach einer gewissen Zeit ablaufen, oder wiederholt (z.B. |
| 122 |
|
|
alle 3s) Ereignisse auslösen und Callbacks aufrufen. |
| 123 |
|
|
|
| 124 |
|
|
=item I/O auf File Handles - io |
| 125 |
|
|
|
| 126 |
|
|
my $watcher = AnyEvent->io (fh => *STDIN, poll => 'r', cb => sub { |
| 127 |
|
|
my $line = <STDIN>; |
| 128 |
|
|
# zeile von stdin gelesen |
| 129 |
|
|
}); |
| 130 |
|
|
|
| 131 |
|
|
I/O-Watcher beobachten einen File Handle und können einen Callback |
| 132 |
|
|
aufrufen, wenn gelesen oder geschrieben werden kann. |
| 133 |
|
|
|
| 134 |
|
|
=item Signale - signal |
| 135 |
|
|
|
| 136 |
|
|
my $quit = AnyEvent->condvar; |
| 137 |
|
|
|
| 138 |
|
|
my $term = AnyEvent->signal (signal => "TERM", cb => sub { |
| 139 |
|
|
$quit->send; |
| 140 |
|
|
}); |
| 141 |
|
|
|
| 142 |
|
|
... |
| 143 |
|
|
|
| 144 |
|
|
$quit->recv |
| 145 |
|
|
|
| 146 |
|
|
Mit Signal-Watchers kann man synchron auf das Auftreten von Signalen |
| 147 |
|
|
warten. |
| 148 |
|
|
|
| 149 |
root |
1.3 |
Das obige Beispiel benutzt eine "condition variable", um mit dem |
| 150 |
|
|
Hauptprogramm zu kommunizieren (mehr Info s.u.). |
| 151 |
root |
1.1 |
|
| 152 |
|
|
=item Kindprozesse sterben sehen - child |
| 153 |
|
|
|
| 154 |
|
|
my $w = AnyEvent->child (pid => $pid, cb => sub { |
| 155 |
|
|
warn "pid $pid hat sich beendet\n"; |
| 156 |
|
|
}); |
| 157 |
|
|
|
| 158 |
|
|
Eher selten benötigt, aber sehr schwer zu implementieren (wenn man es |
| 159 |
root |
1.3 |
richtig tun möchte :), sind Child-Watcher: mit diesen kann man auf das |
| 160 |
root |
1.1 |
Beenden von ge-fork-ten Prozessen warten. |
| 161 |
|
|
|
| 162 |
|
|
=back |
| 163 |
|
|
|
| 164 |
|
|
=head3 Wenn man mal nichts tun möchte |
| 165 |
|
|
|
| 166 |
|
|
Event-basierte APIs haben eines gemein: Im allgemeinen werden Vorgänge |
| 167 |
|
|
nur gestartet und laufen dann quasi-parallel im Hintergrund ab. |
| 168 |
|
|
|
| 169 |
|
|
Aber wenn man nun genügend solche Vorgänge gestartet hat, möchte man ja |
| 170 |
|
|
irgendwann auch mal auf die Ergebnisse warten. |
| 171 |
|
|
|
| 172 |
|
|
Dies erledigt entweder das Hauptprogramm, indem es die entsprechenden |
| 173 |
|
|
Funktionen der Event-Bibliothek aufruft (z.B. C<Event::loop> oder C<main |
| 174 |
|
|
Gtk>), oder man benutzt sogenannte "condition variables", mit denen man |
| 175 |
|
|
auf das eintreten eines bestimmten Zustands warten kann. |
| 176 |
|
|
|
| 177 |
|
|
"Condition Variables" sind so etwas wie "Rendezvouspunkte" zwischen dem |
| 178 |
|
|
Erzeuger von Daten oder einem Ereignis (z.B. C<AnyEvent::CouchDB->view> |
| 179 |
|
|
oder C<AnyEvent::HTTP::http_get>, aber auch genauso C<< AnyEvent->timer |
| 180 |
|
|
>>) und dem Konsumenten (der Benutzer dieser Funktionen, der die Daten |
| 181 |
|
|
haben oder einfach auf das Ereignis warten möchte). |
| 182 |
|
|
|
| 183 |
|
|
=head1 Neues! |
| 184 |
|
|
|
| 185 |
|
|
Auf zu neuen Ufern - im letzten Jahr hat sich viel getan, sowohl in |
| 186 |
|
|
AnyEvent selbst, als auch in Sachen "andere Module die AnyEvent benutzen". |
| 187 |
|
|
|
| 188 |
|
|
=head2 Altes! |
| 189 |
|
|
|
| 190 |
|
|
Die wichtigsten Eigenschaften von AnyEvent sind allerdings beim |
| 191 |
|
|
alten geblieben: |
| 192 |
|
|
|
| 193 |
|
|
=over 4 |
| 194 |
|
|
|
| 195 |
|
|
=item * Die API hat sich kaum verändert, bzw. ist 100% |
| 196 |
|
|
rückwärtskompatibel. |
| 197 |
|
|
|
| 198 |
|
|
=item * AnyEvent ist nach wie vor "Pure-Perl". |
| 199 |
|
|
|
| 200 |
|
|
=item * Der Event-Teil ist nach wie vor getrennt von den anderen Teilen, |
| 201 |
root |
1.3 |
das Event-Modul ist klein und wenn man bezahlt nur für das, was man |
| 202 |
root |
1.1 |
benutzt. |
| 203 |
|
|
|
| 204 |
|
|
=back |
| 205 |
|
|
|
| 206 |
|
|
Aber nun zu den Neuigkeiten: |
| 207 |
|
|
|
| 208 |
|
|
|
| 209 |
|
|
=head2 AnyEvent::Strict |
| 210 |
|
|
|
| 211 |
|
|
Aus Geschwindigkeitsgründen macht AnyEvent selbst keine |
| 212 |
|
|
Parameterprüfung, ganz einfach deshalb, weil die Prüfung meistens mehr |
| 213 |
|
|
Zeit in Anspruch nimmt als der Rest der Funktion. |
| 214 |
|
|
|
| 215 |
|
|
Für einen erfahrenen AnyEvent-Benutzer ist das in der Praxis kein |
| 216 |
|
|
Problem, aber wenn man die API noch nicht genau kennt, können sich so |
| 217 |
|
|
Fehler einschleichen (z.B. alte Event-Hasen benutzen gerne C<fd> statt |
| 218 |
|
|
C<fh> um einen I/O-Watcher zu erzeugen). |
| 219 |
|
|
|
| 220 |
|
|
Daher bietet AnyEvent optional eine strenge Prüfung aller |
| 221 |
|
|
Parameter an. Die kann man ganz einfach aktivieren, in dem man die |
| 222 |
|
|
Environment-Variable C<PERL_ANYEVENT_STRICT> auf C<1> setzt, was auf allen |
| 223 |
|
|
meinen Entwicklungsrechnern der Fall ist. |
| 224 |
|
|
|
| 225 |
|
|
Falls man diese Prüfung grundsätzlich wünscht (was ich ganz dringend |
| 226 |
|
|
I<nicht> empfehle, siehe aber den Punkt "keine Religion"...), kann man diese |
| 227 |
|
|
auch immer aktivieren: |
| 228 |
|
|
|
| 229 |
|
|
use AnyEvent; |
| 230 |
|
|
use AnyEvent::Strict; |
| 231 |
|
|
|
| 232 |
|
|
# jetzt wird brutal geprüft. |
| 233 |
|
|
|
| 234 |
root |
1.3 |
Ich gebe zu, das war sicher noch nicht der Brüller, aber sehen wir |
| 235 |
root |
1.1 |
weiter: |
| 236 |
|
|
|
| 237 |
root |
1.5 |
=head2 AnyEvent::Util |
| 238 |
root |
1.1 |
|
| 239 |
|
|
Dies ist meine Grabbelkiste für nützliche aber eklige Funktionen: |
| 240 |
|
|
|
| 241 |
|
|
=over 4 |
| 242 |
|
|
|
| 243 |
|
|
=item fork_call BLOCK $callback |
| 244 |
|
|
|
| 245 |
|
|
Führt den angegebenen BLOCK in einem separaten Prozess aus und ruft |
| 246 |
|
|
danach den $callback mit den Resultatwerten auf. Ist ideal, um lange |
| 247 |
|
|
Berechnungen in einen separaten Prozess zu verlagern: |
| 248 |
|
|
|
| 249 |
|
|
fork_call { |
| 250 |
|
|
find_primes_till 1e9 |
| 251 |
|
|
} sub { |
| 252 |
|
|
warn "alle primzahen bis 10**9: ", join " ", @_; |
| 253 |
|
|
}; |
| 254 |
|
|
|
| 255 |
|
|
Die Funktion leaked allerdings unter Windows, aber ich habe bisher noch |
| 256 |
root |
1.3 |
keinen Weg gefunden, unter einem native-win32-Perl zu forken und das Kind |
| 257 |
|
|
sauber zu beenden (ich glaube, es gibt keinen...). |
| 258 |
root |
1.1 |
|
| 259 |
|
|
=item my $guard = guard BLOCK |
| 260 |
|
|
|
| 261 |
|
|
Führt den angegebenen BLOCK aus, wenn C<$guard> zerstört wird. Ist |
| 262 |
|
|
bei ereignisgesteuerter Programmierung total hilfreich, um Sachen |
| 263 |
|
|
aufzuräumen. |
| 264 |
|
|
|
| 265 |
|
|
Wenn es vorhanden ist, benutzt AnyEvent das C<Guard>-Modul (ja, das ist |
| 266 |
|
|
Schleichwerbung). |
| 267 |
|
|
|
| 268 |
|
|
=item fh_nonblocking $fh, $nonblocking |
| 269 |
|
|
|
| 270 |
|
|
Einen File Handle non-blocking zu machen, ist nicht ganz einfach (Windows, |
| 271 |
|
|
Windows, kaputt, kaputt...). C<AnyEvent::Util> erledigt dies mit |
| 272 |
|
|
furchtbaren Hacks unter Windows und elegant überall sonst. |
| 273 |
|
|
|
| 274 |
|
|
=back |
| 275 |
|
|
|
| 276 |
|
|
Immer noch nicht überzeugt? Ok, dann machen wir mal mit |
| 277 |
|
|
Netzwerkprogrammierung weiter: |
| 278 |
|
|
|
| 279 |
|
|
|
| 280 |
|
|
=head2 AnyEvent::Socket |
| 281 |
|
|
|
| 282 |
|
|
Sockets in Perl haben ein paar gravierende Nachteile: non-blocking |
| 283 |
|
|
connects sind extrem umständlich, IPv6 und IPv4 brauchen völlig |
| 284 |
|
|
inkompatible APIs, DNS blockiert den gesamtem Prozess und liefert darüber |
| 285 |
|
|
hinaus auch nur altmodische IP-Adressen. |
| 286 |
|
|
|
| 287 |
|
|
Anders mit AnyEvent::Socket: |
| 288 |
|
|
|
| 289 |
|
|
tcp_connect "www.google.com", "80", sub { |
| 290 |
|
|
my ($fh) = @_ |
| 291 |
|
|
or die "www.google.com: $!"; |
| 292 |
|
|
|
| 293 |
|
|
... |
| 294 |
|
|
}; |
| 295 |
|
|
|
| 296 |
|
|
Dieser Aufruf löst zuerst C<www.google.com> auf - im Hintergrund, ohne |
| 297 |
|
|
das Programm anzuhalten - und baut dann, ebenfalls im Hintergrund, eine |
| 298 |
|
|
TCP-Verbindung dorthin auf. |
| 299 |
|
|
|
| 300 |
|
|
Es versteht sich von selbst, daß Multi-Homed Hosts genauso unterstützt |
| 301 |
|
|
werden wie IPv6, und das ohne zusätzliche XS-Module. |
| 302 |
|
|
|
| 303 |
root |
1.3 |
C<tcp_connect> kann aber noch mehr. Z.b. auf Jabber-Server connecten, was |
| 304 |
root |
1.1 |
mit Perl alleine fast unmöglich ist, da man mit Perl keine SRV-Records |
| 305 |
|
|
auflösen kann: |
| 306 |
|
|
|
| 307 |
|
|
tcp_connect "jabber.org", "xmpp-server=5269", sub { |
| 308 |
|
|
# Die pseudo-XML-Kacke parsed ihr bitte selbst... |
| 309 |
|
|
}, 60; # <- timeout |
| 310 |
|
|
|
| 311 |
|
|
Der Name C<tcp_connect> ist leider etwas naiv, tatsächlich werden auch |
| 312 |
root |
1.3 |
andere Protokolle unterstützt, z.B. local unix Sockets: |
| 313 |
root |
1.1 |
|
| 314 |
|
|
tcp_connect "unix/", "/tmp/.X11-unix/X0", sub { ... |
| 315 |
|
|
|
| 316 |
|
|
Listening-Sockets funktionieren prinzipiell genauso einfach, z.B. hier |
| 317 |
|
|
für einen TCPv6-Port: |
| 318 |
|
|
|
| 319 |
|
|
tcp_server "::", 80, sub { |
| 320 |
|
|
my ($fh, $host, $port) = @_; |
| 321 |
|
|
|
| 322 |
|
|
# jede neue Verbindung führt zu einem Aufruf dieses Callbacks |
| 323 |
|
|
}; |
| 324 |
|
|
|
| 325 |
|
|
Oder einen beliebigen TCPv4-Port: |
| 326 |
|
|
|
| 327 |
|
|
tcp_server "0", undef, sub { |
| 328 |
|
|
my ($fh, $host, $port) = @_; |
| 329 |
|
|
... |
| 330 |
|
|
}, sub { |
| 331 |
|
|
my ($thishost, $thisport) = @_; |
| 332 |
|
|
|
| 333 |
|
|
warn "bound to port $thisport\n"; |
| 334 |
|
|
}; |
| 335 |
|
|
|
| 336 |
|
|
Leider muss man sich selbst darum kümmern, ob IPv6-Sockets auch auf IPv4 |
| 337 |
|
|
hören - üblicherweise ja, aber die BSDs haben natürlich mal wieder |
| 338 |
|
|
verbockt und halten sich nicht ans RFC. |
| 339 |
|
|
|
| 340 |
root |
1.3 |
Komplizierteres ist immer möglich - anders als z.B. mit C<IO::Socket> hat |
| 341 |
root |
1.1 |
man volle Kontrolle über die eigentliche Socket und braucht nicht für |
| 342 |
|
|
jeden etwas spezielleren Wunsch extra Support. |
| 343 |
|
|
|
| 344 |
|
|
Und anders als bei anderen Lösungen funktionieren diese Funktionen auch |
| 345 |
|
|
unter Windows korrekt (modulo Tk, daß wie üblich zu kaputt ist, um |
| 346 |
|
|
non-blocking connects stabil zu kriegen, aber AnyEvent arbeitet auch um |
| 347 |
|
|
diese Bugs herum). |
| 348 |
|
|
|
| 349 |
|
|
Neben diesen beiden Funktionen gibt es in AnyEvent::Socket eine Unmenge an |
| 350 |
|
|
Utility-Funktionen zum parsen/anzeigen von IP-Adressen, Socket-Adressen |
| 351 |
|
|
und DNS: |
| 352 |
|
|
|
| 353 |
|
|
$ipn = parse_ipv4 $dotted_quad |
| 354 |
|
|
$ipn = parse_ipv6 $textual_ipv6_address |
| 355 |
|
|
$ipn = parse_address $text |
| 356 |
|
|
$text = AnyEvent::Socket::aton $ipn |
| 357 |
|
|
($host, $service) = parse_hostport $string[, $default_service] |
| 358 |
|
|
$sa_family = address_family $ipn |
| 359 |
|
|
$text = format_address $ipn |
| 360 |
|
|
$text = AnyEvent::Socket::ntoa $ipn |
| 361 |
|
|
inet_aton $name_or_address, $cb->(@addresses) |
| 362 |
|
|
$sa = AnyEvent::Socket::pack_sockaddr $service, $host |
| 363 |
|
|
($service, $host) = AnyEvent::Socket::unpack_sockaddr $sa |
| 364 |
|
|
resolve_sockaddr $node, $service, $proto, $family, $type, $cb->([$family, $type, $proto, $sockaddr], ...) |
| 365 |
|
|
|
| 366 |
|
|
Hier verweise ich auf die Dokumentation. Einzig C<parse_hostport> |
| 367 |
|
|
möchte ich genauer erklären: Selbst die Aufgabe, einen Hostnamen und |
| 368 |
|
|
einen Port anzugeben, ist nicht ganz einfach wenn man es richtig machen |
| 369 |
|
|
will. C<parse_hostport> nimmt ein oder zwei Strings und zerlegt diesen in |
| 370 |
|
|
Host und Port, z.B. diese: |
| 371 |
|
|
|
| 372 |
|
|
www.linux.org |
| 373 |
|
|
www.x.de:443 |
| 374 |
|
|
www.x.de:https=443 |
| 375 |
|
|
127.1:22 |
| 376 |
|
|
::1 |
| 377 |
|
|
affe::1 |
| 378 |
|
|
[10.0.1]:80 |
| 379 |
|
|
[www.x.org] 17 |
| 380 |
|
|
10.0.0.1 smtp |
| 381 |
|
|
|
| 382 |
|
|
Richtig, IPv6 macht das ganze deutlich komplexer. Und IPv6 ist die |
| 383 |
|
|
Zukunft, ob man das nun gut findet oder nicht :/ |
| 384 |
|
|
|
| 385 |
|
|
Ach ja, Stichwort IPv6 - es kann auch furchtbar nervig sein, wenn |
| 386 |
|
|
Programme grundsätzlich die IPv6-Adresse bevorzugen, die gerade nicht |
| 387 |
|
|
erreichbar ist. Der Standard bei AnyEvent ist zur Zeit IPv4 den |
| 388 |
|
|
Vorzug zu geben, was sich allerdings über die Environment-Variable |
| 389 |
|
|
C<PERL_ANYEVENT_PROTOCOLS> beeinflussen lässt. |
| 390 |
|
|
|
| 391 |
|
|
|
| 392 |
|
|
=head2 AnyEvent::DNS |
| 393 |
|
|
|
| 394 |
|
|
Nur am Rande möchte ich AnyEvent::DNS erwähnen. Ursprünglich war es nur |
| 395 |
|
|
dazu gedacht, die Adressen für C<AnyEvent::Socket> zu resolven. Das Modul |
| 396 |
|
|
ist aber ein kompletter Stub-Resolver, der beliebige DNS-Record-Typen, |
| 397 |
|
|
EDNS0, IPv6 und virtual circuit mode supported. |
| 398 |
|
|
|
| 399 |
|
|
Das Modul ist in der Praxis ca. halb so schnell wie z.b. EV::ADNS (braucht |
| 400 |
|
|
natürlich viel mehr Speicher, blockiert aber im Gegensatz zu EV::ADNS |
| 401 |
|
|
nie). |
| 402 |
|
|
|
| 403 |
|
|
Es gibt Hilfsunktionen (hier nur eine Auswahl): |
| 404 |
|
|
|
| 405 |
|
|
AnyEvent::DNS::a $domain, $cb->(@addrs) |
| 406 |
|
|
AnyEvent::DNS::mx $domain, $cb->(@hostnames) |
| 407 |
|
|
AnyEvent::DNS::srv $service, $proto, $domain, $cb->(@srv_rr) |
| 408 |
|
|
AnyEvent::DNS::ptr $domain, $cb->(@hostnames) |
| 409 |
|
|
AnyEvent::DNS::reverse_lookup $ipv4_or_6, $cb->(@hostnames) |
| 410 |
|
|
AnyEvent::DNS::reverse_verify $ipv4_or_6, $cb->(@hostnames) |
| 411 |
|
|
|
| 412 |
|
|
Und eine Resolver-Klasse: |
| 413 |
|
|
|
| 414 |
|
|
use Data::Dumper; |
| 415 |
|
|
use AnyEvent::DNS; |
| 416 |
|
|
AnyEvent::DNS::resolver->resolve ( |
| 417 |
|
|
"google.com", "*", my $cv = AnyEvent->condvar); |
| 418 |
|
|
warn Dumper [$cv->recv]; |
| 419 |
|
|
|
| 420 |
|
|
# gekürztes Ergebnis: |
| 421 |
|
|
# [ |
| 422 |
|
|
# [ 'google.com', 'soa', 'in', 'ns1.google.com', 'dns-admin.google.com', |
| 423 |
|
|
# 2008052701, 7200, 1800, 1209600, 300 ], |
| 424 |
|
|
# [ |
| 425 |
|
|
# 'google.com', 'txt', 'in', |
| 426 |
|
|
# 'v=spf1 include:_netblocks.google.com ~all' |
| 427 |
|
|
# ], |
| 428 |
|
|
# [ 'google.com', 'a', 'in', '64.233.187.99' ], |
| 429 |
|
|
# [ 'google.com', 'mx', 'in', 10, 'smtp2.google.com' ], |
| 430 |
|
|
# [ 'google.com', 'ns', 'in', 'ns2.google.com' ], |
| 431 |
|
|
# ] |
| 432 |
|
|
|
| 433 |
|
|
|
| 434 |
|
|
=head2 AnyEvent::Handle |
| 435 |
|
|
|
| 436 |
|
|
Nun zum ehrgeizigsten Teil der Neuerungen: C<AnyEvent::Handle>. |
| 437 |
|
|
|
| 438 |
|
|
Die Probleme, die dieses Modul lösen möchte, kommen in der Praxis |
| 439 |
|
|
häufig vor: Formatted-I/O, Pipelining und TLS-Verschlüsselung. |
| 440 |
|
|
|
| 441 |
|
|
Früher habe ich immer ad-hoc Lösungen für diese Probleme implementiert, |
| 442 |
|
|
da mir schlicht die Erfahrung gefehlt hat, wie man alle diese Features in |
| 443 |
|
|
ein allgemeines Perl-Modul "gießt". |
| 444 |
|
|
|
| 445 |
|
|
C<AnyEvent::Handle> versucht diese Lösung zu |
| 446 |
|
|
sein: AnyEvent::Handle-Objekte sind Wrapper um existierende File |
| 447 |
|
|
Handles. Im folgenden beschreibe ich die Lösung, die AnyEvent::Handle |
| 448 |
|
|
für die oben beschriebenen Probleme bietet: |
| 449 |
|
|
|
| 450 |
|
|
=over 4 |
| 451 |
|
|
|
| 452 |
|
|
=item Formatted I/O |
| 453 |
|
|
|
| 454 |
|
|
Um auf ein AnyEvent::Handle-Objekt zu schreiben, benutzt man |
| 455 |
|
|
C<push_write>. Das C<push> deutet darauf hin, daß es einen Puffer für |
| 456 |
|
|
die Daten gibt, und C<push_write> hängt die Daten einfach an diesen an: |
| 457 |
|
|
|
| 458 |
|
|
$hdl->push_write ("GET / HTTP/1.0\015\012\015\012"); |
| 459 |
|
|
|
| 460 |
|
|
Mit mehr als einem Argument fordert man zusätzliche Formatierungen an, |
| 461 |
|
|
die meisten dieser Formatierungen gibt es sowohl für die Schreib- als |
| 462 |
|
|
auch die Leseseite: |
| 463 |
|
|
|
| 464 |
|
|
$hdl->push_write (netstring => "xyz"); # djb netsrings |
| 465 |
|
|
$hdl->push_write (packstring => "w", "data"); # benutzt pack() |
| 466 |
|
|
$hdl->push_write (json => [1, 2, 3, 4]); # benutzt JSON |
| 467 |
|
|
$hdl->push_write (storable => $ref); # benutzt Storable |
| 468 |
|
|
|
| 469 |
|
|
Schreiben ist einfach, Lesen ist schwieriger. Anders als beim Schreiben |
| 470 |
|
|
enthält der Puffer keine Daten, sondern die Callbacks, die der Reihe nach |
| 471 |
|
|
aufgerufen werden, wenn Daten eintreffen. |
| 472 |
|
|
|
| 473 |
|
|
Die Flexibilität entsteht dadurch, daß man Callbacks ans Ende des Puffers anhängen kann: |
| 474 |
|
|
|
| 475 |
|
|
$hdl->push_read (json => sub { my $data = $_[1] }); |
| 476 |
|
|
|
| 477 |
|
|
Aber auch vor dem Anfang einschieben kann: |
| 478 |
|
|
|
| 479 |
|
|
$hdl->unshift_read (json => sub { my $data = $_[1] }); |
| 480 |
|
|
|
| 481 |
|
|
Interessant ist dies vor allem, weil man hierdurch recht einfach auch |
| 482 |
|
|
komplexe Protokolle mit Pipelining implementieren kann. |
| 483 |
|
|
|
| 484 |
root |
1.4 |
Man kann jederzeit eigene Formatierungstypen global für alle Handles |
| 485 |
|
|
hinzufügen. Zusätzlich zu den schon vorgestellten Typen für die |
| 486 |
|
|
Schreibrichtung, die auch für die Leserichtung existieren, gibt es noch: |
| 487 |
root |
1.1 |
|
| 488 |
|
|
$hdl->push_read (line => $eol, $cb); # zeilen |
| 489 |
root |
1.7 |
$hdl->push_Read (regex => $accept[, $reject[, $skip], $cb); |
| 490 |
root |
1.1 |
|
| 491 |
|
|
=item Pipelining |
| 492 |
|
|
|
| 493 |
|
|
Pipelining bedeutet, daß man auf einer bestehenden Verbindung mehrere |
| 494 |
|
|
Anfragen gleichzeitig losschickt. Die Herausforderung liegt darin, daß |
| 495 |
|
|
man beim Empfang der Antworten flexibel auf Ausnahmen reagieren können |
| 496 |
|
|
muss. |
| 497 |
|
|
|
| 498 |
|
|
Bei HTTP beispielsweise weiß man erst nach Parsen des Headers, wie lang |
| 499 |
|
|
der Body wird. |
| 500 |
|
|
|
| 501 |
|
|
AnyEvent::Handle erledigt dies mit einer Kombination aus C<push_read> und |
| 502 |
|
|
C<unshift_read>: |
| 503 |
|
|
|
| 504 |
|
|
# sende requests |
| 505 |
|
|
for (@urls) { |
| 506 |
|
|
$hdl->push_write ("GET $_ HTTP/1.0\015\012\015\012"); |
| 507 |
|
|
} |
| 508 |
|
|
|
| 509 |
|
|
# parse antworten |
| 510 |
|
|
for (@urls) { |
| 511 |
|
|
# lese header |
| 512 |
|
|
$hdl->push_read (line => "\015\012\015\012", sub { |
| 513 |
|
|
my ($hdl, $header) = @_; |
| 514 |
|
|
|
| 515 |
|
|
my $content_length = extract "Content-Length", $header; |
| 516 |
|
|
|
| 517 |
|
|
$hdl->unshift_read (chunk => $content_length, sub { |
| 518 |
|
|
my ($hdl, $body) = @_; |
| 519 |
|
|
|
| 520 |
|
|
# fertig |
| 521 |
|
|
}); |
| 522 |
|
|
}); |
| 523 |
|
|
} |
| 524 |
|
|
|
| 525 |
|
|
Der springende Punkt ist, daß C<unshift_read> erst I<nach> dem Empfang |
| 526 |
|
|
des Headers ausgeführt wird. Dadurch kann man z.B. verschiedene Arten von |
| 527 |
|
|
Antworten parsen, oder auf unerwartete Fehler reagieren, obwohl vielleicht |
| 528 |
|
|
schon Antworten auf die nächsten Requests empfangen wurden. |
| 529 |
|
|
|
| 530 |
|
|
=item TLS/SSL |
| 531 |
|
|
|
| 532 |
|
|
TLS ist der Horror. Nicht nur, aber vor allem auch wegen der grauenvollen |
| 533 |
|
|
OpenSSL-API (GnuTLS ist geringfügig besser, doch sucht man vergebens nach |
| 534 |
|
|
einem guten Perl-Modul dafür). Der Beweis dafür ist, daß es praktisch |
| 535 |
|
|
kein Modul gibt, daß non-blocking TLS connects korrekt und wirklich |
| 536 |
|
|
non-blocking durchführt. |
| 537 |
|
|
|
| 538 |
|
|
Es hilft auch nicht gerade, daß Net::SSLeay nur einen Bruchteil der |
| 539 |
|
|
OpenSSL-API umsetzt. |
| 540 |
|
|
|
| 541 |
|
|
AnyEvent::Handle (to boldly go...) versucht einen ganz neuen Ansatz für |
| 542 |
|
|
diese Problematik, bei der OpenSSL vom eigentlichen File Handle komplett |
| 543 |
|
|
getrennt wird, d.h. gar keinen Zugriff auf diesen hat. Dadurch wird |
| 544 |
|
|
garantiert, daß OpenSSL auf gar keinen Fall blockieren kann. |
| 545 |
|
|
|
| 546 |
root |
1.2 |
Ganz wie der Rest von AnyEvent ist einfach einfach: wenn man zu einem |
| 547 |
|
|
TLS-Server connecten möchte, setzt man C<tls> auf C<"connect">, als |
| 548 |
|
|
TLS-Server dagegen setzt man es auf C<"accept">. |
| 549 |
|
|
|
| 550 |
|
|
Hat man kompliziertere Ansprüche, wie Zertifikate oder |
| 551 |
|
|
Verification-Callbacks, so kann man sein eigenes Net::SSLeay::CTX-Objekt |
| 552 |
|
|
oder das Net::SSLeay-Objekt für die Verbindung selbst übergeben. |
| 553 |
|
|
|
| 554 |
|
|
Selbstverständlich kann man den TLS-Handshake zu einem beliebigen |
| 555 |
|
|
Zeitpunkt beginnen, nicht nur am Anfang der Verbindung. |
| 556 |
|
|
|
| 557 |
root |
1.1 |
=back |
| 558 |
|
|
|
| 559 |
root |
1.2 |
Mit AnyEvent::Handle wird manches erstaunlich einfach. Hier ist z.B. ein |
| 560 |
|
|
(ganz primitiver) HTTPS-Client (übrigens das komplette Programm): |
| 561 |
root |
1.1 |
|
| 562 |
|
|
use AnyEvent; |
| 563 |
|
|
use AnyEvent::Socket; |
| 564 |
|
|
use AnyEvent::Handle; |
| 565 |
|
|
|
| 566 |
|
|
my $cv = AnyEvent->condvar; |
| 567 |
|
|
|
| 568 |
|
|
tcp_connect "www.google.com", "443", sub { |
| 569 |
|
|
my ($fh) = @_ or die; |
| 570 |
|
|
|
| 571 |
|
|
my $hdl; $hdl = new AnyEvent::Handle |
| 572 |
|
|
fh => $fh, |
| 573 |
|
|
timeout => 60, |
| 574 |
|
|
tls => "connect", |
| 575 |
|
|
on_eof => sub { |
| 576 |
|
|
undef $hdl; # gibt handle frei |
| 577 |
|
|
$cv->send; # isch hab' ferdisch |
| 578 |
|
|
}, |
| 579 |
|
|
on_read => sub { |
| 580 |
|
|
my ($hdl) = @_; |
| 581 |
|
|
print $hdl->{rbuf}; |
| 582 |
|
|
$hdl->{rbuf} = ""; |
| 583 |
|
|
}; |
| 584 |
|
|
|
| 585 |
|
|
$hdl->push_write ("GET / HTTP/1.0\015\012\015\012"); |
| 586 |
|
|
}; |
| 587 |
|
|
|
| 588 |
|
|
$cv->recv; |
| 589 |
|
|
|
| 590 |
root |
1.4 |
Zuerst zum Einsatz kommt C<tcp_connect>, um den non-blocking Connect |
| 591 |
|
|
durchzuführen. Danach wird das Handle-Objekt erzeugt, konfiguriert, |
| 592 |
|
|
und danach lediglich der Request gesendet, der Rest des Protokolls wird |
| 593 |
|
|
ereignisgesteuert abgehandelt. Der Unterschied zwischen TLS (https) und |
| 594 |
|
|
nicht-TLS (http) besteht lediglich in dem Parameter C<tls => "connect">, |
| 595 |
|
|
der einen sofortigen TLS-Client-Handshake auslöst (will man in der |
| 596 |
|
|
Mitte der Verbindung switchen, z.b. für SMTP, kann man jederzeit die |
| 597 |
|
|
C<starttls>-Methode aufrufen). |
| 598 |
|
|
|
| 599 |
root |
1.2 |
Darüber hinaus gibt es eine Menge Parameter, mit dem man Verhalten, |
| 600 |
|
|
Fehlerbehandlung, Ressourcenbenutzung auf seine Anforderungen abstimmen |
| 601 |
|
|
kann. Für ganz spezielle Anwendungen kann man von AnyEvent::Handle auch |
| 602 |
|
|
eigene Klassen ableiten, aber in den meisten Fällen ist dies unnötig. |
| 603 |
|
|
|
| 604 |
|
|
Ein Nachteil von AnyEvent::Handle ist allerdings, daß die Abstraktion |
| 605 |
|
|
selbst nicht kostenlos ist: Methodenaufrufe in Perl sind vergleichsweise |
| 606 |
|
|
langsam, und AnyEvent::Handle benutzt sehr viele davon. In den meisten |
| 607 |
|
|
Fällen merkt man davon allerdings nichts, da der Overhead durch |
| 608 |
|
|
AnyEvent::Handle immer noch sehr gering ist, wenn man mit den Daten auch |
| 609 |
|
|
noch etwas tun möchte. |
| 610 |
|
|
|
| 611 |
root |
1.1 |
=back |
| 612 |
|
|
|
| 613 |
|
|
|
| 614 |
|
|
=head2 Benchmarks |
| 615 |
|
|
|
| 616 |
root |
1.2 |
Ich bin es gewohnt, meine eigene Lösungen zu schreiben, notfalls immer |
| 617 |
|
|
und immer wieder. Bevor ich eine (hilfreiche) Abstraktion ruhigen |
| 618 |
|
|
Gewissens benutzen kann, muss ich immer erst wissen, wie viel sie kostet, |
| 619 |
|
|
damit ich eine vernünftige Abwägung machen kann. |
| 620 |
|
|
|
| 621 |
|
|
Deshalb habe ich drei Benchmarks durchgeführt, um herauszufinden, wie |
| 622 |
|
|
hoch der Overhead von AnyEvent gegenüber der direkten Benutzung von |
| 623 |
|
|
Event-Bibliotheken ist, und wie verschiedene Event-Bibliotheken bei |
| 624 |
|
|
typischen Server-Situationen skalieren, sowohl für Server mit sehr vielen |
| 625 |
|
|
Verbindungen, als auch für Server mit sehr wenigen. |
| 626 |
|
|
|
| 627 |
|
|
=head3 Benchmark 1: Overhead |
| 628 |
|
|
|
| 629 |
|
|
Um den Overhead von AnyEvent selbst zu testen, erzeugt dieser Benchmark |
| 630 |
|
|
sehr viele Timer (mit einem Time-out von 0 Sekunden) und ebensoviele |
| 631 |
|
|
I/O watcher auf STDOUT (üblicherweise eine tty), der auf den |
| 632 |
|
|
"schreibbar"-Zustand wartet. |
| 633 |
|
|
|
| 634 |
|
|
Dann lässt er alle Watcher genau einmal ihren Callback aufrufen und |
| 635 |
|
|
zerstört die Watcher dann. |
| 636 |
|
|
|
| 637 |
|
|
Gemessen wird die Zeit, die es jeweils braucht, einen Watcher zu erzeugen, |
| 638 |
|
|
den Callback auszuführen und den Watcher wieder zu zerstören. |
| 639 |
|
|
|
| 640 |
|
|
Die Kernel-Schnittstelle sollte einen relativ geringen Einfluss besitzen, |
| 641 |
|
|
sofern die Event-Bibliothek einigermaßen effizient implementiert wurde, |
| 642 |
|
|
da nur ein einziges mal ein einziger File-Descriptor abgefragt werden |
| 643 |
|
|
muss. |
| 644 |
|
|
|
| 645 |
|
|
(Der Benchmark liegt AnyEvent als F<eg/bench> bei). |
| 646 |
|
|
|
| 647 |
|
|
Die Spalten in der Tabelle haben folgende Bedeutung: |
| 648 |
|
|
|
| 649 |
|
|
I<Watcher> ist die Anzahl der erzeugten Watcher-Objekte, d.h. die Anzahl |
| 650 |
|
|
der Timer + I/O-Watcher. |
| 651 |
|
|
|
| 652 |
|
|
I<Bytes> ist die Anzahl der RAM-Bytes, die ein Watcher-Objekt im Schnitt |
| 653 |
|
|
verbraucht. Dabei wird der Perl- und C-Anteil gezählt. |
| 654 |
|
|
|
| 655 |
|
|
I<Create> ist die durchschnittliche Zeit für die Erzeugung eines Watchers |
| 656 |
|
|
in millionstel Sekunden. |
| 657 |
|
|
|
| 658 |
|
|
I<Invoke> ist die Zeit, in millionstel Sekunden, die die Ausführung des |
| 659 |
|
|
Callbacks dauerte. Der Callback selbst zählt lediglich eine Variable |
| 660 |
|
|
herunter. |
| 661 |
|
|
|
| 662 |
|
|
I<Destroy> schließlich ist die durchschnittliche Zeit für das freigeben |
| 663 |
|
|
eines Watcher-Objektes, in millionstel Sekunden. |
| 664 |
|
|
|
| 665 |
|
|
Alle Benchmarks wurden auf einem Core2-Quad/3.6GHz durchgeführt, wobei |
| 666 |
|
|
das Programm mit Echtzeitpriorität auf CPU #3 lief. |
| 667 |
|
|
|
| 668 |
|
|
Name Watcher Bytes Create Invoke Destroy Hinweise |
| 669 |
root |
1.3 |
/uSec /uSec |
| 670 |
root |
1.2 |
EV/EV 400000 224 0.47 0.35 0.27 EV direkt |
| 671 |
|
|
EV/Any 100000 224 2.88 0.34 0.27 EV über AnyEvent |
| 672 |
|
|
Perl/Any 100000 452 4.13 0.73 0.95 AnyEvent/pure perl |
| 673 |
|
|
Event/Event 16000 517 32.20 31.80 0.81 Event direkt |
| 674 |
|
|
Event/Any 16000 590 35.85 31.55 1.06 Event über AnyEvent |
| 675 |
|
|
Glib/Any 16000 1357 102.33 12.31 51.00 >quadratisches Wachstum |
| 676 |
|
|
Tk/Any 2000 1860 27.20 66.31 14.00 SEGV bei >> 2000 watchers |
| 677 |
|
|
POE/Event 2000 6328 109.99 751.67 14.02 via POE::Loop::Event |
| 678 |
|
|
POE/Select 2000 6027 94.54 809.13 579.80 via POE::Loop::Select |
| 679 |
|
|
|
| 680 |
|
|
Wie erwähnt, ist der Benchmark so designed, daß das Kernel-Interface |
| 681 |
|
|
(select, epoll etc.) keinen Einfluss hat. Das bedeutet, daß die |
| 682 |
|
|
Skalierbarkeit der Event-Bibliothek hier I<nicht> gemessen wird. |
| 683 |
|
|
|
| 684 |
|
|
Die Anzahl der Watcher wurde so gewählt, daß die Ausführungszeit für |
| 685 |
|
|
jede Event-Bibliothek ähnlich war: würde man mit EV weniger Watcher |
| 686 |
|
|
wählen, bekäme man keine aussagekräftigen Zahlen, würde man mit Tk |
| 687 |
|
|
mehr wählen, würde es crashen, und würde man bei Glib oder POE mehr |
| 688 |
root |
1.3 |
wählen, würde ich wohl noch nächstes Jahr auf die Ergebnisse |
| 689 |
root |
1.2 |
warten. |
| 690 |
|
|
|
| 691 |
|
|
Wie man sieht, ist die Ausführung von Callbacks und das Zerstören von Watchern |
| 692 |
|
|
bei EV direkt (ohne AnyEvent) und bei AnyEvent mit EV als backend identisch, was daran liegt, daß AnyEvent keinerlei Overhead hinzufügt. |
| 693 |
|
|
|
| 694 |
|
|
Lediglich beim Erzeugen von Watchern ist AnyEvent+EV deutlich langsamer |
| 695 |
|
|
als EV direkt. Das liegt daran, daß AnyEvent a) einen Methodenaufruf und |
| 696 |
|
|
b) benannte Parameter benutzt. Diese beiden Umstände machen das Erzeugen |
| 697 |
|
|
von Watchern mehr als fünf mal langsamer als die direkte Benutzung von |
| 698 |
|
|
EV. |
| 699 |
|
|
|
| 700 |
|
|
Auch ist der Speicherverbrauch mit durchschnittlich 224 Bytes bei beiden |
| 701 |
|
|
gleich. |
| 702 |
|
|
|
| 703 |
|
|
Sehr erfreulich ist auch, daß die Pure-Perl Event-Implementation nicht |
| 704 |
|
|
viel hinterherhinkt: Erzeugen von Watchern und die Callback-Aufrufe |
| 705 |
|
|
sind ungefähr halb so schnell wie mit der stark optimierten libev |
| 706 |
|
|
C-Bibliothek. Der Speicherverbrauch ist ebenfalls noch im Rahmen und liegt |
| 707 |
|
|
ca. beim doppelten dessen von EV. |
| 708 |
|
|
|
| 709 |
|
|
Etwas enttäuscht hat mich Event: Event war meine Bibliothek der Wahl, ich |
| 710 |
|
|
hielt sie für sehr schnell und korrekt, musste aber im letzten Jahrzehnt |
| 711 |
|
|
einige größere Design-Schwächen erkennen. |
| 712 |
|
|
|
| 713 |
|
|
Überrascht hat mich allerdings, wie viel langsamer Event gegenüber einer |
| 714 |
|
|
Pure-Perl Implementation ist. Tatsächlich habe ich einige Zeit mit dem |
| 715 |
root |
1.3 |
Quellcode von Event verbracht und musste feststellen,daß Event in der Tat |
| 716 |
root |
1.2 |
gewaltigen Aufwand beim Aufruf von Callbacks betreibt, was sich in der |
| 717 |
|
|
Zeit niederschlägt. Die langsame Erzeugungszeit liegt dagegen schlicht |
| 718 |
|
|
daran, daß jeder Parameter intern in einen dynamischen Methodenaufruf |
| 719 |
|
|
umgesetzt wird. |
| 720 |
|
|
|
| 721 |
|
|
Wie man am Speicherverbrauch und dem Zeitunterschied sieht, ist die |
| 722 |
|
|
direkte Benutzung von Event etwas effizienter als den Umweg über AnyEvent |
| 723 |
|
|
zu gehen, allerdings ist der Overhead im Vergleich zur Langsamkeit von |
| 724 |
|
|
Event sehr gering. |
| 725 |
|
|
|
| 726 |
|
|
Wie man an den anderen Zeilen sieht, liegt Event allerdings recht |
| 727 |
root |
1.3 |
gut: Glib ruft Callbacks zwar schneller auf, die anderen Zeiten sind aber grauenhaft. |
| 728 |
root |
1.2 |
|
| 729 |
|
|
Auch Tk (sofern es nicht crasht :) ist nicht wirklich schneller, und POE |
| 730 |
|
|
ist jenseits von Gut und Böse. |
| 731 |
|
|
|
| 732 |
|
|
Tatsächlich ist die Implementation von AnyEvent auf POE nicht sehr |
| 733 |
|
|
effizient, da POE dies ausschließt. Wie der nächste Benchmark beweist, |
| 734 |
|
|
ist POE allerdings unter realistischen Bedingungen genauso langsam. |
| 735 |
|
|
|
| 736 |
|
|
Eine anderen Sicht der obigen Resultate ist, daß die Behandlung |
| 737 |
root |
1.3 |
eines Ereignisses mit EV ca. 1_600 Taktzyklen, mit AnyEvents Pure Perl |
| 738 |
|
|
Implementation ca. 3_100 Zyklen und mit POE beinahe 3_000_000 Taktzyklen |
| 739 |
|
|
benötigt_. |
| 740 |
root |
1.2 |
|
| 741 |
|
|
=head3 Benchmark 2: ein großer Server |
| 742 |
|
|
|
| 743 |
|
|
Was den AnyEvent-Overhead angeht, reicht mir der obige Benchmark völlig, |
| 744 |
root |
1.4 |
um mich zu beruhigen: der Overhead von AnyEvent über die Event-Bilbiothek |
| 745 |
|
|
hinaus ist gering, wenn man von EV absieht (was natürlich daran liegt, |
| 746 |
|
|
daß EV so schnell ist). |
| 747 |
root |
1.2 |
|
| 748 |
|
|
Um die Pure-Perl Event-Bibliothek von AnyEvent zu testen, muss aber |
| 749 |
|
|
ein anderer Benchmark her. Außerdem wollte ich unbedingt wissen, wie |
| 750 |
|
|
verschiedene Systeme in der Praxis abschneiden. |
| 751 |
|
|
|
| 752 |
|
|
Um einen großen Server zu simulieren, gibt es ein paar |
| 753 |
|
|
Standardbenchmarks. Einer davon ist es, sehr sehr viele Socketpaare zu |
| 754 |
|
|
erzeugen. Auf der einen Seite jedes Paares ist ein "Server", der ein |
| 755 |
|
|
einzelnes Octet liest, und dieses dann auf eine zufällige andere Socket |
| 756 |
root |
1.3 |
schreibt. Jedes Socketpaar hat einen assoziierten Timer, der bei jeder |
| 757 |
root |
1.2 |
Aktivität zurückgesetzt wird. |
| 758 |
|
|
|
| 759 |
|
|
Auf diese Weise wandern die Octets auf einem Teil der Verbindungen hin und |
| 760 |
|
|
her, was in etwa einem realen Server entspricht: sehr viele Verbindungen, |
| 761 |
|
|
aber nur ein kleiner Teil davon ist in jeder Iteration aktiv. |
| 762 |
|
|
|
| 763 |
root |
1.3 |
Im folgenden werden 10_000 Socketpaare (d.h. 20_000 Sockets) erzeugt, und |
| 764 |
root |
1.2 |
davon sind jeweils 100 "aktiv". |
| 765 |
|
|
|
| 766 |
|
|
(Der Benchmark liegt AnyEvent als F<eg/bench2> bei). |
| 767 |
|
|
|
| 768 |
|
|
Die Spalten bedeuten im einzelnen: |
| 769 |
|
|
|
| 770 |
|
|
I<Create> ist die Zeit, die es braucht, um ein Socketpaar zu erzeugen und |
| 771 |
|
|
alle Watcher zu registrieren, wieder in millionstel Sekunden. |
| 772 |
|
|
|
| 773 |
|
|
I<Request> ist bei weitem die wichtigste Größe. Sie gibt an, wie lange |
| 774 |
|
|
es im Schnitt dauert, um eine "Anfrage" zu bearbeiten und den Timeout |
| 775 |
|
|
zurückzusetzen. |
| 776 |
|
|
|
| 777 |
|
|
Name Create Request |
| 778 |
root |
1.3 |
/uSec /uSec |
| 779 |
root |
1.2 |
EV 69.01 11.16 |
| 780 |
|
|
Perl 73.32 35.87 |
| 781 |
|
|
Event 212.62 257.32 |
| 782 |
|
|
Glib 651.16 1896.30 |
| 783 |
|
|
POE 349.67 12317.24 mit POE::Loop::Event |
| 784 |
|
|
|
| 785 |
|
|
Dieser Benchmark misst nun direkt die Skalierbarkeit und die |
| 786 |
|
|
Geschwindigkeit der Event-Bibliothek. |
| 787 |
|
|
|
| 788 |
|
|
EV hat hier einen großen Vorteil, da es epoll, einen weitaus |
| 789 |
|
|
effizienteren Mechanismus als select oder poll, benutzen kann, während |
| 790 |
|
|
alle anderen entweder select oder poll benutzen. |
| 791 |
|
|
|
| 792 |
|
|
Wenig verwunderlich ist EV hier wieder mit Abstand am schnellsten. |
| 793 |
|
|
|
| 794 |
root |
1.3 |
Sehr erstaunt war ich über das sehr gute Abschneiden der Pure-Perl |
| 795 |
root |
1.2 |
Implementation. Sie ließ die anderen C-Bibliotheken weit hinter |
| 796 |
|
|
sich. Möglicherweise liegt der Grund hierfür darin, daß AnyEvent |
| 797 |
|
|
ganz ähnliche Algorithmen wie EV benutzt um I/O-Watcher und Timer zu |
| 798 |
|
|
verwalten, und ansonsten sehr abgespeckt ist. |
| 799 |
|
|
|
| 800 |
|
|
Event leidet sicher daran, daß es so teuer ist, Watcher zu erzeugen und |
| 801 |
|
|
upzudaten. |
| 802 |
|
|
|
| 803 |
|
|
Woran Glib leidet, ist schlichtweg unglaubliche Ineffizienz: Alle |
| 804 |
|
|
Watcher werden bei jeder Iteration vielmals angeschaut, teilweise |
| 805 |
|
|
sogar verglichen. Ich glaube nicht, daß man eine derart ineffiziente |
| 806 |
|
|
Event-Bibliothek erhielte, wenn man sich hinsetzte und einfach drauf los |
| 807 |
root |
1.3 |
schriebe ohne sich Gedanken zu machen. Tatsächlich ist Glib auch nicht |
| 808 |
root |
1.2 |
dafür gedacht, effizient zu sein oder mehr als einen File Descriptor zu |
| 809 |
|
|
unterstützen (den zum X-Server nämlich). |
| 810 |
|
|
|
| 811 |
|
|
POE ist sicherlich etwas langsamer, als es sein könnte, da für jede |
| 812 |
|
|
Verbindung drei "sessions" eingesetzt wurden, während POE eine Session |
| 813 |
|
|
pro "Verbindung" vorschlägt. Allerdings ist derlei Kritik am Benchmark |
| 814 |
|
|
irrelevant: Auch in einem echten Server würde man für jede Session eine |
| 815 |
|
|
Session benutzen, und selbst wenn POE dreimal schneller würde wäre es |
| 816 |
|
|
immer noch über 100 mal langsamer als die Pure-Perl Event-Implementation |
| 817 |
|
|
von AnyEvent. |
| 818 |
|
|
|
| 819 |
root |
1.3 |
Im übrigen wurde in diesem Benchmark sogar die Pure-Perl Version von POE durch |
| 820 |
root |
1.2 |
eine optimierte Variante ersetzt wurde, die das C<Event>-Modul benutzt. |
| 821 |
|
|
|
| 822 |
root |
1.3 |
=head3 Benchmark 3: ein klitzekleiner Server |
| 823 |
root |
1.2 |
|
| 824 |
|
|
Benchmarks für riesige Systeme sind sicher wichtig, aber wenn man "nur |
| 825 |
root |
1.3 |
eben" mal AnyEvent benutzt, sollte es auch schnell sein, wenn man nur |
| 826 |
root |
1.2 |
sehr wenige aktive Verbindungen hat. |
| 827 |
|
|
|
| 828 |
|
|
Im folgenden wurde daher nur acht Socketpaare benutzt, von denen jeweils drei aktiv sind: |
| 829 |
|
|
|
| 830 |
|
|
Name Create Request |
| 831 |
root |
1.3 |
/uSec /uSec |
| 832 |
root |
1.2 |
EV 20.00 6.54 |
| 833 |
|
|
Perl 25.75 12.62 |
| 834 |
|
|
Event 81.27 35.86 |
| 835 |
|
|
Glib 32.63 15.48 |
| 836 |
|
|
POE 261.87 276.28 mit POE::Loop::Event |
| 837 |
|
|
|
| 838 |
root |
1.3 |
Wie man sieht, ändert sich nichts drastisch: Glib ist nun so schnell |
| 839 |
root |
1.2 |
wie die anderen, aber EV und AnyEvents Pure-Perl Implementation sind |
| 840 |
|
|
trotzdem schneller. |
| 841 |
|
|
|
| 842 |
|
|
Während POE weiterhin stark hinterherhinkt, für drei Verbindungen aber |
| 843 |
|
|
absolut adäquat ist :-> |
| 844 |
|
|
|
| 845 |
|
|
|
| 846 |
root |
1.1 |
=head2 Ausblick auf CPAN |
| 847 |
root |
1.2 |
|
| 848 |
|
|
AnyEvent existiert nicht in Isolation: Es gibt inzwischen eine stattliche |
| 849 |
|
|
Anzahl von CPAN-Modulen die AnyEvent und nur AnyEvent als Abhängigkeit |
| 850 |
root |
1.3 |
besitzen. Für AnyEvent ist es nicht so wichtig wie für andere |
| 851 |
root |
1.2 |
"Frameworks" (wie POE), viele weitere CPAN-Module zu benutzen, da |
| 852 |
|
|
AnyEvent-Module ja mit anderen Frameworks kombiniert werden können, |
| 853 |
|
|
umgekehrt ist dies allerdings nicht der Fall (AnyEvent + POE Programm |
| 854 |
|
|
geht, POE + Tk-Programm geht nicht). |
| 855 |
|
|
|
| 856 |
|
|
Hier ist eine fast erschöpfende Auswahl von AnyEvent-Modulen auf CPAN: |
| 857 |
|
|
|
| 858 |
|
|
=over 4 |
| 859 |
|
|
|
| 860 |
|
|
=item AnyEvent::HTTP |
| 861 |
|
|
|
| 862 |
root |
1.3 |
Ein ganz einfacher HTTP-Client. Da man mit LWP keine parallelen Abfragen |
| 863 |
root |
1.2 |
durchführen im selben Prozess kann, ist es recht hilfreich :) |
| 864 |
|
|
|
| 865 |
root |
1.3 |
=item AnyEvent::AIO |
| 866 |
root |
1.2 |
|
| 867 |
|
|
Integriert IO::AIO transparent in AnyEvent. AnyEvent::AIO ist ein ideales |
| 868 |
root |
1.3 |
Pendant zu AnyEvent: AnyEvent abstrahiert non-blocking I/O, IO::AIO |
| 869 |
root |
1.2 |
abstrahiert asynchrones I/O. |
| 870 |
|
|
|
| 871 |
|
|
=item AnyEvent::DBI |
| 872 |
|
|
|
| 873 |
|
|
Langsames, aber asynchrones, DBI-Frontend. |
| 874 |
|
|
|
| 875 |
|
|
=item AnyEvent::CouchDB |
| 876 |
|
|
|
| 877 |
|
|
Eine Schnittstelle zur CouchDB. Ein must have! |
| 878 |
|
|
|
| 879 |
|
|
=item AnyEvent::BDB |
| 880 |
|
|
|
| 881 |
|
|
Asynchrone Schnittstelle zur guten alten BerkelyDB. |
| 882 |
|
|
|
| 883 |
|
|
=item AnyEvent::IRC |
| 884 |
|
|
|
| 885 |
|
|
Event basiertes IRC. |
| 886 |
|
|
|
| 887 |
|
|
=item AnyEvent::XMPP |
| 888 |
|
|
|
| 889 |
|
|
Alternative zum eigenen Parser für Jabber-Pseudo-XML. |
| 890 |
|
|
|
| 891 |
|
|
=item AnyEvent::FastPing |
| 892 |
|
|
|
| 893 |
|
|
Wenn man mal eine Million Ping-Pakete pro Sekunde verschicken will. Eignet |
| 894 |
|
|
sich ideal um Firewalls abzuschießen :) |
| 895 |
|
|
|
| 896 |
|
|
=item AnyEvent::Mojo |
| 897 |
|
|
|
| 898 |
|
|
Wenn ich's bloß wüsste... |
| 899 |
|
|
|
| 900 |
|
|
=item AnyEvent::HTTPD |
| 901 |
|
|
|
| 902 |
|
|
Ein einfacher Webserver zum Einbetten in die eigene Applikation. |
| 903 |
|
|
|
| 904 |
|
|
=item Coro::AnyEvent |
| 905 |
|
|
|
| 906 |
|
|
Die Kombination von Coro-Threads und AnyEvent ist ideal - komplexe Logik |
| 907 |
|
|
kann man bequem per Thread implementieren, Low-Level Event-Handling geht |
| 908 |
|
|
meist per AnyEvent angenehmer. |
| 909 |
|
|
|
| 910 |
|
|
=back |