English Questions are OK
Wird mir der Vortrag helfen?
Übergang von sequentiellem Programm zu asynchron mit Futures
Javascript / E
Max Maischein
DZ BANK Frankfurt
Deutsche Zentralgenossenschaftsbank
Data Scientist
Interne Suchmaschine
Web Crawler / Intranet Crawler -> Windows
Warum linear? Weil's einfach ist.
preemptives Multitasking
fork() funktioniert erstaunlich gut unter Windows.
Erstaunlich gut ist leider nicht gut genug
Nur verständlich, wenn man das Bild oben hat
bzw. das Bild zu HTTP Requests...
geschachtelte State Machines sind schwierig
AnyEvent
Callback Hell
Javascript
async
/ await
Zwei neue Keywords um asynchronen Code schöner zu schreiben
Future::AsyncAwait
Perl 5.16+
Queue - eine Queue
1: our @queue;
UA - User Agent ("Requester", "Browser"):
LWP::UserAgent
1: $ua->get($url);
UA - User Agent ("Requester", "Browser"):
LWP::UserAgent
1: $ua->get($url);
Future
1: my $ua = Future::HTTP->new(); 2: $ua->http_get($url);
Extractor
z.B. HTML::Selector::XPath
... oder reguläre Ausdrücke
1: our %seen; 2: our @queue = @ARGV; 3: ... 4: do { 5: my $url = shift @queue; 6: #sleep 10; 7: fetch_and_extract( $url ); 8: } while (@queue); 9: print for @results;
Idealerweise wie die synchrone API, nur mit "->get()" dahinter:
1: my $response = $ua->get($url);
wird zu
1: my $response_future = $ua->get( $url ); # starten 2: # irgendwas machen 3: my $response = $response_future->get(); # Antwort verarbeiten
Idealerweise wie die synchrone API, nur mit "->get()" dahinter:
1: my $response = $ua->get($url);
wird zu
1: my $response_future = $ua->get( $url )->then(sub { 2: my ($response) = @_; 3: # Antwort verarbeiten 4: }); # starten
Modul: Future
->get()
ruft das eigentliche Framework auf
Immer noch State Machine im Hintergrund
... aber eine hübschere Fassade
"await" davor
"await" macht das implizite "->get()"
"await" davor
"await" macht das implizite "->get()"
1: my $response_future = $ua->get( $url ); # starten 2: my $response = $response_future->get(); # Antwort verarbeiten
"await" davor
"await" macht das implizite "->get()"
1: my $response = await $ua->get( $url );
1: my $f = $ua->get(); 2: $f->then( sub { 3: my( $response ) = @_; 4: print "Habe Antwort, weiter geht's\n"; 5: });
wird zu
1: my $response = await $ua->get(); 2: print "Habe Antwort, weiter geht's\n";
1: my $f = AnyEvent::Future->timer( after => 5 ); 2: $f->then( sub { 3: my( $response ) = @_; 4: print "Aufgewacht, weiter geht's\n"; 5: });
wird zu
1: my $f = await AnyEvent::Future->timer( after => 5 ); 2: print "Aufgewacht, weiter geht's\n";
1: our %seen; 2: our @queue = @ARGV; 3: ... 4: my @done; 5: repeat { 6: my $url = shift @queue; 7: # await sleep(10); 8: $pending{ $url } = 1; 9: fetch_and_extract( $url )->then(sub { 10: delete $pending{ $url }; 11: }); 12: 13: my $next_action; 14: if( @queue ) { 15: $next_action = Future->done(1) 16: } elsif( scalar keys %pending ) { 17: $next_action = Future->done(1) 18: } else { 19: $next_action = $done; 20: $done->done(@results) 21: } 22: $next_action 23: }, while => sub { $_[0]->get }); 24: @results = $done->get;
1: fork async 2: CPU Beliebige CPUs 1 CPU 3: Crash egal fatal 4: Race 5: conditions Ja eher nein 6: Framework Parallel::FM Future / AnyEvent / Mojolicious / ... 7: Globale 8: Variablen Nein Ja 9: Implementation Trivial Rewrite
Fragen?
avoid IPC
Use Dancer and WWW::Mechanize::Chrome in the same process
async
macht deutlich, wohin abgegeben werden kann
Nur an den direkten Aufrufer
Coro macht viel mehr Gymnastik
Kann dafür auch viel mehr
Möchte/brauche ich nicht
Queue sollte persistent sein
Anfrage und Antwort speichern (WARC)
1: Queue -> In flight -> Extractor -> WARC -> Results 2: ^+---- retry ---+ | 3: ^+---- new links ------+
Wenn Du alle Fragen kennst, die in Zukunft gestellt werden, warum bist Du dann Programmierer?
Wir möchten die Zahl der Requests wieder reduzieren
5 Sekunden bevor der nächste Request gesendet wird
1: sleep 5;