2022年04月22日10:52:41更新

本篇内容主要讲述了最新的think-swoole扩展的使用。

本指南的目的不是为了让你掌握Swoole开发,而且帮助你使用think-swoole快速部署ThinkPHP5.1应用到SwooleHttpServer,以及使用快速启动Swoole服务,如果你需要了解Swoole的具体用法和原理,请参考Swoole官方文档,说的比较详细了。

本文的内容并不适用于ThinkPHP 5.0及以下版本(5.0或者5.1.18之前版本的Swoole的支持可以参考这里) !

安装Swoole

首先按照Swoole官网说明安装swoole扩展,推荐新手可以直接使用

sudo pecl install swoole

会安装最新的稳定版(截至本文发布最新版本是4.0.3版本),如果你需要安装某个版本,例如,如果你不需要使用协程功能,只需要安装1.*版本,可以使用:

sudo pecl install swoole-1.10.5

可以在这里查看所有的swoole版本。

安装完成后,你可能需要在你的php.ini中添加:

extension=swoole

最后,请确认你的php的swoole模块已经支持。

php -m

如果你能够看到swoole在列表中,说明swoole模块已经正常安装了。

如果安装过程中遇到问题,根据提示进行操作即可,或者自行百度,这里不再赘述。

安装think-swoole

接下来第二步是安装think-swoole扩展,本文中的内容以最新版本的扩展为例(可能部分功能老版本的扩展不支持),如果你的扩展版本较旧,请更新框架或者扩展版本。

think-swoole是ThinkPHP官方发布的swoole扩展,从2.0+版本完善了对Swoole的支持。

ThinkPHP5+的扩展都是基于Composer安装的,所以确认你已经安装了Composer

如果你已经有自己的ThinkPHP5.1项目了,为了使用最新的特性,建议更新到最新版本V5.1.20+),然后可以在应用根目录下使用下面命令安装扩展。

composer require topthink/think-swoole=2.0.*

会安装最新的稳定版本的think-swoole扩展。

如果你是第一次使用ThinkPHP5.1,那么可以先创建一个初始项目,然后再安装扩展,依次执行下面的命令即可。

composer create-project topthink/think tp
cd tp
composer require topthink/think-swoole

 

如果你的composer不是最新版本的,请执行命令更新:composer self-update

如果你的composer镜像不是国内的,请执行命令切换阿里composer镜像:

所有项目都会使用该镜像地址:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

取消配置:

composer config -g --unset repos.packagist

项目配置
仅修改当前工程配置,仅当前工程可使用该镜像地址:

composer config repo.packagist composer https://mirrors.aliyun.com/composer/


取消配置:

composer config --unset repos.packagist

调试
composer 命令增加 -vvv 可输出详细的信息,命令如下:

composer -vvv require alibabacloud/sdk

 

启动Swoole HTTP服务

第一个场景(也是该扩展最重要的一个场景),毕竟大部分使用think-swoole扩展的用户都是在使用ThinkPHP开发网站或者项目,使用think-swoole扩展可以让你的产品直接部署到Swoole上,并且享受下面的优势:

  • 无需对代码进行改造就能带来性能的数倍提升;
  • 可以在Apache/Nginx等传统WEB服务器和Swoole之间切换部署;

简单点说,就是你可以在传统模式下开发你的应用,然后直接部署到Swoole上运行,但无需针对Swoole写任何的处理代码。

安装完扩展后,你什么都不需要做,最简单的就是直接在命令行(应用根目录下面)下执行:

php think swoole

启动成功后会显示

Starting swoole http server...
Swoole http server started: <http://0.0.0.0:9501>
You can exit with `CTRL-C`

可以看到已经在0.0.0.0:9501启动一个HTTP Server服务端,下面我们可以直接访问当前的应用。

http://localhost:9501

如果你之前已经有运行一个80端口的WEB服务,那么可以比较下两个页面的区别。

如果你是刚创建的项目,那么可以直接看到ThinkPHP5.1的欢迎页面。

否则你会看到你的项目首页。

配置文件

HTTPServer的参数可以在应用配置目录下的swoole.php里面配置,该文件会在扩展安装的时候自动生成(如果没有则可以自己创建)。

扩展自带的配置参数主要包括:

