php swoole ,swoole task

本文介绍Swoole的任务处理机制,包括如何配置并启动Swoole服务来处理耗时任务,通过Task功能将这些任务分配给独立的工作进程处理,以提高服务器响应速度。

前提安装好swoole后,php添加好swoole扩展

deepin 安装请参考 博客其他文章

 

swoole server task 类直接上代码

服务端代码(server.php)

 

/*
 swoole Task运行实例
 Task简介
 Swoole的业务逻辑部分是同步阻塞运行的,如果遇到一些耗时较大的操作,例如访问数据库、广播消息等,就会影响服务器的响应速度。因此Swoole提供了Task功能,将这些耗时操作放到另外的进程去处理,当前进程继续执行后面的逻辑.
 运行Task,需要在swoole服务中配置参数 task_worker_num,即可开启task功能。此外,必须给swoole_server绑定两个回调函数:onTask和onFinish。这两个回调函数分别用于执行Task任务和处理Task任务的返回结果。
*/

class server
{
    private $serv;

    /**
     * [__construct description]
     * 构造方法中,初始化 $serv 服务
     */
    public function __construct() {
//服务地址,以后最好做成配置
        $this->serv = new swoole_server('0.0.0.0', 9501);
        //初始化swoole服务
        $this->serv->set(array(
            'worker_num'  => 8,
            'daemonize'   => false, //是否作为守护进程,此配置一般配合log_file使用
            'max_request' => 1000,
            'log_file'    => './swoole.log',
            'task_worker_num' => 8
        ));

        //设置监听
        $this->serv->on('Start', array($this, 'onStart'));
        $this->serv->on('Connect', array($this, 'onConnect'));
        $this->serv->on("Receive", array($this, 'onReceive'));
        $this->serv->on("Close", array($this, 'onClose'));
        $this->serv->on("Task", array($this, 'onTask'));
        $this->serv->on("Finish", array($this, 'onFinish'));

        //开启
        $this->serv->start();
    }

    public function onStart($serv) {
        echo SWOOLE_VERSION . " onStart\n";
    }

    public function onConnect($serv, $fd) {
        echo $fd."Client Connect.\n";
    }

    public function onReceive($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));
//如果不调用task
//直接返回
//调用处理代码,的到结果$result,然后返回
// $serv->send($fd['fd'] ,$result);


        echo "Continue Handle Worker\n";
    }

    public function onClose($serv, $fd) {
        echo "Client Close.\n";
    }

    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 < 2 ; $i ++ ) {
            sleep(1);
            echo "Task {$task_id} Handle {$i} times...\n";
        }
        $fd = json_decode($data, true);
        $serv->send($fd['fd'] , "Data in Task {$task_id}");
        return "Task {$task_id}'s result";
    }

    public function onFinish($serv,$task_id, $data) {
        echo "Task {$task_id} finish\n";
        echo "Result: {$data}\n";
    }
}

调用php 命令运行某个start_server.php.里面代码

start_server.php

$server new server();

 

客户端代码(client.php)

 

class Client
{
    private $client;

    public function __construct() {
        $this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
        $this->client->on('Connect', array($this, 'onConnect'));
        $this->client->on('Receive', array($this, 'onReceive'));
        $this->client->on('Close', array($this, 'onClose'));
        $this->client->on('Error', array($this, 'onError'));
    }

    public function connect() {
        if(!$fp = $this->client->connect("127.0.0.1", 9501 , 1)) {
            echo "Error: {$fp->errMsg}[{$fp->errCode}]\n";
            return;
        }
    }

    //connect之后,会调用onConnect方法
    public function onConnect($cli) {
        fwrite(STDOUT, "Enter Msg:");
        swoole_event_add(STDIN,function(){
            fwrite(STDOUT, "Enter Msg:");
            $msg = trim(fgets(STDIN));
            $this->send($msg);
        });
    }

    public function onClose($cli) {
        echo "Client close connection\n";
    }

    public function onError() {

    }

