Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Асинхронный PHP. Onliner PHP Meetup

Avatar for zloyuser zloyuser
November 17, 2018

Асинхронный PHP. Onliner PHP Meetup

Avatar for zloyuser

zloyuser

November 17, 2018
Tweet

More Decks by zloyuser

Other Decks in Programming

Transcript

  1. Асинхронность Способность программной системы не блокировать поток выполнения. Асинхронная операция

    Операция не блокирующая поток выполнения программы до своего завершения. Теория
  2. public function update(User $user) { try { $sql = 'UPDATE

    users SET ...'; return $this->connection->execute($sql, $user->data()); } catch (DBException $error) { log($error->getMessage()); } return 0; } Блокирующие операции
  3. public function update(User $user) { try { $sql = 'UPDATE

    users SET ...'; return $this->connection->execute($sql, $user->data()); } catch (DBException $error) { log($error->getMessage()); } return 0; } Блокирующие операции
  4. public function update(User $user) { try { $sql = 'UPDATE

    users SET ...'; return $this->connection->execute($sql, $user->data()); } catch (DBException $error) { log($error->getMessage()); } return 0; } Блокирующие операции
  5. Блокирующие операции I/O • Файловая система: fwrite, file_get_contents, etc. •

    Базы Данных: \PDOConnection, \RedisClient, etc. • Процессы: exec, system, proc_open, etc. • stdin/stdout: readline, echo, print, etc. Прочие • Таймеры: sleep, usleep
  6. // class SqlConnection public function executeAsync(string $query) { if (!$socket

    = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) die(socket_last_error()); $data = $this->makeSqlBinaryData($query); socket_connect($socket, '127.0.0.1', 3306); socket_write($socket, $data, strlen($data)); ... } Асинхронный SQL клиент
  7. // class SqlConnection public function executeAsync(string $query) { ... socket_connect($socket,

    '127.0.0.1', 3306); socket_write($socket, $data, strlen($data)); return ??? } Асинхронный SQL клиент
  8. interface Promise { const STATUS_PENDING = 0; const STATUS_RESOLVED =

    1; const STATUS_REJECTED = 2; public function when(callable $onResolve, callable $onReject); public function resolve($data); public function reject(\Throwable $error); } Promise
  9. public function update(User $user) { $sql = 'UPDATE users SET

    ...'; $promise = DB::executeAsync($sql, $user->data()); $promise->when(function (int $rows) { echo "Affected rows: $rows"; }, function (\Throwable $error) { echo "Something goes wrong:" . $error->getMessage(); }); } Promise
  10. // class SqlConnection public function executeAsync(string $query) { ... $promise

    = new Promise(); Loop::onReadable($socket, function ($socket) use ($promise) { while ($data = socket_read($socket)) $buffer .= $data; $promise->resolve($this->convertData($data)); }); return $promise; } Event Loop
  11. while (true) { $read = $this->readSockets; $write = $this->writeSockets; stream_select($read,

    $write, null, 0); foreach ($read as $i => $stream) { call_user_func($this->readCallbacks[$i], $stream); } // Do same for write sockets } Event Loop
  12. $loop = React\EventLoop\Factory::create(); $factory = new ConnectionFactory($loop); $connection = $factory->createLazyConnection('127.0.0.1:3306');

    $connection->query('UPDATE users SET ...')->then( function (QueryResult $command) { echo count($command->resultRows) . ' row(s) in set.'; }, function (Exception $error) { echo 'Error: ' . $error->getMessage(); } ); Асинхронный SQL клиент. ReactPHP
  13. Loop::run(function() { $pool = Mysql\pool("host=127.0.0.1 port=3306 db=test"); try { $result

    = yield $pool->query("UPDATE users SET ..."); echo $result->affectedRows . ' row(s) in set.'; } catch (\Throwable $error) { echo 'Error: ' . $error->getMessage(); } }); Асинхронный SQL клиент. AMPHP
  14. Loop::run(function() { $pool = Mysql\pool("host=127.0.0.1 db=test"); try { $result =

    yield $pool->query("UPDATE users SET ..."); echo $result->affectedRows . ' row(s) in set.'; } catch (\Throwable $error) { echo 'Error: ' . $error->getMessage(); } }); Асинхронный SQL клиент. AMPHP
  15. Генераторы function generator() { yield 1; yield 2; yield 3;

    } $generator = generator(); // instanceof \Generator $generator->rewind(); while ($generator->valid()) { echo $generator->current(); }
  16. Генераторы. Взаимодействие function printer() { while (true) { echo yield;

    } } $generator = printer(); $generator->send('Hello'); $generator->send(' World!');
  17. Генераторы. Исключения function printer() { while (true) { try {

    echo yield; } catch (\Throwable $e) { echo $e->getMessage(); } } } printer()->throw(new \Exception('Hello world'));
  18. Generator и Promise class Generator implements \Iterator { public function

    send($data); public function throw(\Throwable $error); } class Promise { public function resolve($data); public function reject(\Throwable $error); }
  19. Корутины function coroutine(\Generator $gen) { $promise = $gen->current(); $promise->when(function($data) use

    ($gen) { $gen->send($data); coroutine($gen); }, function (\Throwable $error) use ($gen) { $gen->throw($error); coroutine($gen); }); }
  20. Теория Корутина компонент программы, который поддерживает остановку и продолжение выполнения

    с сохранением текущего состояния. Кооперативная многозадачность Тип многозадачности, при котором следующая задача выполняется только после того, как текущая задача явно объявит себя готовой отдать процессорное время другим задачам.
  21. Обработка запроса PHP Плюсы • Простая обработка ошибок • Не

    думаем про память Минусы • Ограничение количества процессов • Затраты на Context Switch
  22. Loop::run(function () { $app = new Application(); // application bootstrap

    $servers = [Socket\listen('0.0.0.0:80')]; $server = new Server($servers, new CallableRequestHandler( function (Request $request) use ($app) { $response = yield $app->dispatch($request); return new Response(Status::OK, [], $response); }) ); yield $server->start(); }); Асинхронный HTTP сервер
  23. • Отсутствие стандартов ◦ async-interop, React, Promise A+, AMPHP •

    Утечки памяти ◦ xdebug, strace • Блокирующие операции ◦ kelunik/loop-block • Поддержка библиотек ◦ Драйверы БД: Cassandra, Influx, ClickHouse Асинхронный PHP. Проблемы
  24. class UserRepository { public function find(int $id): iterable { $data

    = yield $this->db->query('SELECT * FROM users WHERE id = ?', $id); return User::fill($data); } } Асинхронный PHP. Проблемы
  25. Асинхронный PHP. Проблемы class UserRepository { public function find(int $id):

    \Generator { $data = yield $this->db->query('SELECT * FROM users WHERE id = ?', $id); return User::fill($data); } }
  26. Асинхронный PHP. Проблемы class UserRepository { /** * @param int

    $id * @return Promise<User> */ public function find(int $id) { $data = yield $this->db->query('SELECT * FROM users WHERE id = ?', $id); return User::fill($data); } }
  27. PHP 8 class UserRepository { public async function find(int $id):

    Promise<User> { $data = await $this->db->query('SELECT * FROM users WHERE id = ?', $id); return User::fill($data); } }
  28. PHP 8 class UserRepository { public async function find(int $id):

    Promise<User> { $data = await $this->db->query('SELECT * FROM users WHERE id = ?', $id); return User::fill($data); } } Generics
  29. PHP 8 class UserRepository { public async function find(int $id):

    Promise<User> { $data = await $this->db->query('SELECT * FROM users WHERE id = ?', $id); return User::fill($data); } } EventLoop