配置参数 描述 默认值
host 监听地址 0.0.0.0
port 监听端口 9501
mode 运行模式 SWOOLE_PROCESS
sock_type Socket type SWOOLE_SOCK_TCP
app_path 应用目录(守护进程模式必须设置) 自动识别
ssl 是否启用https false
file_monitor 是否监控文件更改(V2.0.9+) false
file_monitor_interval 监控文件间隔(秒)(V2.0.9+) 2
file_monitor_path 监控目录 (V2.0.9+) 默认监控application和config目录

其它的swoole参数可以参考官方文档的配置参数,所有swoole本身支持的配置参数都可以直接在swoole.php中使用。

守护进程模式

如果需要使用守护进程模式运行,可以使用

php think swoole -d

或者在swoole.php文件中设置

'daemonize'	=>	true

不过一定要记得,如果启用守护进程模式,必须设置应用目录app_path(使用绝对路径),否则会出错。

'host'     	=> '0.0.0.0', // 监听地址
'port'     	=> 9501, // 监听端口
'daemonize'	=>	true,
'app_path' 	=> '/home/www/tp/application/',

基本操作

如果要停止服务,可以使用

php think swoole stop

reload服务

php think swoole reload

stop服务

php think swoole stop

restart服务

php think swoole restart

restartreload的区别是,restart会先stop然后start,而reload则是平滑重启服务,不会中断服务。

如果你需要修改地址和端口,可以修改swoole.php配置文件

'host'     => 'tp5.com', // 监听地址
'port'     => 8080, // 监听端口

改完后,需要重启服务才能生效

php think swoole restart

现在可以直接访问

http://tp5.com:8080

如果你需要设置80端口,需要root权限才可以。

如果你安装的是2.0.12+版本的扩展,还可以支持在命令行指定地址和端口,例如:

php think swoole -H tp.com -p 9508

如果启动了多个不同端口的服务,reloadrestartstop操作必须也是针对某个端口的才能正确操作,我们以reload操作为例进行说明。

如果我们需要reload前面启动的tp.com:9508服务,下面的指令是错误的

php think swoole reload

可能会出现错误提示:

no swoole http server process running.

必须带上正确的端口号(host不是必须的)

php think swoole reload  -p 9508

然后,你会看到提示信息如下,表示reload成功:

Reloading swoole http server...
> success

CookieSession

由于Swoole的特殊性,扩展本身接管了Cookie类和Session类的处理,因此不要调用(包括依赖注入)think\Cookiethink\Session类,而应该改为think\swoole\Cookie类和think\swoole\Session类。但think\facade\Cookiethink\facade\Session类的用法是正常的,因此原来的静态方法调用依然可以正常使用。同时,Request对象的cookie方法和session方法也可以正常使用。

关于SwooleSession的用法,这里要特别强调下。

Swoole是没有Session的概念,因此所有PHP内置的session函数都是无效的,think-swoole扩展单独封装了一个Session类,和系统的Session机制无关。

该扩展提供的think\swoole\Session类是基于swoole_tableThinkPHP缓存的混合解决方案。

每次Session::start()的时候系统会从swoole_table或者定义的缓存类型中获取当前用户的Session数据,而session_id数据则通过Cookie获取。并且在当前worker进程中不会过期,但每次从swoole_table或者缓存中获取session的时候则会判断是否过期,session的有效期还是通过session.php配置文件的expire配置参数进行设置。

swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。

由于swoole_table需要事先分配内存大小和字段定义,在swoole.php配置文件中需要添加定义:

'table'	=>	[
	// 定义最大记录数
	'size'	=>	1024,
    // 字段类型定义(目前仅支持 string int 和 float类型)
    'column'	=>[
    	'data'	=>	['string',255], // 字符串类型 长度为255个字节
        'expire'=> ['int',8], // 整型 长度为8
    ],
],

swoole_table分配的内存无法动态扩容和调整字段类型,如果修改则需要重启才能生效。

然后在session.php配置文件中,添加

'use_swoole_table'	=>	true,

swoole_table是一个可选方案。我们更建议使用缓存机制来处理Session,如果你的应用比较大,则应该配置使用redis之类的缓存机制更加适合,直接在你的cache.php中定义相关缓存配置即可。

为了避免复杂,swoole的Session类不再支持prefix参数,如果需要区分比如前后台不同session的需求,可以使用name参数进行区分。