    public function onReceive($cli, $data) {
        echo "Received: ".$data."\n";
    }

    public function send($data) {
        $this->client->send($data);
    }

    public function isConnected($cli) {
        return $this->client->isConnected();
    }

}

同步调用:

$client = new Client();
        $client->connect();
        if ($result = $client->send($data)) {
//默认请求,请求任务是,直接发送请求,无交互数据(从server获取到返回值)
//            print_r($client->receive());
            echo 'success';
        } else {
            echo 'fail';
        }
        //获取又返回的swoole,请求
//        if($data = $client->recv()){
//            echo  "获取到的返回值".$data;
//        }
        $client->close();

异步调用

$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
//设置事件回调函数
        $client->on("connect", function($cli) {
            global $params;
            //$params = array();
            //$params['service'] = 'Default.Index';
            //$params['username'] = 'swoole';

            $data = json_encode($params);

            echo "Send: " . $data . "\n";;

            $cli->send($data);
        });
        $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";
        });
//发起网络连接
        $client->connect($ip, $port, 3);
  •  
  • 其他写法

配置文件信息

/**
     * Swoole扩展类库
     */
    'Swoole' => array(
    //服务
    'server' => array(
        'ip' => '127.0.0.1',
        'port' => 9501,
        'worker_num' => 1,
    ),
    //计划任务
    'task' => array(
        'ip' => '127.0.0.1',
        'port' => 9502,
        'worker_num' => 1,
    ),
),

分开swolle普通请求和,task模式

类源码:

class SwooleLite
{

    /**
     * 任务列表
     * @var
     */
    protected $task_list;

    /**
     * 配置信息
     * @var mixed
     */
    protected $config;

    /**
     *
     * 开启调试模式
     * @var bool
     */
    protected $debug;

    /**
     * debug 暂时没用debug 后期可扩展为,开启记录日志
     * Lite constructor.
     * @param bool $debug
     */
    public function __construct($debug = FALSE)
    {
        $this->debug = $debug;
        $this->config = \PhalApi\DI()->config->get('app.Swoole.server');
    }

    /**
     * 启动接口服务,支持长链接
     */
    public function runServer()
    {
        $oldErrorHandler = set_error_handler(array(__CLASS__, 'myErrorHandler'));

        $this->config = \PhalApi\DI()->config->get('app.Swoole.server');

        $ip = isset($this->config['ip']) ? $this->config['ip'] : '127.0.0.1';
        $port = isset($this->config['port']) ? $this->config['port'] : 9501;
        $workerNum = isset($this->config['worker_num']) ? $this->config['worker_num'] : 8;

        $serv = new \swoole_server($ip, $port);

        $serv->set(array(
            'worker_num' => $workerNum,     //工作进程数量
            'daemonize' => true,            //是否作为守护进程
        ));

        $serv->on('connect', function ($serv, $fd) {
//            \PhalApi\DI()->logger->debug('client connect in swoole');
            \PhalApi\DI()->logger->debug('client connect in swoole');
        });

        $serv->on('receive', function ($serv, $fd, $fromId, $data) {
            try {

                $params = json_decode($data, TRUE);
                if (!is_array($params)) {
                    $params = array();
                }
                \PhalApi\DI()->logger->debug('params in swoole', $params);

                $url = 's=App.Site.Index&username=dogstar';
                $result = \PhalApi\DI()->request->requestApi($url);
                \PhalApi\DI()->logger->debug('result in swoole', $result);
//                \PhalApi\DI()->response->setData($result['data']);
//                \PhalApi\DI()->response->getResult();
//                $res = \PhalApi\DI()->response->output();
//                $result = \PhalApi\DI()->response->output();
//                \PhalApi\DI()->logger->debug('output in swoole', $res);
                $serv->send($fd, json_encode($result));

            } catch (Exception $ex) {
                echo $ex->getTraceAsString();
                \PhalApi\DI()->logger->error('exception in swoole', $ex->getMessage());
                //TODO 通知管理员
            }
            if (\PhalApi\DI()->notorm) {
                \PhalApi\DI()->notorm->disconnect();
            }

            $serv->close($fd);
        });

        $serv->on('close', function ($serv, $fd) {
            \PhalApi\DI()->logger->debug('client close in swoole');
        });

        $serv->start();
    }

