Slide 1

Slide 1 text

Guzzle PromiseΛ࢖ͬͨ
 ඇಉظॲཧʹΑΔAPIίʔϧͷߴ଎Խ PHPΧϯϑΝϨϯε෱Ԭ2016 2016-05-21-Sat @suzuki

Slide 2

Slide 2 text

About me

Slide 3

Slide 3 text

My favorite account is "suzuki" GitHub: @suzuki Twitter: @suzuki

Slide 4

Slide 4 text

PHP Conference Kansai 2015 https://speakerdeck.com/suzuki/automatically-run-the-javascript-test-in-multiple-browsers

Slide 5

Slide 5 text

https://twitter.com/hasegawayosuke/status/704143316611235840

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Story

Slide 9

Slide 9 text

Background • ͦΖͦΖೖࣾ1ϲ݄ʹͳΔࠒͷ࿩ • ؆୯ͳमਖ਼Ҋ݅͸͍͔ͭ͘ऴΘΒͤͨ • ৽͍͠Ҋ݅ʹΞαΠϯ͞Εͨ

Slide 10

Slide 10 text

Requirement • 1࣌ؒʹ1ճͷόονॲཧͰ࣮ߦ͢Δ • 1ճͷॲཧʹ͖ͭ࠷େ100ສ݅ͷσʔλΛ
 APIͰૹ৴͢Δ • APIίʔϧ1ճͰɺ1݅ͷσʔλૹ৴͕Մೳ → 1࣌ؒʹ࠷େ100ສ݅ͷAPIίʔϧ͕ඞཁ

Slide 11

Slide 11 text

Think of the system • ࣮૷ݴޠ • PHPͰ͸ແཧʁ • Go΍Scala౳͕ద੾ͩͱͯ͠΋࣮૷ܦݧ͸ͳ͍ • ίʔϧઌͷAPI • ॳΊͯར༻͢ΔAPI • ΞαΠϯ࣌఺Ͱ͸ಈ࡞͕೺ѲͰ͖͍ͯͳ͍ → ॳΊͯͷݴޠͰॳΊͯͷAPIίʔϧ͢Δඞཁ͕ʂʁ

Slide 12

Slide 12 text

Policy to implement • ׳Ε͍ͯΔݴޠͰ·ͣ͸ಈ͔͢ • εϐʔυ͕଍Γͳ͚Ε͹ଞͷݴޠͰ࣮૷͢Δ → PHP + Guzzle ͰͷϦϑΝϨϯε࣮૷Λ͢Δ

Slide 13

Slide 13 text

Guzzle

Slide 14

Slide 14 text

Topic: What is guzzle ? http://eow.alc.co.jp/search?q=Guzzle

Slide 15

Slide 15 text

http://docs.guzzlephp.org/en/latest/

Slide 16

Slide 16 text

Usage use GuzzleHttp\Client; $client = new Client(); $response = $client->request( 'GET', 'http://localhost:8008/' );

Slide 17

Slide 17 text

GET, Magic method $response = $client->request( 'GET', 'http://localhost:8008/' ); $response = $client->get( 'http://localhost:8008/' );

Slide 18

Slide 18 text

Other magic methods $response = $client->get('http://localhost/'); $response = $client->delete('http://localhost/'); $response = $client->head('http://localhost/'); $response = $client->options('http://localhost/'); $response = $client->patch('http://localhost/'); $response = $client->post('http://localhost/'); $response = $client->put('http://localhost/');

Slide 19

Slide 19 text

Using Request object use GuzzleHttp\Client; use GuzzleHttp\Psr7\Request; $client = new Client(); $request = new Request( 'GET', 'http://localhost:8008' ); $response = $client->send($request);

Slide 20

Slide 20 text

Response object $response = $client->request( 'GET', 'http://localhost:8008/' ); echo get_class($response); => GuzzleHttp\Psr7\Response

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Test Server

Slide 23

Slide 23 text

server.js var http = require('http'); var sleep = require('sleep-async')(); var server = http.createServer(); var sleepMiliSecond = 1000; server.on('request', function(req, res) { console.log('Received.'); sleep.sleep(sleepMiliSecond, function() { res.writeHead(200, {'Content-Type': 'text/json'}); res.write('{"status": "ok"}'); res.end(); }); }); server.listen(8008, 'localhost');