文件监控

由于Swoole服务运行过程中PHP文件是常驻内存运行的,这样可以避免重复读取磁盘、重复解释编译PHP,以便达到最高性能。所以更改业务代码后必须手动reload或者restart才能生效。

think-swoole扩展提供了监控文件更新的功能,在检测到相关目录的文件有更新后会自动reload,从而不需要手动进行reload操作,方便开发调试。

如果你的应用开启了调试模式,文件监控功能是自动开启的。原则上,在部署模式下不建议开启文件监控,一方面有性能损耗,另外一方面对文件所做的任何修改都需要确认无误才能进行更新部署。如果你确实需要在部署模式下开启文件监控,可以设置如下:

'file_monitor'	=>	true, // 开启文件监控
'file_monitor_interval'	=>	1, // 文件监控检测的时间间隔
'file_monitor_path'	=>	'', // 文件监控目录 一般不需要设置 默认会监控应用目录和配置目录

事件回调

扩展自带的HTTPServer包含了onWorkerStartonRequest两个事件回调,你如果需要增加其它的回调事件处理,可以在配置文件中直接添加:

'WorkerStop'	=>	function($server, $worker_id) {

},
'WorkerError'	=>	function($serv, $worker_id, $worker_pid, $exit_code, $signal) {

},

关于事件回调的具体用法,可以参考swoole官方文档。

如果不熟悉内部机制,请勿随意替换和更改onWorkerStartonRequest事件回调,会导致不可预期的结果。

Nginx+Swoole部署

可以使用Nginx请求转发到Swoole的方式部署,充分发挥Nginx的配置优势和强大功能。

server {
	listen 80;
    root /var/www/tp/public/;
    server_name 127.0.0.1;
	index index.html index.htm index.php;
    
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "keep-alive";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://127.0.0.1:9501;
    }
}

静态资源访问

如果你没有使用Nginx代理的话,为了确保静态资源的正常访问,请确认下面的参数配置正确:

// 网站根目录位置
'document_root'         => Env::get('root_path') . 'public',
// 开启静态资源处理
'enable_static_handler' => true,

使用Chrome浏览器会自动请求一次favicon.ico,所以确保你的网站根目录下面有存在favicon.ico文件,否则会产生一次404请求的错误日志。

HTTPSHTTP2支持

如果需要配置你的HTTP服务支持HTTPS,需要增加配置如下:

'ssl'			=>	true,
'ssl_cert_file' => __DIR__.'/ssl.crt',
'ssl_key_file' 	=> __DIR__.'/ssl.key',

记得准确指定你的cert证书和key私钥的路径。

使用SSL必须在编译swoole时加入--enable-openssl选项

如果需要支持HTTP2协议,则在SSL支持的基础上还需要在编译的时候加入--enable-http2选项

./configure --enable-openssl --enable-http2

然后在swoole.php中增加配置

'open_http2_protocol'	=>	true

其它注意事项

为了让你的应用能够顺利的运行在Swoole上面,扩展做了大量的底层处理工作,包括让你的请求数据、CookieSession正常运作。

Swoole下面,不能使用$_GET$_POST$_REQUEST$_SERVER$_COOKIE以及$_SESSION等原生的PHP用法,只能使用框架提供的类和方法进行获取。

错误的用法:

$name = $_GET['name'];
$name = $_POST['name'];
$name = $_COOKIE['name'];
$name = $_SESSION['name'];
$host = $_SERVER['HTTP_HOST'];

V2.0.11+版本开始,系统可以支持原生全局变量的获取,但仍然不建议使用。

正确的用法(以下用法都采用了Facade静态代理类):

$name = Request::get('name');
$name = Request::param('name');
$name = Cookie::get('name');
$name = Session::get('name');
$host = Request::server('http_host');

如果你要获取php://input内容,必须把原来的代码

file_get_contents('php://input');

改成

Request::getInput();

不过更建议使用

Request::put();

因为可以支持json数据的自动解析而不需要手动进行json_decode

由于onWorkerStart运行的时候还没有HTTP_HOST,因此最好在应用配置文件config/app.php中设置app_host

请不要调用PHP原生的header方法,使用Response对象的header方法替代。
不要使用PHP原生的session相关函数,使用Session类的相关方法。