    /**
     * 启动计划任务,支持异步处理
     */
    public function runTask()
    {
        $oldErrorHandler = set_error_handler(array(__CLASS__, 'myErrorHandler'));

        $this->config = \PhalApi\DI()->config->get('app.Swoole.task');

        $ip = isset($this->config['ip']) ? $this->config['ip'] : '127.0.0.1';
        $port = isset($this->config['port']) ? $this->config['port'] : 9502;
        $workerNum = isset($this->config['worker_num']) ? $this->config['worker_num'] : 4;

        $serv = new \swoole_server($ip, $port);

        $serv->set(array('task_worker_num' => $workerNum));

        $serv->on('Receive', function ($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";
//            $taskId = $serv->task($data);
//            $this->task_list[$taskId] = $fd;
//            \PhalApi\DI()->logger->debug("asynctask($taskId) dispath in swoole", print_r($data));
//            echo "Continue Handle Worker\n";
        });

        $serv->on('Task', function ($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 "Taks {$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";
//
//            \PhalApi\DI()->logger->debug("asynctask($taskId) start in swoole", print_r($data));
//
//            $params = json_decode($data, TRUE);
//            if (!is_array($params)) {
//                $params = array();
//            }
//
//            try {
//
//                $params = json_decode($data, TRUE);
//                if (!is_array($params)) {
//                    $params = array();
//                }
//                \PhalApi\DI()->logger->debug('params in swoole', $params);
//
//                $url = 's=App.Site.Index&username=dogstar';
//                $result = \PhalApi\DI()->request->requestApi($url);
//                \PhalApi\DI()->logger->debug('result in swoole', $result);
//                $serv->finish(json_encode($result));
//
//            } catch (Exception $ex) {
//                echo $ex->getTraceAsString();
//                \PhalApi\DI()->logger->error("asynctask($taskId) exception in swoole", $ex->getMessage());
//                $serv->finish("Exception: " . $ex->getMessage());
//                //TODO 通知管理员
//            }
//
//            if (\PhalApi\DI()->notorm) {
//                \PhalApi\DI()->notorm->disconnect();
//            }
        });

        $serv->on('Finish', function ($serv, $task_id, $data) {
            echo "Task {$task_id} finish\n";
            echo "Result: {$data}\n";

            \PhalApi\DI()->logger->debug("asynctask($task_id) finish in swoole", $data);
            $serv->close($this->task_list[$task_id]);
        });

        $serv->start();
    }

    /**
     * error handler function
     */
    public static function myErrorHandler($errno, $errstr, $errfile, $errline)
    {
        if (!(error_reporting() & $errno)) {
            // This error code is not included in error_reporting
            return;
        }

        switch ($errno) {
            case E_USER_ERROR:
            case E_USER_WARNING:
            case E_USER_NOTICE:
            default:
                \PhalApi\DI()->logger->error('error in swoole',
                    array('errno' => $errno, 'errstr' => $errstr, 'errline' => $errline, 'errfile' => $errfile));
                break;
        }

        /* Don't execute PHP internal error handler */
        return true;
    }
}

 

(1)长链接入口

通过书写php 命令,调用run_server.php,文件内容为下面的

//$ vim ./Server/run_server.php
$swooleLite = new Swoole_Lite();
$swooleLite->runServer();
启动、重启和关闭服务

启动可以用:

php ./Server/run_server.php

关闭可以用:

ps -ef | grep run_server | grep -v grep | awk '{print $2}'|xargs kill -9

(2)异步计划任务

在使用长链接入口对外提供接口服务后,由于不再是HTTP协议,所以入口建议放置在新的目录./Server,而不再是./Public。
入口文件的编写,如同我们以往一样,很简单:

