PHP asynchronous programming with Swoole: Part 2

This is the second post about Swoole and it contains code samples that take advantage of Swoole asynchronous functions and coroutines. The first post can be found here.

HTTP Requests

Asynchronous 1: We put each request in a separate coroutine and pass them a channel through which they will pass to us the results of each request.

$channel = new Swoole\Coroutine\Channel(2);

$url1 = 'www.mysite1.com';
$url2 = 'ww.mysite2.com';

go(function () use ($channel, $url1) {
    $cli1 = new SwooleClient($url1, 80);
    $cli1->setHeaders([
        'Host' => $url1,
    ]);

    $cli1->get('/');
    $channel->push([
        'code'    => $cli1->getStatusCode(),
        'headers' => $cli1->getHeaders(),
        'cookies' => $cli1->getCookies(),
        'body'    => substr($cli1->getBody(), 0, 100)
    ]);
});

go(function () use ($channel, $url2) {
    $cli2 = new SwooleClient($url2, 80);
    $cli2->setHeaders([
        'Host' => $url2,
    ]);

    $cli2->get('/contact');
    $channel->push([
        'code'    => $cli2->getStatusCode(),
        'headers' => $cli2->getHeaders(),
        'cookies' => $cli2->getCookies(),
        'body'    => substr($cli2->getBody(), 0, 100)
    ]);
});

$results[] = $channel->pop();
$results[] = $channel->pop();

Asynchronous 2: We keep both HTTP requests within the same coroutine and put them in defer modein order to allow a second request to execute while the first is waiting for its result.

$cli1 = new SwooleClient($url1, 80);
$cli1->setHeaders([
    'Host' => $url1,
]);
$cli1->setDefer();
$cli1->get('/');

$cli2 = new SwooleClient($url2, 80);
$cli2->setHeaders([
    'Host' => $url2,
]);
$cli2->setDefer();
$cli2->get('/contact');

$cli1->recv();
$cli2->recv();

var_dump('-------------------- 1');
var_dump($cli1->getStatusCode());
var_dump($cli1->errCode);
var_dump(substr($cli1->getBody(), 0, 300));
var_dump('-------------------- 2');
var_dump($cli2->getStatusCode());
var_dump($cli2->errCode);
var_dump(substr($cli2->getBody(), 0, 300));

Asynchronous 3: We just show the lines that make the request. The rest are the same with the previous example.

$cli1 = new SwooleClient('www.wordreference.com', 443, true);
$cli1->setDefer();
$cli1->get('/');

$cli2 = new SwooleClient('www.wordreference.com', 443, true);
$cli2->setDefer();
$cli2->get('/');

Database Requests

$connInfo1 = [
    'host'     => '...ip...',
    'port'     => 3306,
    'user'     => '...username...',
    'password' => '...password...',
    'database' => '...db name...'
];

$query1 = '...sql query...';
$query2 = '...sql query...';

Synchronous:

$mysql1 = new Swoole\Coroutine\MySQL();
$mysql1->connect($connInfo1);
if ($mysql1 == false) {
    var_dump("MySQL server 1 can't be connected.");
    return;
}

$results1 = $mysql1->query($query1, 2);


$mysql2 = new Swoole\Coroutine\MySQL();
$mysql2->connect($connInfo2);
$stmt = $mysql2->prepare($query2);
if ($stmt == false) {
    var_dump("MySQL server 2 can't be connected.");
    return;
}
$results2 = $stmt->execute([]);

Asynchronous:

$mysql1 = new Swoole\Coroutine\MySQL();
$mysql1->connect($connInfo1);
if ($mysql1 == false) {
    var_dump("MySQL server 1 can't be connected.");
    return;
}
$mysql1->setDefer(true);
$mysql1->query($query1, 2);


$mysql2 = new Swoole\Coroutine\MySQL();
$mysql2->connect($connInfo2);
$mysql2->setDefer(true);
$isPrepared = $mysql2->prepare($query2);

$stmt = $mysql2->recv();

if ($stmt == false) {
    var_dump("MySQL server 2 can't be connected.");
    var_dump($mysql2->errno, $mysql2->error);
    return;
}
$stmt->execute([]);


$results1 = $mysql1->recv();
$results2 = $stmt->recv();

DNS requests

Synchronous:

$ip1 = dns_get_record('www.ucla.edu', DNS_A);
$ip2 = dns_get_record('blog.gougousis.net', DNS_A);
$ip3 = dns_get_record('www.jpassion.com', DNS_A);

var_dump($ip1, $ip2, $ip3);

Asynchronous 1:

$ip1 = Swoole\Coroutine::dnsLookup('www.ucla.edu', 0.5);
$ip2 = Swoole\Coroutine::dnsLookup('blog.gougousis.net', 0.5);
$ip3 = Swoole\Coroutine::dnsLookup('www.jpassion.com', 0.5);

var_dump($ip1, $ip2, $ip3);

Asynchronous 2:

$ip1 = Swoole\Coroutine::getHostByName('www.ucla.edu');
$ip2 = Swoole\Coroutine::getHostByName('blog.gougousis.net');
$ip3 = Swoole\Coroutine::getHostByName('www.wordreference.com', AF_INET6);

var_dump($ip1, $ip2, $ip3);

Filesystem Requests

An example of a PHP script that parallelizes filesystem I/O with network I/O.

require_once 'vendor/autoload.php';

use Swoole\Coroutine\System;
use Swoole\Coroutine\WaitGroup;
use Swoole\Coroutine\Http\Client as SwooleClient;

go(function () {
    $wg = new WaitGroup();
    $wg->add(2);

    $start = microtime(true);

    go(function () use ($wg) {
        $start1 = microtime(true);

        $content = System::readFile('./bigfile1.pdf');
        $filezise1 = System::writeFile($targetFilePath1, $content);

        $content = System::readFile('./bigfile2.pdf');
        $filezise2 = System::writeFile($targetFilePath2, $content);

        $end1 = microtime(true);
        $time1 = $end1 - $start1;

        echo "Filename: $targetFilePath1 , Size: $filezise1 \n";
        echo "Filename: $targetFilePath2 , Size: $filezise2 \n";
        echo "Duration1: $time1 \n";

        $wg->done();
    });

    go(function () use ($wg) {
        $start2 = microtime(true);

        $cli1 = new SwooleClient('www.mysite.com', 80);
        $cli1->setHeaders([
            'Host' => "www.mysite.com",
        ]);
        $cli1->get('/');

        $end2 = microtime(true);
        $time2 = $end2 - $start2;

        echo "Status1: " . $cli1->getStatusCode() . " , Duration2: $time2 \n";

        $wg->done();
    });

    $wg->wait();

    $end = microtime(true);

    $dur = $end - $start;
    echo "Total duration: $dur \n";
});