目前为止,尚有一些功能不够完善(例如文件上传之类),请期待后续版本更新。

快速启动Swoole Server

现在来看第二个场景,通过简单的配置快速启动一个swoole服务,包括WebSocket/Http/Socket服务。

可以支持直接启动一个Swoole server(需要think-swoole扩展 2.0.9+版本)

php think swoole:server

会显示如下信息:

Starting swoole server...
Swoole socket server started: <0.0.0.0:9508>
You can exit with `CTRL-C`

这个时候已经在0.0.0.0:9508启动一个Websocket服务。

你可以在浏览器中访问

http://127.0.0.1:9508

会看到类似下面的页面(后面是一串随机数)。

守护进程

如果需要使用守护进程方式运行,可以使用

php think swoole:server -d

或者在swoole.php文件中设置

'daemonize'	=>	true

配置文件

如果需要自定义参数,可以在config/swoole_server.php中进行配置,包括:

配置参数 描述 默认值
type 服务类型(支持socket、http或者留空) socket
host 监听地址 0.0.0.0
port 监听端口 9508
mode 运行模式 SWOOLE_PROCESS
sock_type Socket type SWOOLE_SOCK_TCP
daemonize 守护进程 false

注意不要和swoole.php文件文件混淆,两者的作用完全不同。

并且支持swoole所有的参数,以及支持使用闭包方式定义相关事件回调。

return [
    // 扩展自身配置
    'host'         => '0.0.0.0', // 监听地址
    'port'         => 9501, // 监听端口
    'type'         => 'socket', // 服务类型 支持 socket http或者留空
    'mode'         => SWOOLE_PROCESS,
    'sock_type'    => SWOOLE_SOCK_TCP,

    // 可以支持swoole的所有配置参数
    'daemonize'    => false,
    'worker_num'   => 4,

    // 事件回调定义
    'onOpen'       => function ($server, $request) {
        echo "server: handshake success with fd{$request->fd}\n";
    },

    'onMessage'    => function ($server, $frame) {
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
        $server->push($frame->fd, "this is server");
    },

    'onRequest'    => function ($request, $response) {
        $response->end("<h1>Hello Swoole. #" . rand(1000, 9999) . "</h1>");
    },

    'onClose'      => function ($ser, $fd) {
        echo "client {$fd} closed\n";
    },
];

自定义服务类

如果你需要更高级的自定义事件回调,也可以使用自定义的Swoole服务类。

<?php
namespace app\http;

use think\swoole\Server;

class Swoole extends Server
{
	protected $host = '127.0.0.1';
	protected $port = 9502;
    protected $serverType = 'socket';
    protected $mode = SWOOLE_PROCESS;
    protected $sockType = SWOOLE_SOCK_TCP;
	protected $option = [ 
		'worker_num'=> 4,
		'daemonize'	=> true,
		'backlog'	=> 128
	];

	public function onReceive($server, $fd, $from_id, $data)
	{
		$server->send($fd, 'Swoole: '.$data);
	}
}

自定义服务类必须继承think\swoole\Server类,支持swoole所有的回调方法定义(回调方法必须是public类型)。

serverType 属性定义为 socket或者http 则支持swoole的swoole_websocket_serverswoole_http_server

然后在swoole_server.php中增加配置参数:

return [
	'swoole_class'	=>	'app\http\Swoole',
];

定义该参数后,其它配置参数均不再有效。

然后就可以在命令行启动服务端

php think swoole:server

一样可以支持使用守护进程模式运行,

php think swoole:server -d

同样也支持reloadrestartstop 操作。

php think swoole:server reload

客户端代码的实现有很多,如果你是使用PHP的话,可以用Swoole\Client类。

<?php
namespace app\index\controller;

use Swoole\Client;
use think\Controller;

class Test extends Controller
{
  public function index() {
      $client = new Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
      $ret = $client->connect("127.0.0.1", 9501);
      
      if(empty($ret)){
          echo 'error!connect to swoole_server failed';
      } else {
          $client->send('test');
      }
  }

}

启动多个swoole服务

你可以通过命令行的指令启动多个不同端口的swoole服务,例如:

php think swoole:server -p 9800
php think swoole:server -p 9700

如果要分别对不同端口的服务进行stop操作,务必使用

php think swoole:server stop -p 9800
php think swoole:server stop -p 9700