Slide 24

Slide 24 text

server.js • ҙਤతʹ1ඵͷsleepΛೖΕ͍ͯΔ • গ͠஗ΊͷAPIΛγϛϡϨʔτ

Slide 25

Slide 25 text

Run server $ npm install $ node server.js

Slide 26

Slide 26 text

Request using Curl

Slide 27

Slide 27 text

sample01.php use GuzzleHttp\Client; $client = new Client(); $response = $client->request( 'GET', 'http://localhost:8008/' ); echo $response->getBody() . PHP_EOL;

Slide 28

Slide 28 text

Request using Guzzle

Slide 29

Slide 29 text

Summary of sample01.php • 1ճͷϦΫΤετͳͷͰɺॲཧͷ஗͞ʢ1ඵͷ sleepʣ͸͋·ΓؾʹͳΒͳ͍ • Ͱ͸ճ਺Λଟͯ͘͠ΈΔͱͲ͏ͳΔͷ͔ʁ

Slide 30

Slide 30 text

sample02.php for ($i = 0; $i < 10; ++$i) { $response = $client->request( 'GET', 'http://localhost:8008' ); echo $response->getBody() . PHP_EOL; }

Slide 31

Slide 31 text

Run sample02.php

Slide 32

Slide 32 text

Summary of sample02.php • ෳ਺ճͷAPIίʔϧ΋໰୊ͳ͘Ͱ͖ͨ • ͨͩ͠εϐʔυ͸஗͍ • 1ඵͷsleep͕10ճߦΘΕΔ

Slide 33

Slide 33 text

࣮Ҋ݅Ͱͷεϐʔυײ • ͜ͷํ๏ʹ͍ۙܗͰͷϦϑΝϨϯε࣮૷Λͨ͠ • 10,000ϦΫΤετʹ10਺෼͔͔ͬͨ • 50,000ϦΫΤετ͕1࣌ؒܦաͯ͠΋ऴΘΒͣ • ఘΊͯڧ੍ऴྃͨ͠ → ͜ͷ··Ͱ͸ཁ݅Λຬͨͤͳ͍

Slide 34

Slide 34 text

ղܾํ๏͸ʁ • σʔλΛ෼ׂ͢Δʁ • ෼ׂ਺ͷ਺͚ͩεΫϦϓτΛىಈʁ • ෳ਺ϓϩηεͰ࣮ߦ͢Δʁ • forkͯ͠ؤுΔʁ • ඇಉظϦΫΤετ͕͋ͬͨɺ͜ΕͰͳΜͱ͔ͳΔʂʁ → Guzzle ͷ Async ϦΫΤετΛ࢖ͬͯΈΔ͜ͱʹ

Slide 35

Slide 35 text

Async Requests

Slide 36

Slide 36 text

http://docs.guzzlephp.org/en/latest/quickstart.html#async-requests

Slide 37

Slide 37 text

Sync and Async methods $client->get() $client->getAsync() $client->request() $client->requestAsync()

Slide 38

Slide 38 text

sample03.php for ($i = 0; $i < 10; ++$i) { $response = $client->requestAsync( 'GET', 'http://localhost:8008/' ); echo $response->getBody() . PHP_EOL; }

Slide 39

Slide 39 text

Run sample03.php

Slide 40

Slide 40 text

Summary of sample03.php PHP Fatal error: Call to undefined method GuzzleHttp\Promise\Promise::getBody()

Slide 41

Slide 41 text

Not $response

Slide 42

Slide 42 text

Promise object $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); echo get_class($promise); => GuzzleHttp\Promise\Promise

Slide 43

Slide 43 text

sample04.php for ($i = 0; $i < 10; ++$i) { $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); // echo $response->getBody() . PHP_EOL; }

Slide 44

Slide 44 text

Run sample04.php

Slide 45

Slide 45 text

Summary of sample04.php • PHP Fatal error͸ग़ͳ͘ͳͬͨ • ࣮ߦʹ͸੒ޭ͍ͯ͠Δʁ • αʔόଆʹʮReceived.ʯͷද͕ࣔग़͍ͯͳ͍ • ϦΫΤετ͕ಧ͍͍ͯͳ͍ʁʁ • Promise ͬͯͳʹʁ ͋ͷ Promise ͷ͜ͱʁʁ

