swoole+phpfpm如何实现不同域名指向不同目录的多租户模式?
网友回复
好的,使用 Swoole 结合 PHP-FPM 实现基于不同域名指向不同目录的多租户模式是一个非常强大且灵活的方案。这种架构下,Swoole 扮演了传统架构中 Nginx 的角色:一个高性能的 HTTP 网关。
其核心思想是:
Swoole 作为前端 HTTP 服务器:接收所有客户端请求。
动态路由逻辑:在 Swoole 的 onRequest 事件中,检查请求头中的 Host 字段(即访问的域名)。
确定文档根目录:根据 Host 域名,从配置(如数组、配置文件、数据库)中查找对应的网站根目录(Document Root)。
代理到 PHP-FPM:Swoole 充当 FastCGI 客户端,将请求(包括动态计算出的 SCRIPT_FILENAME 参数)转发给后端的 PHP-FPM 服务进行处理。
返回响应:Swoole 接收 PHP-FPM 的执行结果,并将其返回给客户端。
这种方式可以让你在纯 PHP 环境中实现一个类似 Nginx server 块的功能,并且拥有 Swoole 带来的协程并发优势。
实现步骤
1. 环境准备安装 Swoole 扩展:确保你的 PHP 环境已经安装了 Swoole 扩展。
安装并运行 PHP-FPM:确保 PHP-FPM 服务正在运行,并记下其监听地址(通常是 Unix Socket 或 TCP Socket)。
例如,在 /etc/php/8.1/fpm/pool.d/www.conf 中找到 listen 配置,可能是 /run/php/php8.1-fpm.sock 或 127.0.0.1:9000。
创建租户目录和文件:
# 创建网站根目录 sudo mkdir -p /var/www/tenant-a sudo mkdir -p /var/www/tenant-b # 为租户A创建入口文件 echo "<?php echo '<h1>Welcome to Tenant A!</h1>'; phpinfo(); ?>" | sudo tee /var/www/tenant-a/index.php # 为租户B创建入口文件 echo "<?php echo '<h1>This is Tenant B speaking.</h1>'; phpinfo(); ?>" | sudo tee /var/www/tenant-b/index.php # 设置权限,确保php-fpm进程有权限读取 sudo chown -R www-data:www-data /var/www/tenant-a sudo chown -R www-data:www-data /var/www/tenant-b2. 编写 Swoole HTTP 网关服务器
我们将使用 Swoole 内置的协程 FastCGI 客户端 Swoole\Coroutine\FastCGI\Client 来与 PHP-FPM 通信。
server.php:
<?php
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Coroutine\Http\Server;
use Swoole\Coroutine\FastCGI\Client;
use Swoole\Coroutine\FastCGI\Client\Exception as FcgiException;
Co\run(function () {
// 创建 Swoole HTTP 服务器
$server = new Server('0.0.0.0', 9501, false);
// 租户配置:域名 => 网站根目录
// 在生产环境中,这应该从配置文件、数据库或服务发现中加载
$tenantConfig = [
'tenant-a.com' => '/var/www/tenant-a',
'tenant-b.com' => '/var/www/tenant-b',
];
// PHP-FPM 的监听地址 (请根据你的配置修改)
$fpmHost = 'unix:/run/php/php8.1-fpm.sock'; // Unix Socket 方式
// $fpmHost = '127.0.0.1:9000'; // TCP 方式
// 注册请求处理函数
$server->handle('/', function (Request $request, Response $response) use ($tenantConfig, $fpmHost) {
$host = $request->header['host'] ?? '';
// 1. 根据 Host 查找租户的文档根目录
$documentRoot = $tenantConfig[$host] ?? null;
if (!$documentRoot) {
$response->status(404);
$response->end("<h1>404 Not Found</h1><p>Tenant for host '{$host}' is not configured.</p>");
return;
}
// 2. 静态文件处理 (重要优化)
$file...点击查看剩余70%


