使用Swoole协程一键代理如WordPress等的PHP-FPM服务

在Swoole最新发布的v4.5的版本中,新增了一个很有意思的新特性:协程版本的FastCGI客户端。

什么是FastCGI

官方解释为:

快速通用网关接口(Fast Common Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。

FastCGI接口方式采用C/S架构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或多个脚本解析守护进程。

当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程执行,然后将得到的结构返回给浏览器。

这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

实现了FastCGI客户端,那么我们就可以直接与PHP-FPM服务进行交互,至于有什么用呢,具体可以看Twosee大佬发布的原文:使用Swoole协程一键代理PHP-FPM服务

协程FastCGI客户端作用是什么?

协程FastCGI客户端的到来,相当于我们的协程应用现在拥有了PHP-FPM这样一个无比强大稳定的进程管理器作为Task进程池来完成同步阻塞任务,借此我们可以解决很多问题,如:

  • 有一些协议暂未受到Swoole协程的支持,但却有可用的同步阻塞的版本(MongoDBsqlserver等),我们就可以通过它放心地投递给PHP-FPM来完成。
  • 或是你有一个很老的PHP-FPM项目饱受性能困扰又因积重难返而无法快速重构,我们还是可以借助它来更平滑地将旧业务迁移到新的异步/协程服务器中。

如何代理WordPress?

协程FastCGI客户端最强大的是还支持一键代理功能,可以将其它HTTP请求对象转化为FastCGI请求(目前只支持了Swoole\Http,后续可能加入PSR支持),也可以将FastCGI响应转化为HTTP响应,基于这个特性,我们可以做到代理如WordPress等的PHP-FPM服务。

需要注意的是:此用法无生产意义,生产中 proxy 可用于代理部分老 API 接口的 HTTP 请求到旧的 FPM 服务上 (而不是代理整站)

<?php

declare(strict_types=1);

use Swoole\Constant;
use Swoole\Coroutine\FastCGI\Proxy;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;

$documentRoot = '/path/to/wordpress'; // WordPress目录的绝对路径
$server = new Server('0.0.0.0', 80, SWOOLE_BASE);
$server->set(
    [
        Constant::OPTION_WORKER_NUM => swoole_cpu_num() * 2,
        Constant::OPTION_HTTP_PARSE_COOKIE => false,
        Constant::OPTION_HTTP_PARSE_POST => false,
        Constant::OPTION_DOCUMENT_ROOT => $documentRoot,
        Constant::OPTION_ENABLE_STATIC_HANDLER => true,
        Constant::OPTION_STATIC_HANDLER_LOCATIONS => ['/'],
    ]
);
$proxy = new Proxy('127.0.0.1:9000', $documentRoot);
$server->on(
    'request',
    function (Request $request, Response $response) use ($proxy) {
        $proxy->pass($request, $response);
    }
);
$server->start();

有些同学尝鲜的时候遇到了问题,如不知道为什么Proxy的第一个参数填什么,就随便填了一个ip端口,结果报错了

PHP Fatal error:  Uncaught Swoole\Coroutine\FastCGI\Client\Exception: Connection refused in @swoole-src/library/core/Coroutine/FastCGI/Client.php:175
Stack trace:
#0 @swoole-src/library/core/Coroutine/FastCGI/Client.php(70): Swoole\Coroutine\FastCGI\Client->ioException()
#1 @swoole-src/library/core/Coroutine/FastCGI/Proxy.php(183): Swoole\Coroutine\FastCGI\Client->execute(Object(Swoole\FastCGI\HttpRequest), -1)
#2 /home/wwwroot/test.php(23): Swoole\Coroutine\FastCGI\Proxy->pass(NULL, Object(Swoole\Http\Response))
#3 {main}
  thrown in @swoole-src/library/core/Coroutine/FastCGI/Client.php on line 175

或者本地使用php -S起了一个HTTP服务器,如

php -S 127.0.0.1:9501

然后Proxy的参数就填了一个127.0.0.1:9501,结果可想而知,依旧报错

PHP Fatal error:  Uncaught Swoole\Coroutine\FastCGI\Client\Exception: Connection reset by peer in @swoole-src/library/core/Coroutine/FastCGI/Client.php:175
Stack trace:
#0 @swoole-src/library/core/Coroutine/FastCGI/Client.php(100): Swoole\Coroutine\FastCGI\Client->ioException(104)
#1 @swoole-src/library/core/Coroutine/FastCGI/Proxy.php(183): Swoole\Coroutine\FastCGI\Client->execute(Object(Swoole\FastCGI\HttpRequest), -1)
#2 /home/wwwroot/test.php(23): Swoole\Coroutine\FastCGI\Proxy->pass(NULL, Object(Swoole\Http\Response))
#3 {main}
  thrown in @swoole-src/library/core/Coroutine/FastCGI/Client.php on line 175

这些写法都是错误的,正确的写法是与Nginx和php-fpm通信写法一致,可以填写为ip:port或者使用unix socket,即Nginx配置中的fastcgi_pass

不知道怎么写的话就去你的Nginx安装目录,执行

grep -R "fastcgi_pass"

就会得到类似以下的信息:

example/magento2-example.conf: fastcgi_pass   unix:/tmp/php-cgi.sock;
example/magento2-example.conf: fastcgi_pass   unix:/tmp/php-cgi.sock;
example/magento2-example.conf: fastcgi_pass   unix:/tmp/php-cgi.sock;
example/owncloud-example.conf:  fastcgi_pass unix:/tmp/php-cgi.sock;
example/nextcloud-example.conf: fastcgi_pass unix:/tmp/php-cgi.sock;
enable-php-pathinfo.conf: fastcgi_pass  unix:/tmp/php-cgi.sock;
enable-php.conf: fastcgi_pass  unix:/tmp/php-cgi.sock;
nginx.conf.default: #    fastcgi_pass   127.0.0.1:9000;

我这里的fastcgi_pass使用的是unix:/tmp/php-cgi.sock。端口没有监听,所以如果我使用127.0.0.1:9000的话,会报错Connection refused

80端口一般Nginx会使用,如果你之前使用的Nginx跑WordPress,可能会提示端口占用无法启动,所以需要关闭Nginx服务或者修改我们刚才代码中的80端口为其他端口

如果你选择修改端口,那么这样的话,你的WordPress的站点设置->常规中的WordPress地址(URL)站点地址(URL)中是没有端口的,默认依旧是80端口

当你启动服务之后访问会发现发生了301重定向

$ curl -i 127.0.0.1:801

HTTP/1.1 301 Moved Permanently
X-Powered-By: PHP/7.3.11
Content-Type: text/html; charset=UTF-8
X-Redirect-By: WordPress
Location: http://127.0.0.1/
Server: swoole-http-server
Connection: keep-alive
Date: Mon, 27 Apr 2020 06:45:19 GMT
Content-Length: 0

这个时候你就需要修改WordPress的站点设置->常规中的WordPress地址(URL)站点地址(URL)加上对应的端口即可。

最后,如果报错:

PHP Fatal error:  Uncaught Error: Class 'Swoole\Coroutine\FastCGI\Proxy' not found

请升级你的Swoole扩展到v4.5.0的版本~ 不要忘了点个star哦

https://github.com/swoole/library

https://github.com/swoole/swoole-src

1 条评论

发表评论

*