phasync / server
PHP 的高性能异步 TCP 和 UDP 套接字服务器。
Requires
- phasync/phasync: ^1
Requires (Dev)
- friendsofphp/php-cs-fixer: dev-master
- pestphp/pest: 3.x-dev
README
这个库使得在 PHP 中创建高效的 TCP 或 UDP 服务器变得非常简单。它使用合理的配置细节包装了 \stream_socket_server()
,并允许您非常容易地处理多个并发连接。
示例 HTTP 服务器
// Starts listening to port 8080 on localhost Server::serve('tcp://127.0.0.1:8080', function($stream, $peer) { phasync::readable($stream); // Wait for data from the client fread($stream, 65536); phasync::writable($stream); // Wait until the client is ready to receive data fwrite($stream, "HTTP/1.1 200 Ok\r\n". "Connection: close\r\n". "Content-Length: 13\r\n". "\r\n". "Hello, $peer!" ); fclose($stream); });
性能
此实现性能非常好,在很多情况下比 Node.js 处理并发连接(在合成基准测试中每秒处理更多高达 50% 的请求)要快。这种性能得益于利用 phasync
协程和非阻塞 IO(如上面的示例中的 phasync::readable()
和 phasync::writable()
调用),以及 PHP 8.3 JIT。
类概述
Server
类提供了一个简化了创建高效网络服务器的接口。它处理套接字创建、配置和连接管理的复杂性,使您能专注于应用程序逻辑。
类方法
public static function serve(string $address, ?Closure $socketHandler = null, ?float $timeout = null): Fiber
创建并启动一个 TCP 或 UDP 服务器。对于 TCP 或 UNIX 套接字服务器,处理函数必须启动一个协程来处理多个并发连接。
参数
$address
(string): 绑定的地址,格式为protocol://host:port
(例如,tcp://127.0.0.1:8080
或udp://0.0.0.0:9000
)。$handler
(Closure): 对于 TCP 连接,闭包将在协程中运行,接收流资源和 peer 地址作为参数。对于 UDP 连接,闭包将在协程中运行,接收 Server 实例作为参数。$timeout
(float|null): 套接字操作的可选超时。
返回
Fiber
TCP 示例
Server::serve('tcp://127.0.0.1:8080', function($stream, $peer) { // ... handle TCP connection });
UDP 示例
Server::serve('udp://127.0.0.1:9000', function(Server $server) { while (true) { $data = $server->recvfrom(65536, 0, $peer); if ($data !== false) { $server->sendto("Response Data", 0, $peer); } } });
设计决策
禁用读和写缓冲区以及大块大小
与 PHP 管理读和写的缓冲区不同,开发者应尝试读取和写入大数据块。不要尝试读取 10 字节,而是每次读取 65536 字节,并在需要时自行缓冲。
当你读取大块数据时,缓冲的优势就会消失,最终结果会稍微快一些。此外,套接字的工作方式更符合开发者的预期。
简而言之
- 为了带宽而写入大块数据
- 为了低延迟而写入小块数据
- 始终读取大块数据并自行处理或缓冲数据
so_reuseport
默认情况下启用此功能。假设我们希望避免在应用程序终止后网络端口一段时间内处于忙碌状态,例如由于错误。此外,它还允许同一用户拥有的其他进程开始监听同一端口,从而简化了应用程序扩展到多个进程的过程。
TCP Nagle 算法
默认情况下,Nagle 算法被禁用。此决定的依据是假设如果开发者向套接字写入短消息,开发者希望该消息快速传递。对于比网络数据包更长的消息,Nagle 算法几乎没有影响。因此,通常我们支持开发者先构建更大的块再写入,例如,发送完整的 HTML 响应。请参阅上面关于禁用读写缓冲区的章节。
要启用 Nagle 算法,您必须向 phasync\Server
构造函数传递一个自定义流上下文。