//$ vim ./Server/run_server.php

$swooleLite = new Swoole_Lite();
$swooleLite->runServer();

 

启动、重启和关闭服务

启动可以用:

nohup php ./Server/run_task.php > ./Server/run_task.log 2>&1 &

 

(3)客户端调用

测试脚本源码:check.php

if ($argc < 3) {
    echo "Usage: $argv[0] <ip> <port> [service] [params ...]\n\n";
    echo "Example: $argv[0] 127.0.0.1 9501 Default.Index username=swoole\n\n";
    exit(1);
}

$ip = trim($argv[1]);
$port = intval($argv[2]);
$service = isset($argv[3]) ? trim($argv[3]) : 'Default.Index';
$params = array('service' => $service);

if (isset($argv[4])) {
    $moreParams = array_slice($argv, 4);
    foreach ($moreParams as $param) {
        list($key, $value) = explode('=', $param);
        $params[$key] = $value;
    }
}

$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
//设置事件回调函数
$client->on("connect", function($cli) {
    global $params;
    //$params = array();
    //$params['service'] = 'Default.Index';
    //$params['username'] = 'swoole';

    $data = json_encode($params);

    echo "Send: " . $data . "\n";;

    $cli->send($data);
});
$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";
});
//发起网络连接
$client->connect($ip, $port, 3);

 

 

在扩展类库里有一个测试的脚本,可以用来进行PHP客户端的请求。

(1)默认接口调用
$ php ./check.php 127.0.0.1 9501 Default.Index username=swoole
Send: {"service":"Default.Index","username":"swoole"}
Received: {"ret":200,"data":{"title":"Hello World!","content":"Hi swoole, welcome to use PhalApi!","version":"1.1.3","time":1430620911},"msg":""}
Connection close
(2)带数据库的调用
$ php ./check.php 127.0.0.1 9501 User.getBaseInfo userId=1
Send: {"service":"User.getBaseInfo","userId":"1"}
Received: {"ret":200,"data":{"code":0,"msg":"","info":{"id":"1","username":"aevit","nickname":"test","password":"DE4CA99150F44B26F0D320DCA6E4B7629C43B6","salt":"wefewfew","reg_time":"0","avatar":"http:\/\/image.famillelab.com\/no_avatar.png","UUID":""}},"msg":""}
Connection close
(3)异步计划任务的调度
$ php ./check.php 127.0.0.1 9502 Default.Index username=swoole
Send: {"service":"Default.Index","username":"swoole"}
Connection close

假设Default.Index为一个计划任务的接口,但目前发现一个问题是,首次请求异步计划任务不会主动结束,而需要工作强制ctrl + c结束,再请求,则正常。

对应的log,可以看到:

2015-05-03 12:24:57|DEBUG|asynctask(0) dispath in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(0) start in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(0) finish in swoole|{"ret":200,"data":{"title":"Hello World!","content":"Hi swoole, welcome to use PhalApi!","version":"1.1.3","time":1430627097},"msg":""}
2015-05-03 12:24:57|DEBUG|asynctask(1) dispath in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(1) start in swoole|{"service":"Default.Index","username":"swoole"}
2015-05-03 12:24:57|DEBUG|asynctask(1) finish in swoole|{"ret":200,"data":{"title":"Hello World!","content":"Hi swoole, welcome to use PhalApi!","version":"1.1.3","time":1430627097},"msg":""}
...

 

附赠:swoole 重启脚本

linux shell 写swoole重启脚本 代码如下
#!/bin/sh
kill `lsof -t -i:9501`
sleep 2
php /data/web/mircoweb/wwwroot/Public/swoole.php
sleep 1
netstat -ntlp

把以上代码,写入shell脚本,比如 swoole_reload.sh,

然后执行 sh swoole.reload.sh

 

 

转载于:https://my.oschina.net/osgrace/blog/1551788

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值