Async扩展

2018-10-10 11:29 更新

Async本身是一个非常有用的构造,它将通过其合作多任务基础设施提供可能的节省时间。然而,我们知道将有一组常用的功能,其中Async最有用:数据库访问和缓存,Web资源访问和流。

MySQL的

Async MySQL扩展类似于 mysqliHHVM附带的扩展。此扩展将主要用于Async创建连接和查询MySQL数据库。

完整的API将包含所有可以通过Async访问MySQL中的类和方法; 我们将在这里介绍一些更常见的情况。

连接到MySQL数据库的主要类别是 AsyncMysqlConnectionPool其主要方法是异Async connect()

查询数据库的主要类别是 AsyncMysqlConnection使用两种主要的查询方法,query()以及queryf()两种Async。还有一个功能来确保要执行的查询被安全地调用escapeString()

从查询中检索结果的主类是一个被称为抽象类AsyncMysqlResult,它本身有两个被称为的具体子类AsyncMysqlQueryResult 和 AsyncMysqlErrorResult。这些类的主要方法是,vectorRows()并且mapRows()都是非同步的。

<?hh
class AsyncMysqlConnectionPool {
  public function __construct(array $pool_options): void;
  public static async function connect(
    string $host,
    int $port,
    string $dbname,
    string $user,
    string $password,
    int $timeout_micros = -1,
    string $extra_key = ""): Awaitable<AsyncMysqlConnection>;
  // More methods in this class, of course
}

class AsyncMysqlConnection {
  public function query(string $query, int $timeout_micros)
    : Awaitable<AsyncMysqlResult>;
  public function queryf(string $pattern, ..$args)
    : Awaitable<AsyncMysqlResult>;
  public function escapeString(string $data): string;
  // More methods in this class, of course
}

class AsyncMysqlQueryResult extends AsyncMysqlResult {
  public function vectorRows(): Vector; // vector of Vectors
  public function mapRows(): Vector; // vector of Maps
  // return db column values as Hack types instead of
  // string representations of those types.
  public function vectorRowsTyped(): Vector;
  public function mapRowsTyped(): Vector;
  // More methods in this class, of course
}

class AsyncMysqlErrorResult extends AsyncMysqlResult {
  public function failureType(): string;
  public function mysql_errno(): int;
  public function mysql_error(): string;
  // More methods in this class, of course
}

以下是一个简单的示例,显示如何从具有此扩展名的数据库获取用户名。

<?hh

namespace Hack\UserDocumentation\Async\Extensions\Examples\MySQL;

use \Hack\UserDocumentation\Async\Extensions\Examples\AsyncMysql\ConnectionInfo as CI;

async function get_connection(): Awaitable<\AsyncMysqlConnection> {
  // Get a connection pool with default options
  $pool = new \AsyncMysqlConnectionPool(array());
  // Change credentials to something that works in order to test this code
  return await $pool->connect(
    CI::$host,
    CI::$port,
    CI::$db,
    CI::$user,
    CI::$passwd,
  );
}

async function fetch_user_name(\AsyncMysqlConnection $conn,
                               int $user_id) : Awaitable<string> {
  // Your table and column may differ, of course
  $result = await $conn->queryf(
    'SELECT name from test_table WHERE userID = %d',
    $user_id
  );
  // There shouldn't be more than one row returned for one user id
  invariant($result->numRows() === 1, 'one row exactly');
  // A vector of vector objects holding the string values of each column
  // in the query
  $vector = $result->vectorRows();
  return $vector[0][0]; // We had one column in our query
}

async function get_user_info(\AsyncMysqlConnection $conn,
                             string $user): Awaitable<Vector<Map>> {
  $result = await $conn->queryf(
    'SELECT * from test_table WHERE name = %s',
    $conn->escapeString($user)
  );
  // A vector of map objects holding the string values of each column
  // in the query, and the keys being the column names
  $map = $result->mapRows();
  return $map;
}

async function async_mysql_tutorial(): Awaitable<void> {
  $conn = await get_connection();
  if ($conn !== null) {
    $result = await fetch_user_name($conn, 2);
    var_dump($result);
    $info = await get_user_info($conn, 'Fred Emmott');
    var_dump($info instanceof Vector);
    var_dump($info[0] instanceof Map);
  }
}

\HH\Asio\join(async_mysql_tutorial());

Output

string(11) "Fred Emmott"
bool(true)
bool(true)

连接池

Async MySQL扩展不支持复用 - 每个并发查询需要自己的连接。但是,扩展支持连接池。

Async MySQL扩展提供了一种池连接对象的机制,因此您不需要在每次进行查询时创建新的连接。the class是AsyncMysqlConnectionPool 一个可以这样创建:

?hh

namespace Hack\UserDocumentation\Async\Extensions\Examples\MySQLConnectionPool;

use \Hack\UserDocumentation\Async\Extensions\Examples\AsyncMysql\ConnectionInfo as CI;

function get_pool(): \AsyncMysqlConnectionPool {
  return new \AsyncMysqlConnectionPool(
    array('pool_connection_limit' => 100)
  ); // See API for more pool options
}

async function get_connection(): Awaitable<\AsyncMysqlConnection> {
  $pool = get_pool();
  $conn = await $pool->connect(
    CI::$host,
    CI::$port,
    CI::$db,
    CI::$user,
    CI::$passwd,
  );
  return $conn;
}

async function run(): Awaitable<void> {
  $conn = await get_connection();
  var_dump($conn);
}

\HH\Asio\join(run());

Output

object(AsyncMysqlConnection)#6 (0) {
}

这是强烈建议您使用连接池为您的MySQL连接; 如果由于某种原因你真的需要一个,单个异步客户端,有一个AsyncMysqlClient提供一个connect()方法的类。

