Fiber 协程
workerman 从 5.0.0 开始支持 Fiber 协程 (纤程)
注意 Fiber 特性需要 PHP>=8.1 并安装
composer require revolt/event-loop ^1.0.0
介绍
Fiber 是 php 内置的协程 (纤程),它可以中断 PHP 代码,然后在需要的时候恢复其运行。它的最大作用是让开发者可以用同步的方式写异步非阻塞代码,这极大地增强了代码的可维护性。
示例
下面通过示例来对比协程和异步回调编程的区别。 假设我们有一个需求要求需要调用一个 HTTP 接口,然后延迟一秒响应,异步回调及协程写法分别如下。
异步回调用法
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Timer;
use Workerman\Http\Client;
$worker = new Worker('http://0.0.0.0:12345');
$worker->onMessage = static function($connection, $request)
{
static $http;
$http = $http ?: new Client();
// 请求HTTP接口
$http->get('http://example.com/', function ($response) use ($connection) {
// 延迟一秒发送
Timer::add(1, function() use ($connection, $response) {
// 向浏览器发送数据
$connection->send((string)$response->getBody());
}, null, false);
});
};
Worker::runAll();
协程用法
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Workerman\Timer;
use Workerman\Http\Client;
$worker = new Worker('http://0.0.0.0:12345');
$worker->onMessage = static function($connection, $request)
{
static $http;
$http = $http ?: new Client();
// 调用HTTP接口
$response = $http->get('http://example.com/');
// 延迟1秒
Timer::sleep(1);
// 发送数据
$connection->send((string)$response->getBody());
};
Worker::runAll();
注意 以上代码需要安装 composer require workerman/http-client ^2.0.0
这两种写法都是异步非阻塞执行的,运行效率都很好,但是协程用法比异步回调更容易阅读,更容易维护。
Fiber 注意事项
- Fiber 协程并不支持将 Pdo、Redis、及 PHP 内部阻塞函数协程化,也就是说使用这些扩展及函数仍然是阻塞式调用
- 目前可用的 Fiber 协程客户端有 workerman/http-client,workerman/redis
Swoole 协程
workerman v5 同时支持将 Swoole 作为底层事件驱动
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use Swoole\Coroutine\Http\Client;
use Swoole\Coroutine;
// 这里需要手动将Swoole设置为底层事件驱动
Worker::$eventLoopClass = Workerman\Events\Swoole::class;
$worker = new Worker('http://0.0.0.0:12345');
$worker->onMessage = static function($connection, $request)
{
Coroutine::create(function() use ($connection) {
$cli = new Client('example.com', 80);
$cli->get('/get');
$connection->send($cli->body);
});
};
Worker::runAll();
提示
- 建议使用 swoole5.0 或者后续更高版本
- 把 swoole 作为底层事件驱动可以让 workerman 支持 swoole 的协程
- 使用 swoole 作为底层事件驱动时可以不安装 event 扩展
- swoole 默认没有开启一键协程,也就是说基于 Pdo、Redis、PHP 内置文件读写是阻塞式调用
- 如需开启一键协程,需要手动调用
\Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
更多请参考 Swoole 手册
更多请参考事件驱动
关于协程
首先没必过份迷信协程,当数据库、Redis 等存储都在内网时,多进程阻塞式调用很多时候比协程更快。从 techempower.com 3 年来的压测数据来看 workerman 阻塞式数据库调用性能要优于 swoole 数据库连接池 + 协程,甚至比 go 语言的 gin、echo 等协程框架性能高近 1 倍。
workerman 已经将 PHP 应用的性能提升了数倍甚至数十倍,绝大部分 workerman 项目加上协程可能不会有更大的性能提升。 如果你的系统里有慢调用,例如有外部 HTTP 调用,可以考虑使用协程来提升性能。