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