MCRouter

MCRouter是一个memcached协议路由库。为了帮助您的memcached memcached部署,它提供了连接池,基于前缀的路由等功能。

Async MCRouter扩展基本上是作为HHVM一部分的Memcached扩展的Async但是子集的版本。主要课程是MCRouter。有两种方法可以创建MCRouter对象的实例。该createSimple()采取其中Memcached是正在运行的服务器地址的矢量。更多的配置__construct()允许更多的选项调整。得到一个对象后,您可以使用Async的核心memcached协议的方法,如版本add()get()del()

class MCRouter {
  public function __construct(array<stirng, mixed> $options, string $pid = '');
  public static function createSimple(ConstVector<string> $servers): MCRouter;
  public async function add(string $key, string $value, int $flags = 0,
                            int $expiration = 0): Awaitable<void>;
  public async function get(string $key): Awaitable<string>;
  public async function del(string $key): Awaitable<void>;
  // More methods exist, of course
}

以下是一个简单的示例,显示如何从memcached获取用户名:

<?hh

namespace Hack\UserDocumentation\Async\Extensions\Examples\MCRouter;

require __DIR__ . "/../../../../vendor/hh_autoload.php"; // For wrap()

function get_mcrouter_object(): \MCRouter {
  $servers = Vector { getenv('HHVM_TEST_MCROUTER') };
  $mc = \MCRouter::createSimple($servers);
  return $mc;
}

async function add_user_name(
  \MCRouter $mcr,
  int $id,
  string $value): Awaitable<void> {
  $key = 'name:' . $id;
  await $mcr->set($key, $value);
}

async function get_user_name(\MCRouter $mcr, int $user_id): Awaitable<string> {
  $key = 'name:' . $user_id;
  try {
    $res = await \HH\Asio\wrap($mcr->get($key));
    if ($res->isSucceeded()) {
      return $res->getResult();
    }
    return "";
  } catch (\MCRouterException $ex) {
    echo $ex->getKey() . PHP_EOL . $ex->getOp();
    return "";
  }
}

async function run(): Awaitable<void> {
  $mcr = get_mcrouter_object();
  await add_user_name($mcr, 1, 'Joel');
  $name = await get_user_name($mcr, 1);
  var_dump($name); // Should print "Joel"
}

\HH\Asio\join(run());

Output

string(4) "Joel"

如果使用此协议时出现问题,则可能会抛出两个可能的异常。MCRouterException当核心选项发生错误时,如删除密钥一样被抛出。MCRouterOptionException当您提供不可解析的选项列表时发生。

卷曲

Hack目前为cURL提供了两个Async功能。

curl_multi_await

cURL为URL提供了一个数据传输库。异步cURL扩展提供了两个功能,其中一个是围绕另一个的包装。curl_multi_await()是HHVM的异步版本curl_multi_select()。它等待直到cURL句柄上有活动,并且完成后,您可以使用curl_multi_exec()处理结果,就像在non-async情况下一样。

async function curl_multi_await(resource $mh,
                                float $timeout = 1.0): Awaitable<int>;

curl_exec

HH\Asio\curl_exec()是一个包装纸curl_multi_await()。它很容易使用,因为您不必担心资源创建,因为您只需传递字符串URL即可。

namespace HH\Asio {
  async function curl_exec(mixed $urlOrHandle): Awaitable<string>;
}

以下是使用lambda来获取URL内容向量的示例,以减少使用完全关闭语法的代码冗长度。

<?hh

namespace Hack\UserDocumentation\Async\Extensions\Examples\Curl;

function get_urls(): Vector<string> {
  return Vector {
    "http://example.com",
    "http://example.net",
    "http://example.org",
  };
}

async function get_combined_contents(Vector $urls): Awaitable<Vector<string>> {
  // Use lambda shorthand syntax here instead of full closure syntax
  $handles = $urls->mapWithKey(($idx, $url) ==> \HH\Asio\curl_exec($url));
  $contents = await \HH\Asio\v($handles);
  echo $contents->count();
  return $contents;
}

\HH\Asio\join(get_combined_contents(get_urls()));

Output

3

Streams

Async stream扩展有一个功能, stream_await(),其功能类似于HHVM stream_select()。它等待流进入状态(例如STREAM_AWAIT_READY),但没有多路复用功能stream_select()。您可以使用HH \ Asio \ v()等待多个流句柄,但是所有组合等待的结果将不会完成,直到所有底层流完成。

async function stream_await(resource $fp, int $events,
                            float $timeout = 0.0): Awaitable<int>;

此示例显示如何使用 stream_await() 写资源。

<?hh

namespace Hack\UserDocumentation\Async\Extensions\Examples\Stream;

function get_resources(): array<resource> {
  $r1 = fopen('php://stdout', 'w');
  $r2 = fopen('php://stdout', 'w');
  $r3 = fopen('php://stdout', 'w');

  return array($r1, $r2, $r3);
}

async function write_all(array<resource> $resources): Awaitable<void> {
  // UNSAFE : the typechecker isn't aware of stream_await until 3.12 :(
  $write_single_resource = async function(resource $r) {
    $status = await stream_await($r, STREAM_AWAIT_WRITE, 1.0);
    if ($status === STREAM_AWAIT_READY) {
      fwrite($r, str_shuffle('ABCDEF') . PHP_EOL);
    }
  };
  // You will get 3 shuffled strings, each on a separate line.
  await \HH\Asio\v(array_map($write_single_resource, $resources));
}

\HH\Asio\join(write_all(get_resources()));

Output

DCFAEB
BAFCDE
ACBDEF
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号