thinkphp5集成swoole的方法

PHP的异步、并行、高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。

前提:

保证你的环境下已经安装好swoole拓展!如不知道怎么安装请上网百度 参考下面:

博主的是在ubuntu下安装的:

安装的时候尽量使用php自带的pecl安装,一键安装,没那么多事儿。耐操的同学可以试试自行编译安装,虽然也比较简单,但容易出现一些版本的问题。

一键安装:/usr/local/php/bin/pecl install swoole

确保产生的swoole.so文件在/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226下面(一般都在),不在的话考到这里

然后在php.ini文件里添加extension="/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/swoole.so",然后重启php-fpm加载swoole扩展模块即可。 使用php -m 命令查看加载的模块,看看有没有swoole

正文

swoole主要分为client和server两个部分,client向资源池发送任务,server从资源池中去任务处理。两者监听同一个端口号。

Server: server 定义了一条命令 swoole-server需要以守卫进程的方式运行,一直监听9800端口号,只要有client法该客户端发送任务,server就可以捕获并且处理。

server 生命周期分为 Receive(接收)、Task(执行)、Finish(执行完成),详细见下面代码和注释。

新建目录:mkdir aplication/console

修改命令文件:application/command.php

            return [

                '\app\console\swoole',

            ];

setName('swoole-server')->setDescription('swoole服务端');
    }
 
    protected function execute(Input $input, Output $output) {
        $this->serv = new \swoole_server('0.0.0.0', 9800);   // 允许所有IP访问
        $this->logFile = RUNTIME_PATH . 'swoole-log' . DS . date('Ymd') . '.log';
        $this->serv->set([
            'worker_num'      => 12,
            // 一般设置为服务器CPU数的1-4倍
            'task_worker_num' => 20,
            // task进程的数量(一般任务都是同步阻塞的,可以设置为单进程单线程)
            'daemonize'       => true,
            'open_eof_split'  => true,//打开eof_split检测
            'package_eof'     => PHP_EOL,//设置EOF
            // 以守护进程执行
            //  'task_ipc_mode' => 1,  // 使用unix socket通信,默认模式
            'log_file'        => $this->logFile,
            // swoole日志
            // 数据包分发策略(dispatch_mode=1/3时,底层会屏蔽onConnect/onClose事件,
            // 原因是这2种模式下无法保证onConnect/onClose/onReceive的顺序,非请求响应式的服务器程序,请不要使用模式1或3)
            //  'dispatch_mode' => 2,        // 固定模式,根据连接的文件描述符分配worker。这样可以保证同一个连接发来的数据只会被同一个worker处理
        ]);
        $this->serv->on('Receive', [
            $this,
            'onReceive'
        ]);
        $this->serv->on('Task', [
            $this,
            'onTask'
        ]);
        $this->serv->on('Finish', [
            $this,
            'onFinish'
        ]);
        $this->serv->start();
    }
 
    /**
     * 测试使用,生成环境不要用,效率极低
     * @param $strLogContext
     */
    public function error($strLogContext) {
        $fp = @fopen($this->logFile, "a+");
        @fputs($fp, $strLogContext . PHP_EOL);
        @fclose($fp);
    }
 
    /**
     * 接收到数据时回调此函数,发生在worker进程中
     * $server,swoole_server对象
     * $fd,TCP客户端连接的文件描述符
     * $from_id,TCP连接所在的Reactor线程ID
     * $data,收到的数据内容,可能是文本或者二进制内容
     */
    public function onReceive($serv, $fd, $from_id, $data) {
        $str = PHP_EOL . "=========== onReceive ============" . PHP_EOL;
        $str .= "Get Message From Client {$fd}:{$data}" . '--fromid--' . $from_id . PHP_EOL;
        Log::record($str, 'swoole');
        $serv->task($data);
    }
 
    /**
     * 在task_worker进程内被调用。worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务。当前的Task进程在调用onTask回调函数时会将进程状态切换为忙碌,
     * 这时将不再接收新的Task,当onTask函数返回时会将进程状态切换为空闲然后继续接收新的Task。
     * $task_id是任务ID,由swoole扩展内自动生成,用于区分不同的任务。$task_id和$src_worker_id组合起来才是全局唯一的,不同的worker进程投递的任务ID可能会有相同
     * $src_worker_id来自于哪个worker进程
     * $data 是任务的内容
     */
    public function onTask($serv, $task_id, $src_worker_id, $data) {
        $array = json_decode($data, true);
        $str   = "=========== onTask ============" . PHP_EOL;
        $str .= var_export($array, 1) . PHP_EOL;
        Log::record($str, 'swoole');
 
        return $array;
    }
 
    /**
     * 当worker进程投递的任务在task_worker中完成时,task进程会通过swoole_server->finish()方法将任务处理的结果发送给worker进程
     * $task_id是任务的ID
     * $data是任务处理的结果内容(也就是onTask()函数,中return的值)
     */
    public function onFinish($serv, $task_id, $data) {
        if($data['errno'] > 0) {
            $str = "onFinish ERROR:{$task_id} " . json_encode($data) . '|' . date('Y-m-d H:i:s') . PHP_EOL;
            Log::record(['more_info' => [$str . '|' . date('Y-m-d H:i:s') . PHP_EOL]], 'elk');
            Log::save();
        }
    }
 
}

client是想进程池发送任务的,同样的是监听9800端口。

下面的demo代码是把client也定义了一条命令,实际业务中也可以直接在 业务逻辑中调用_sendData($data)方法,前提是client类不要在继承Command类,否则会出错。

namespace app\console\swoole;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Log;
class ClientCommand extends Command{
    protected function configure() {
        $this->setName('swoole-client')->setDescription('swoole客户端');
    }
    protected function execute(Input $input, Output $output) {
        Log::record('swoole-client', 'swoole');
        $data = [1,2,3];
        $this->_sendData(json_encode($data));
    }
     private function _sendData($data) {
        $client = new \swoole_client(SWOOLE_SOCK_TCP);
        $client->connect('127.0.0.1', 9800, 1);
        $client->send($data.PHP_EOL);
    }
 
}

使用方法

定义了上面两个命令之后,可以执行了。

在项目中先运行server,让它开始监听:php think swoole-server

然后执行client开始投递任务:php think swoole-client

然后在日志中就可以看到执行时的日志

你可能感兴趣的:(Swoole,Thinkphp)