Slide 46

Slide 46 text

Promise

Slide 47

Slide 47 text

Famous in JavaScript http://azu.github.io/promises-book/

Slide 48

Slide 48 text

• ඇಉظॲཧΛந৅Խͨ͠ΦϒδΣΫτ 1SPNJTF What is Promise ? ඇಉظॲཧͷ࣮ߦ ੒ޭͨ࣌͠ SFTPMWF ࣦഊͨ࣌͠ SFKFDU

Slide 49

Slide 49 text

Method Chains 1SPNJTF then then 1SPNJTF 1SPNJTF 1SPNJTF catch

Slide 50

Slide 50 text

Promise overview var promise = new Promise(function(resolve, reject) { // Async routines if (isSuccess) { resolve(value); } else { reject(error); } });

Slide 51

Slide 51 text

Method Chains sample var promise = new Promise(...); promise .then(function(value) { if (isSuccess) { resolve(value); } else { reject(error); }; }) .then(function(value) { if (isSuccess) { resolve(value); } else { reject(error); }; }) .catch(function(error) { // handle error }) ; 1SPNJTF 1SPNJTF 1SPNJTF 1SPNJTF then then catch

Slide 52

Slide 52 text

Status of Promise ඇಉظॲཧͷ࣮ߦ ॲཧͷ੒൱ TUBUVT ࣮ߦલ 1FOEJOH ࣮ߦޙ ੒ޭ 'VMpMMFE ࣮ߦޙ ࣦഊ 3FKFDUFE

Slide 53

Slide 53 text

Spec: Promises/A+ https://promisesaplus.com/

Slide 54

Slide 54 text

Spec: ECMAScript 2015 (ES6) http://www.ecma-international.org/ecma-262/6.0/

Slide 55

Slide 55 text

Guzzle Promise

Slide 56

Slide 56 text

Guzzle Promises https://github.com/guzzle/promises#guzzle-promises

Slide 57

Slide 57 text

Promise is required by Guzzle https://github.com/guzzle/guzzle/blob/master/composer.json#L18

Slide 58

Slide 58 text

Guzzle Promise overview use GuzzleHttp\Promise\Promise; $promise = new Promise(function() use (&$promise) { // Async routine if ($isSuccess) { $promise->resolve($value); } else { $promise->reject($error); } });

Slide 59

Slide 59 text

Async Request using Promise use GuzzleHttp\Client; use Psr\Http\Message\ResponseInterface; use GuzzleHttp\Exception\RequestException; $client = new Client(); $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); $promise ->then(function(ResponseInterface $response) { echo $response->getBody() . PHP_EOL; }) ->otherwise(function(RequestException $e) { echo $e->getMessage() . PHP_EOL; }) ; $promise->wait();

Slide 60

Slide 60 text

sample05.php for ($i = 0; $i < 10; ++$i) { $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); $promise ->then(function(ResponseInterface $response) { echo $response->getBody() . PHP_EOL; }) ; $promise->wait(); }

Slide 61

Slide 61 text

Run sample05.php

Slide 62

Slide 62 text

Tooooo slow …

Slide 63

Slide 63 text

sample05.php for ($i = 0; $i < 10; ++$i) { $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); $promise ->then(function(ResponseInterface $response) { echo $response->getBody() . PHP_EOL; }) ; $promise->wait(); } Not Async

Slide 64

Slide 64 text

sample06.php use GuzzleHttp\Promise; $promises = []; for ($i = 0; $i < 10; ++$i) { $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); $promise ->then(function(ResponseInterface $response) { echo $response->getBody() . PHP_EOL; }); $promises[] = $promise; } Promise\all($promises)->wait();

Slide 65

Slide 65 text

Run sample06.php

Slide 66

Slide 66 text

Fast !!

Slide 67

Slide 67 text

sample07.php $promises = []; for ($i = 0; $i < 10000; ++$i) { $promise = $client->requestAsync( 'GET', 'http://localhost:8008/' ); $promise ->then(function(ResponseInterface $response) { echo $response->getBody() . PHP_EOL; }) ; $promises[] = $promise; } Promise\all($promises)->wait();

Slide 68

Slide 68 text

Run sample07.php

Slide 69

Slide 69 text

Summary of sample07.php PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes)

Slide 70

Slide 70 text

Why exhausted memory limit ? • $promises ഑ྻʹ10000ݸͷ Promise Φϒ δΣΫτ͕ೖͬͨ··ղ์͞Ε͍ͯͳ͍ • ͦΕͧΕͷ Promise ΦϒδΣΫτ͸ঢ়ଶΛอ ͍࣋ͯ͠ΔͷͰͦΕͳΓʹॏ͍σʔλྔ → Կ͔ղܾํ๏͸ʁ

Slide 71

Slide 71 text

Generator

Slide 72

Slide 72 text

http://php.net/manual/en/language.generators.overview.php

Slide 73

Slide 73 text

Generator $requests = function() { for ($i = 0; $i < 10; ++$i) { yield new Request( 'GET', 'http://localhost:8008/' ); } };

Slide 74

Slide 74 text

How to use Generator sample08.php use GuzzleHttp\Client; use GuzzleHttp\Psr7\Request; $client = new Client(); $requests = function() { for ($i = 0; $i < 10; ++$i) { yield new Request('GET', 'http://localhost:8008/'); } }; foreach ($requests() as $request) { $response = $client->send($request); echo $response->getBody() . PHP_EOL; }

Slide 75

Slide 75 text

Topic: Generator can use in ES6 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator

Slide 76

Slide 76 text

Pool

Slide 77

Slide 77 text

Pool overview use GuzzleHttp\Pool; $pool = new Pool($client, $requests(), [ 'concurrency' => 5, 'fulfilled' => function ($response, $index) { // when success }, 'rejected' => function ($reason, $index) { // when failed }, ]); Generator

Slide 78

Slide 78 text

Pool config LFZ UZQF QVSQPTF DPODVSSFODZ JOUFHFS NBYDPODVSSFODZQBSBNFUFS GVMpMMFE DBMMBCMFGVODUJPO $BMMXIFO1SPNJTFJTSFTPMWFE SFKFDUFE DBMMBCMFGVODUJPO $BMMXIFO1SPNJTFJTSFKFDUFE

Slide 79

Slide 79 text

sample09.php $client = new Client(); $requests = function() { for ($i = 0; $i < 10; ++$i) { yield new Request('GET', 'http://localhost:8008/'); } }; $pool = new Pool($client, $requests(), [ 'concurrency' => 5, 'fulfilled' => function(ResponseInterface $response, $index) { echo $response->getBody() . PHP_EOL; }, ]); $promise = $pool->promise(); $promise->wait();

Slide 80

Slide 80 text

Run sample09.php

Slide 81

Slide 81 text

Summary of sample09.php • Ұؾʹ5ͭͷϦΫΤετ͕౤͛ΒΕ͍ͯΔ • concurrency = 5 Ͱ΋͍ͩͿ଎͍ • Ͱ͸ concurrency Λ૿΍ͤ͹΋ͬͱ଎͍ʁ

Slide 82

Slide 82 text

Concurrency = 10 sample10.php $pool = new Pool($client, $requests(), [ 'concurrency' => 10, 'fulfilled' => function(ResponseInterface $response, $index) { echo $response->getBody() . PHP_EOL; }, ]);

Slide 83

Slide 83 text

Run sample11.php

Slide 84

Slide 84 text

Summary of sample10.php • concurrency = 5 ΑΓ΋͞Βʹ଎͍ • Ͱ͸ɺ΋ͬͱ΋ͬͱ૿΍ͤ͹͍͍ͷͰ͸ʁ

Slide 85

Slide 85 text

sample11.php $client = new Client(); $requests = function() { for ($i = 0; $i < 10000; ++$i) { yield new Request('GET', 'http://localhost:8008/'); } }; $pool = new Pool($client, $requests(), [ 'concurrency' => 1000, 'fulfilled' => function(ResponseInterface $response, $index) { echo sprintf("%5d: %s\n", $index, $response->getBody()); }, ]); $promise = $pool->promise(); $promise->wait();

Slide 86

Slide 86 text

Run sample11.php

Slide 87

Slide 87 text

Summary of sample11.php • 10000ճͷϦΫΤετʹ੒ޭ • ϝϞϦෆ଍ʹͳΒͳ͔ͬͨ • concurrency = 1000 Ͱ΋༨༟ʁ • Ͳ͜·Ͱ૿΍ͤΔ΋ͷͳͷ͔ʁ

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

࣮Ҋ݅Ͱͷ Pool ར༻ • concurrency = 100 Λ࠾༻ • APIαʔό΁ͷಉ࣌ΞΫηε਺͸ڐ༰ൣғ • ϝϞϦͷ࢖༻ྔ΋ڐ༰ൣғ • ໿50ສϦΫΤετΛ30෼͔͔Βͣʹ׬ྃՄೳ → 1࣌ؒ100ສϦΫΤετΛΫϦΞ

Slide 92

Slide 92 text

Conclusion

Slide 93

Slide 93 text

Promise • JavaScriptͰ࢖ΘΕ͍ͯΔPromise͕PHPʹ΋ • JavaScriptͷ஌͕ࣝPHPͰ׆͔ͤΔ • PHPͰ΋࢖ΘΕ͍ͯΔGenerator͕JSʹ΋ • PHPͷ஌͕ࣝJavaScriptͰ׆͔ͤΔ → ೔ࠒ͔Βෳ਺ͷݴޠΛ࢖͓ͬͯ͘ͱศར

Slide 94

Slide 94 text

Guzzle • ෳ਺ͷϦΫΤετ͕ඞཁͳΒAsyncΛར༻ • Promiseͷ֓೦Ͱܧଓ͢Δॲཧ͕΍Γ΍͍͢ • PoolΛ࢖͑͹઀ଓ਺੍ޚ΋؆୯ʹ • concurrency͸APIଆͱϝϞϦʹ༏͍͠਺஋Ͱ → ਺ेສ݅Λૹ৴͢Δ৔߹Ͱ΋҆ఆՔಇ

Slide 95

Slide 95 text

Company • ೖࣾ1ϲ݄Ͱʢ৽ਓʹ͸ʣ೉қ౓ߴΊͷҊ͕݅ ߱ͬͯ͘Δ͜ͱ΋͋Δ • ݫ͕͋͠͞Δձࣾ • ৘ใڞ༗͢Δͱ஥͔ؒΒΞυόΠε͕߱ͬͯ͘ Δ͜ͱ΋͋Δ • ༏͠͞΋͋Δձࣾ

Slide 96

Slide 96 text

We are hiring ! https://www.mercari.com/jp/jobs/

Slide 97

Slide 97 text

Thank you

Slide 98

Slide 98 text

Appendix

Slide 99

Slide 99 text

Sample codes • https://github.com/suzuki/guzzle-promise-sample

Slide 100

Slide 100 text

PHP side links • Guzzle Documentation • http://docs.guzzlephp.org/en/latest/ • Guzzle Promises • https://github.com/guzzle/promises • PHP Documentation: Generator • http://php.net/manual/en/language.generators.overview.php • AWS SDK for PHP • https://docs.aws.amazon.com/aws-sdk-php/v3/guide/

Slide 101

Slide 101 text

JavaScript side links • JavaScript Promiseͷຊ • http://azu.github.io/promises-book/ •Promise/A+ • https://promisesaplus.com/ •ECMAScript 2015 (ES6) • http://www.ecma-international.org/ecma-262/6.0/ •MDN Generator • https://developer.mozilla.org/en-US/docs/Web/JavaScript/ Reference/Global_Objects/Generator

Slide 102

Slide 102 text

References • Asynchronous API Interaction with Guzzle • https://speakerdeck.com/jeremeamia/asynchronous-api- interaction-with-guzzle • JavaScript Promises - Thinking Sync in an Async World • https://speakerdeck.com/kerrick/javascript-promises- thinking-sync-in-an-async-world • guzzleͰඇಉظϦΫΤετΛฒྻॲཧͰ͍ͬͺ͍౤͛Δ • http://hacknote.jp/archives/16095/

Slide 103

Slide 103 text

Topic: Use case of Pool https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/commands.html#commandpool

Slide 104

Slide 104 text

Topic: Use case of Promise https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/promises.html