基於 swoole 下 非同步佇列 API

Latent發表於2020-04-26

說明

1.在 Server 程式中如果需要執行很耗時的操作,比如一個聊天伺服器傳送廣播,Web 伺服器中傳送郵件。如果直接去執行這些函式就會阻塞當前程式,導致伺服器響應變慢。
Swoole 提供了非同步任務處理的功能,可以投遞一個非同步任務到 TaskWorker 程式池中執行,不影響當前請求的處理速度。(官網說明)

服務端
<?php
/**
 * Created by PhpStorm
 * User: pl
 * Date: 2020/4/26
 * Time: 10:29
 */


class TaskServers
{
    private $server;

    public function __construct()
    {

        $this->server = new Swoole\Server('127.0.0.1',9501);

        $this->server->set([
            'task_worker_num' => 3,     //開啟的程式數 一般為cup核數 1-4倍
            'daemonize'       => 1,     //已守護程式執行該程式
            'max_request'     => 10000,  //worker程式最大任務數
            'dispatch_mode' => 2,
            'task_ipc_mode'   => 3,     //設定為爭搶模式
        ]);

        $this->server->on('Receive',array($this,'onReceive'));
        $this->server->on('Task',array($this,'onTask'));
        $this->server->on('Finish',array($this,'onFinish'));
        $this->server->start();

    }

    /**
     * @param swoole_server $server
     * @param $fd
     * @param $form_id
     * @param $data
     *  開始投遞非同步任務
     */
    public function onReceive(swoole_server $server , $fd , $form_id , $data)
    {

        $this->server->task($data);
    }

    /**
     * @param swoole_server $server
     * @param $fd
     * @param $from_id
     * @param $data
     *  執行非同步任務
     */
    public function onTask( $server , $fd , $from_id , $data)
    {
        $data = json_decode($data,true);
        try {
            $log_txt =  date('Y-m-d H:i:s')."開始執行任務".PHP_EOL ;
            $this->log($log_txt);
            return  $this->request_curl($data['url'],$data['data'],'post');
        }catch (\Exception $exception){

            $log_txt =  date('Y-m-d H:i:s')."執行任務失敗發生錯誤".PHP_EOL ;
            $this->log($log_txt);
        }
    }

    public function onFinish( $server , $task_id, $data)
    {

        $log_txt =  date('Y-m-d H:i:s')."$data".PHP_EOL ;
        $this->log($log_txt);


    }

   public function request_curl($url = '', $request_data = '', $request_type = 'get', $headers = [], $is_ssl = false)
    {


            $ch = curl_init (); //curl初始化


            if( $request_type == 'get' && !empty( $request_data) )
            {
                $num = 0;
                foreach ( $request_data as $key => $value )
                {
                    if($num == 0)
                    {
                        $url .= '?' . $key.'='.$value;
                    }
                    else
                    {
                        $url .= '&'. $key . '=' . $value;
                    }

                    $num ++;
                }

                $num = 0;
            }
            //區分get和post
            curl_setopt ( $ch, CURLOPT_URL, $url ); //URL地址
            curl_setopt ( $ch, CURLOPT_HEADER, 0 ); //頭資訊不輸出
            curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );  //如果成功只將結果返回,不自動輸出任何內容

            //post型別就實現此結果
            if( $request_type == 'post')
            {
                curl_setopt ( $ch, CURLOPT_POST, 1 ); //設定為POST方式
                curl_setopt ( $ch, CURLOPT_POSTFIELDS, $request_data );  //POST資料
                curl_setopt ( $ch, CURLOPT_HTTPHEADER, array("Expect:")); //當post資料大於1024時強制執行
            }

            //判斷是否繞過證照
            if( $is_ssl )
            {
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);//繞過ssl驗證
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            }

            if(!empty($headers))
            {
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            }

            $result = curl_exec ( $ch ); //執行

            if ( $result == FALSE)
            {
                $result = api_format( '404', '請求失敗' , '', true);
            }

            curl_close ( $ch ); //關閉資源


        return $result;
    }

    public function log($log_txt)
    {
        $log ='log/'.date('Y_m_d').'log';
        if(!file_exists($log)) {
            touch($log);
            chown($log,0777);
        }

        $file_log = fopen($log,"a");

        fputs($file_log,$log_txt);
        fclose($file_log);
    }

}
$task = new TaskServers();

客戶端

<?php
/**
 * Created by PhpStorm
 * User: pl
 * Date: 2020/4/26
 * Time: 11:54
 */
class ClientRequest
{
    private $client;
    private $params; //請求引數

    public function __construct($params)
    {
        $this->client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_KEEP);
        $this->params = $params;
    }

    public function connect()
    {
        if (!$this->client->connect('127.0.0.1', 9501, 1)) {
            return json_encode([
                'code' => 500,
                'err_msg' => '連結非同步客戶端失敗'
            ]);
        }

        /**
         * 注意請求格式
         * $params['url'] 介面地址
         * $params['type']介面請求方式
         * $params['data']引數
         */
        $params = $this->params;
        $array['url'] = $params['url'];
        unset($params['url']);
        $array['data'] = $params;
        $this->client->send(json_encode($array, JSON_UNESCAPED_UNICODE));
    }


}

if (!empty($_GET)) {
    $params = $_GET;
}

if (!empty($_POST)) {
    $params = $_POST;
}


$client = new ClientRequest($params);
$client->connect();

我們以介面形式上去呼叫另外一個耗時介面.簡單對比一下響應速度。

基於 swoole 下 非同步佇列 API

基於 swoole 下 非同步佇列 API

基於 swoole下 非同步佇列API

最後補充:基於swoole 一個簡單的非同步佇列就完成了。可以將此佇列封裝成api佇列介面,將它丟到task裡面去慢慢執行吧~哈哈

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章