本文目录导读:

- 使用非阻塞流模式
- 使用 stream_select 轮询
- 使用 Swoole 扩展(推荐)
- 使用 ReactPHP(纯 PHP 实现)
- 使用 Amp 库
- 数据库 I/O 优化
- 文件 I/O 优化
- 混合使用异步和同步
- 最佳实践建议
在 PHP 中改造阻塞 I/O 是一个常见的优化需求,我将从几个层面来介绍解决方案:
使用非阻塞流模式
设置非阻塞模式
<?php // 设置流为非阻塞模式 stream_set_blocking($stream, false); // 或者使用 fcntl $flags = fcntl($stream, F_GETFL, 0); fcntl($stream, F_SETFL, $flags | O_NONBLOCK);
示例:非阻塞读取
<?php
$server = stream_socket_server("tcp://0.0.0.0:8080", $errno, $errstr);
stream_set_blocking($server, false);
while (true) {
$client = @stream_socket_accept($server, 0);
if ($client) {
stream_set_blocking($client, false);
// 处理客户端连接
}
// 继续处理其他任务
usleep(1000);
}
使用 stream_select 轮询
<?php
$server = stream_socket_server("tcp://0.0.0.0:9000", $errno, $errstr);
$clients = [$server];
while (true) {
$read = $clients;
$write = null;
$except = null;
$ready = stream_select($read, $write, $except, 0, 200000); // 200ms超时
if ($ready > 0) {
foreach ($read as $socket) {
if ($socket === $server) {
$client = stream_socket_accept($server);
$clients[] = $client;
} else {
$data = fread($socket, 8192);
if (strlen($data) > 0) {
// 处理数据
}
}
}
}
}
使用 Swoole 扩展(推荐)
异步方式
<?php
// 安装 swoole: pecl install swoole
$server = new Swoole\Server('0.0.0.0', 9501);
$server->on('connect', function ($server, $fd) {
echo "Client: {$fd} connected\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
// 异步处理
$server->send($fd, "Received: " . $data);
});
$server->on('close', function ($server, $fd) {
echo "Client: {$fd} closed\n";
});
$server->start();
协程方式
<?php
Co\run(function () {
$server = new Co\Socket(AF_INET, SOCK_STREAM, 0);
$server->bind('0.0.0.0', 9502);
$server->listen();
while (true) {
$client = $server->accept();
go(function () use ($client) {
while (true) {
$data = $client->recv();
if (!$data) break;
$client->send("Echo: " . $data);
}
});
}
});
使用 ReactPHP(纯 PHP 实现)
<?php
// 安装: composer require react/socket
require __DIR__ . '/vendor/autoload.php';
use React\EventLoop\Factory;
use React\Socket\ConnectionInterface;
use React\Socket\SocketServer;
$loop = Factory::create();
$server = new SocketServer('0.0.0.0:8080', $loop);
$server->on('connection', function (ConnectionInterface $connection) {
echo "New connection from: " . $connection->getRemoteAddress() . PHP_EOL;
$connection->on('data', function ($data) use ($connection) {
echo "Received: " . $data;
$connection->write("Echo: " . $data);
});
$connection->on('close', function () {
echo "Connection closed\n";
});
});
echo "Server running at 0.0.0.0:8080\n";
$loop->run();
使用 Amp 库
<?php
// 安装: composer require amphp/amp amphp/socket
require __DIR__ . '/vendor/autoload.php';
use Amp\Loop;
use Amp\Socket\Socket;
Loop::run(function () {
$server = Amp\Socket\listen('0.0.0.0:8080');
while ($socket = yield $server->accept()) {
Amp\asyncCall(function () use ($socket) {
while (null !== $chunk = yield $socket->read()) {
yield $socket->write("Echo: " . $chunk);
}
});
}
});
数据库 I/O 优化
<?php
// 使用 async MySQL 查询
$db = new MysqliDb();
$db->setAsyncMode(true);
$result = $db->queryAsync("SELECT * FROM users WHERE id > ?", [1000]);
// 先处理其他业务
do_other_work();
// 稍后获取结果
if ($result) {
$rows = $db->fetchAll(true); // 等待并获取结果
process_users($rows);
}
文件 I/O 优化
<?php
// 使用非阻塞文件读取
$fp = fopen('/path/to/large_file.log', 'r');
stream_set_blocking($fp, false);
while (!feof($fp)) {
$line = fgets($fp, 4096);
if ($line !== false) {
process_line($line);
} else {
// 没有数据,先做其他事情
do_other_work();
usleep(10000); // 10ms 后重试
}
}
混合使用异步和同步
<?php
// 处理多个数据库连接并行
class AsyncDBHandler {
private $connections = [];
public function asyncQuery($dbConfig, $query) {
$conn = new PDO();
$conn->query($query);
$this->connections[] = $conn;
}
public function waitForResults() {
while ($this->connections) {
foreach ($this->connections as $key => $conn) {
if ($conn->inTransaction()) {
// 检查结果是否可用
}
}
usleep(1000);
}
}
}
最佳实践建议
-
根据场景选择方案:
- 简单场景:使用
stream_select或非阻塞模式 - 高并发:选择 Swoole 或 Workerman
- 需要跨平台:使用 ReactPHP 或 Amp
- 简单场景:使用
-
性能考虑:
// 设置合理的超时时间 stream_set_timeout($stream, 0, 100000); // 100ms // 使用缓冲 stream_set_write_buffer($stream, 8192);
-
错误处理:
// 非阻塞模式下的错误处理 if (false === fread($stream, 8192)) { // 检查是否是 EAGAIN/EWOULDBLOCK $err = socket_last_error(); if ($err !== SOCKET_EAGAIN && $err !== SOCKET_EWOULDBLOCK) { // 真正的错误 throw new Exception("Read error: " . socket_strerror($err)); } } -
监控和调试:
// 使用断言确保非阻塞模式生效 assert(!stream_get_meta_data($stream)['blocked']); // 添加调试日志 $start = microtime(true); if (false !== $data = fread($stream, 8192)) { $elapsed = microtime(true) - $start; if ($elapsed > 0.1) { error_log("Slow read: {$elapsed}s"); } }
非阻塞 I/O 改造的核心是不等待,通过异步处理、事件循环、协程等机制,让程序在等待 I/O 时能够执行其他任务,选择具体方案时要考虑:
- 项目复杂度
- 性能需求
- 团队技术栈
- 运维成本
如果是新项目,强烈推荐使用 Swoole 或 Workerman,它们提供了更完善的非阻塞 I/O 支持。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播