> 環境說明: 系統:Ubuntu14.04 (安裝教程包括CentOS6.5)
> PHP版本:PHP-5.5.10
> swoole版本:1.7.6-stable
上一章已經簡單介紹了如何寫一個簡單的Echo服務器,并了解了onReceive等幾個核心回調函數的使用方法。這一章,我將介紹如何使用Swoole的異步任務Task。
## **1.Task簡介**
Swoole的業務邏輯部分是同步阻塞運行的,如果遇到一些耗時較大的操作,例如訪問數據庫、廣播消息等,就會影響服務器的響應速度。因此Swoole提供了Task功能,將這些耗時操作放到另外的進程去處理,當前進程繼續執行后面的邏輯。
## **2.開啟Task功能**
開啟Task功能只需要在swoole_server的配置項中添加[task_worker_num](https://github.com/LinkedDestiny/swoole-doc/blob/master/doc/01.%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9.md#6task_worker_num)一項即可,如下:
~~~
$serv->set(array(
'task_worker_num' => 8
));
~~~
即可開啟task功能。此外,必須給swoole_server綁定兩個回調函數:[onTask](https://github.com/LinkedDestiny/swoole-doc/blob/master/doc/02.%E4%BA%8B%E4%BB%B6%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0.md#6ontask)和[onFinish](https://github.com/LinkedDestiny/swoole-doc/blob/master/doc/02.%E4%BA%8B%E4%BB%B6%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0.md#7onfinish)。這兩個回調函數分別用于執行Task任務和處理Task任務的返回結果。
## **3.使用Task**
首先是發起一個Task,代碼如下:
~~~
public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
echo "Get Message From Client {$fd}:{$data}\n";
// send a task to task worker.
$param = array(
'fd' => $fd
);
// start a task
$serv->task( json_encode( $param ) );
echo "Continue Handle Worker\n";
}
~~~
可以看到,發起一個任務時,只需通過swoole_server對象調用task函數即可發起一個任務。swoole內部會將這個請求投遞給task_worker,而當前Worker進程會繼續執行。
當一個任務發起后,task_worker進程會響應[onTask](https://github.com/LinkedDestiny/swoole-doc/blob/master/doc/02.%E4%BA%8B%E4%BB%B6%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0.md#6ontask)回調函數,如下:
~~~
public function onTask($serv,$task_id,$from_id, $data) {
echo "This Task {$task_id} from Worker {$from_id}\n";
echo "Data: {$data}\n";
for($i = 0 ; $i < 10 ; $i ++ ) {
sleep(1);
echo "Task {$task_id} Handle {$i} times...\n";
}
$fd = json_decode( $data , true )['fd'];
$serv->send( $fd , "Data in Task {$task_id}");
return "Task {$task_id}'s result";
}
~~~
這里我用sleep函數和循環模擬了一個長耗時任務。在onTask回調中,我們通過task_id和from_id(也就是worker_id)來區分不同進程投遞的不同task。當一個task執行結束后,通過return一個字符串將執行結果返回給Worker進程。Worker進程將通過[onFinish](https://github.com/LinkedDestiny/swoole-doc/blob/master/doc/02.%E4%BA%8B%E4%BB%B6%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0.md#7onfinish)回調函數接收這個處理結果。
下面來看onFinish回調:
~~~
public function onFinish($serv,$task_id, $data) {
echo "Task {$task_id} finish\n";
echo "Result: {$data}\n";
}
~~~
在[onFinish](https://github.com/LinkedDestiny/swoole-doc/blob/master/doc/02.%E4%BA%8B%E4%BB%B6%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0.md#7onfinish)回調中,會接收到Task任務的處理結果$data。在這里處理這個返回結果即可。 (**Tip:**?可以通過在傳遞的data中存放fd、buff等數據,來延續投遞Task之前的工作)
[點此查看完整示例](https://github.com/LinkedDestiny/swoole-doc/blob/master/src/02/swoole_task_server.php)
## **4.swoole_client**
之所以在這里講解如何使用swoole_client是因為,在寫服務端代碼的時候,不可避免的需要用到客戶端來進行測試。swoole提供了swoole_client用于編寫測試客戶端,下面我將講解如何使用這個工具。
swoole_client有兩種工作模式:同步阻塞模式和異步回調模式。其中,同步阻塞模式在上一章中已經給出示例,其使用和一般的socket基本無異。因此,我將重點講解swoole_client的異步回調模式。
創建一個異步client的代碼如下:
~~~
$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
~~~
其中,**SWOOLE_SOCK_ASYNC**選項即表明創建一個異步client。
既然是異步,那當然需要回調函數。swoole_client一共有四個回調函數,如下:
~~~
$client->on("connect", function($cli) {
$cli->send("hello world\n");
});
$client->on("receive", function($cli, $data){
echo "Received: ".$data."\n";
});
$client->on("error", function($cli){
echo "Connect failed\n";
});
$client->on("close", function($cli){
echo "Connection close\n";
});
~~~
這幾個回調函數的作用基本和swoole_server類似,只有參數不同,因此不再贅述。?[點此查看完整示例](https://github.com/LinkedDestiny/swoole-doc/blob/master/src/02/swoole_async_client.php)
## **進階:簡易聊天室**
我用swoole擴展寫了一個簡單的聊天室Demo([點此查看](https://github.com/LinkedDestiny/swoole-doc/tree/master/src/Chatroom)) 這個Demo雖然用到了一些其他的架構,但是核心功能仍然是依托swoole擴展實現的。
* 通過onReceive回調接收數據,根據預先規定的字段找到對應的處理函數。
* 通過onTask處理發送數據、廣播這樣的耗時內容。
[Server.php](https://github.com/LinkedDestiny/swoole-doc/blob/master/src/Chatroom/Server/app/socket/Server.php)是全部的Swoole回調函數實現的類。