はじめに
以前 pthreads
で非同期処理(厳密には並列処理)をする話を書きました。
- PHPで超簡単に並列処理をするPromiseを作ってPackagistに公開した話
- PHP Promiseで複数のソケットを同時に捌く
- PHPのpthreads(マルチスレッディング)でソケットを捌く
と、記事を書かせていただいたのですが、実は非同期処理の拡張の有名所で、 Swoole
があります。
Swooleは公式サイトにも書かれていますが、イベントドリブンな非同期処理をすることを専門に扱っている拡張機能です。
Swoole is an high-performance network framework using an event-driven, asynchronous, non-blocking I/O model which makes it scalable and efficient. It is written in C language without 3rd party libraries as PHP extension.
Swoole
を使うことによって、 WebSocket, TCP/UDP通信の非同期、I/Oの監視など様々なことが行なえます。
また、 Swoole
のバージョン2 では、Coroutineが導入され、通常の処理のようなものでも、非同期処理を組み込めるようになりました。
この記事は主に Coroutine について扱います
とりあえず Swoole の環境を用意する
PHP7.3のインストールと同時に、Swooleの環境を用意します。
なお PHP7.3は現時点での最新版のRC5です。
PHP7.3のインストールについては過去に書いた記事が参考になるかもしれません
FROM centos:7
# Setup
RUN yum -y install epel-release wget
RUN cd /tmp && wget https://downloads.php.net/~cmb/php-7.3.0RC5.tar.gz -O php-7.3.0RC5 && tar zxvf php-7.3.0RC5
RUN yum -y upgrade
# Dependencies installation
RUN yum -y install git gcc gcc-c++ make libxml2-devel libicu-devel openssl-devel autoconf
RUN wget https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.tar.gz -O cmake-3.12.0-Linux-x86_64.tar.gz && \
tar zxvf cmake-3.12.0-Linux-x86_64.tar.gz
RUN rm -rf /usr/share/aclocal && \
rm -rf /usr/share/applications && \
rm -rf /usr/share/mime
RUN mv -f cmake-3.12.0-Linux-x86_64/bin/* /usr/bin/ && \
mv -f cmake-3.12.0-Linux-x86_64/share/* /usr/share/
RUN mkdir /tmp/libzip && \
cd /tmp/libzip && \
curl -sSLO https://libzip.org/download/libzip-1.4.0.tar.gz && \
tar zxf libzip-1.4.0.tar.gz && \
cd libzip-1.4.0/ && \
mkdir build && \
cd build && \
cmake ../ && \
make && \
make install
# PHP installation
RUN cd /tmp/php-7.3.0RC5 && \
./configure --enable-pcntl --enable-intl --enable-zip --enable-pdo --enable-sockets --with-openssl && \
make && \
make install
RUN yum -y install autoconf
RUN cd /tmp && wget https://getcomposer.org/download/1.6.5/composer.phar && \
mv composer.phar /bin/composer && \
chmod a=rx /bin/composer
# Swoole Installation
RUN git clone https://github.com/swoole/swoole-src.git && \
cd swoole-src && \
phpize && \
./configure && \
make && \
make install
# Add an extension to php.ini
RUN echo extension=swoole.so >> /usr/local/lib/php.ini
CMD cd /var/www/html && php -S 0.0.0.0:12000
docker-composeは下記のようにしています
version: "3"
services:
php:
build: .
tty: true
volumes:
- ./:/var/www/html
ports:
- 12000:12000
docker-composeで起動させます。
$ docker-compose up --build
正常にSwooleが入っているか確認する
> docker-compose exec php bash -c "php -m | grep swoole"
swoole
swoole
の文字列があれば完璧 🙆
Coroutine を書いてみる
簡単な例
すごく簡単な例を書いてみます。
<?php
go(function () {
co::sleep(3);
echo "いぬ\n";
});
go(function () {
co::sleep(2);
echo "ねこ\n";
});
go(function () {
co::sleep(1);
echo "ちくわ\n";
});
こうしたとき、結果は
> docker-compose exec php bash -c "php /var/www/html/test.php"
ちくわ
ねこ
いぬ
となります。この時点ですごい感動 😂
co::sleep で止めつつ期待する値が出力されるかどうか試す
計算回数が違う for 文で試してみましょう
<?php
go(function () {
echo "けーき\n";
co::sleep(3);
echo "ちゅ〜る\n";
});
go(function () {
echo "さんぽ\n";
co::sleep(1);
echo "ねこじゃらし\n";
});
echo "日向ぼっこ\n";
期待する結果になりました。
> docker-compose exec php bash -c "php /var/www/html/test.php"
けーき
さんぽ
日向ぼっこ
ねこじゃらし
ちゅ〜る
普通の sleep を入れると
<?php
go(function () {
echo "けーき\n";
co::sleep(3);
echo "ちゅ〜る\n";
});
go(function () {
echo "さんぽ\n";
co::sleep(1);
echo "ねこじゃらし\n";
});
echo "日向ぼっこ\n";
sleep(2);
echo "サンマのおひたし\n";
こちらに関しては、少し期待してはいない値ではあるのですが、 sleep がそもそもすべてのコンテキストの動作を止めてしまうと考えると納得がいきそうです。
> docker-compose exec php bash -c "php /var/www/html/test.php"
けーき
さんぽ
日向ぼっこ
サンマのおひたし
ねこじゃらし
ちゅ〜る
- https://github.com/php/php-src/blob/67e0138c0dfd966624223911a0821f6c294ad1c6/ext/standard/basic_functions.c#L4552
- https://github.com/php/php-src/blob/67e0138c0dfd966624223911a0821f6c294ad1c6/main/php.h#L119
swoole の関数を使って、非同期に通信してみる
適当に Dockerfile
の CMDを変えてみます。
...
CMD cd /var/www/html && php -S 0.0.0.0:12000 index.php
index.php に下記を用意してみます。
<?php
echo '怒涛のねこ (' . $_GET['neko_power'] . ')';
test.php に下記を用意してみます。
<?php
go(function () {
$http = new \Co\Http\Client("localhost", 12000);
$http->get('/index.php?neko_power=nyan');
echo $http->body . "\n";
echo "あじのたい焼き\n";
});
go(function () {
$http = new \Co\Http\Client("localhost", 12000);
$http->get('/index.php?neko_power=biyo-n');
echo $http->body . "\n";
echo "さわらのブーメラン\n";
});
echo "ふぐのみじんぎり\n";
上記の結果は
> docker-compose exec php php /var/www/html/test.php
ふぐのみじんぎり
怒涛のねこ (nyan)
あじのたい焼き
怒涛のねこ (biyo-n)
さわらのブーメラン
になったり
> docker-compose exec php php /var/www/html/test.php
ふぐのみじんぎり
怒涛のねこ (biyo-n)
さわらのブーメラン
怒涛のねこ (nyan)
あじのたい焼き
だったりするので、正常に非同期になっていることがわかります。
Swoole についての話は以上です