thinkphp3.2引入第三方类方法_kobel的博客-CSDN博客

mikel阅读(1123)

来源: thinkphp3.2引入第三方类方法_kobel的博客-CSDN博客

订阅专栏
1、在第三方类中,无需写namespace

2、在引用的方法中,用vendor(文件夹.类名),例如:vendor(‘yg.Yg’);

3、在使用时,需要实例化,在类里面new的时候要多加一个\,例如;$mail = new \ yg();

4、在引用类的方法时,例如:$mail::方法名称()。
————————————————
版权声明:本文为CSDN博主「kobel28」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luyongtao28/article/details/83013683

thinkphp 3.2公共类库、应用类库ThinkPHP/Library讲解_weixin_34293246的博客-CSDN博客

mikel阅读(899)

来源: thinkphp 3.2公共类库、应用类库ThinkPHP/Library讲解_weixin_34293246的博客-CSDN博客

 thinkphp 3.2公共类库、应用类库ThinkPHP/Library讲解

ThinkPHP的类库主要包括公共类库和应用类库,都是基于命名空间进行定义和扩展的。只要按照规范定义,都可以实现自动加载。

        公共类库

公共类库通常是指ThinkPHP/Library目录下面的类库,例如:

Think目录:系统核心类库
Org目录:第三方公共类库

这些目录下面的类库都可以自动加载,你只要把相应的类库放入目录中,然后添加或者修改命名空间定义。 你可以在Org/Util/目录下面添加一个Image.class.php 文件,然后添加命名空间如下:

namespace Org\Util;
class Image {
}

这样,就可以用下面的方式直接实例化Image类了:

$image = new \Org\Util\Image;

        除了这些目录之外,你完全可以在ThinkPHP/Library目录下面添加自己的类库目录,例如,我们添加一个Com目录用于企业类库扩展:

Com\Sina\App类(位于Com/Sina/App.class.php )

namespace Com\Sina;
class App {
}

Com\Sina\Rank类(位于Com/Sina/Rank.class.php)

namespace Com\Sina;
class Rank {
}

公共类库除了在系统的Library目录之外,还可以自定义其他的命名空间,我们只需要注册一个新的命名空间,在应用或者模块配置文件中添加下面的设置参数:

‘AUTOLOAD_NAMESPACE’ => array(
‘Lib’ => APP_PATH.’Lib’,
)

我们在应用目录下面创建了一个Lib目录用于放置公共的Lib扩展,如果我们要把上面两个类库放到Lib\Sina目录下面,只需要调整为:

Lib\Sina\App类(位于Lib/Sina/App.class.php )

namespace Lib\Sina;
class App {
}

Lib\Sina\Rank类(位于Lib/Sina/Rank.class.php)

namespace Lib\Sina;
class Rank {
}

如果你的类库没有采用命名空间的话,需要使用import方法先加载类库文件,然后再进行实例化,例如: 我们定义了一个Counter类(位于Com/Sina/Util/Counter.class.php):

class Counter {
}

在使用的时候,需要按下面方式调用:

import(‘Com.Sina.Util.Couter’);
$object = new \Counter();

        应用类库

应用类库通常是在应用或者模块目录下面的类库,应用类库的命名空间一般就是模块的名称为根命名空间,例如: Home\Model\UserModel类(位于Application\Home\Model)

namespace Home\Model;
use Think\Model;
class UserModel extends Model{
}

Common\Util\Pay类(位于Application\Common\Util)

namespace Common\Util;
class Pay {
}

Admin\Api\UserApi类(位于Application\Admin\Api)

namespace Admin\Api;
use Think\Model;
class UserApi extends Model{
}

记住一个原则,命名空间的路径和实际的文件路径对应的话 就可以实现直接实例化的时候自动加载。

转载于:https://my.oschina.net/u/2247058/blog/358065

tp3.2/thinkphp3.2引入外部类文件/.php文件总结_尼古拉斯鹏-CSDN博客

mikel阅读(829)

来源: tp3.2/thinkphp3.2引入外部类文件/.php文件总结_尼古拉斯鹏-CSDN博客

一、引入第三方类库 将文件放在Org/Util下面 比如:.class.php文件

1.可以将文件放在Org/Util下面如test.class.php (也就是说以Think、Org为根命名空间的类都可以自动加载:)

ThinkPHP/Library/Org/Util/test.class.php。

2.给类库加命名空间如下

namespaceOrg\Util;

3.在控制器中实例化这个类的方式如下:

new\Org\Util\Auth();

二、引入类库放在项目模块中然后引入的方法

1.要给类名以.php后缀的改为以.class.php后缀的 如上图

2. a.如果被引入的类文件没有命名空间 如下:

$c = new \AopClient;

b.如果有命名空间

可以 use Wechat\ORG\AopClient; 然后$c = new \AopClient;实例化

也可以加个根命名空间 $c = new \AopClient;实例化

三、手动加载第三方普通.php后缀的原生文件

如果你的第三方类库都放在Vendor目录下面,并且都以.php为类文件后缀,也没用采用命名空间的话,

那么可以使用系统内置的Vendor函数简化导入。

例如,我们把 Zend \ Filter\Dir.php 放到 Vendor 目录下面,这个时候 Dir 文件的路径就是 Vendor\Zend\Filter\Dir.php,我们使用vendor 方法导入只需要使用:

实例化:

Vendor(‘Zend.Filter.Dir’);

$obj = new \Dir();

注意:如果你的文件是a.b.php(b不是class)的话,也可以这样导入:

Vendor(‘目录.a#b’);

实例化对象时候,以class ab {…}示例:new \ab();

四、手动加载.php后缀且面向过程的文件

文件内容是面向过程的,就是文件里面没有class aaa{} ,不用实例化操作,直接使用。

我们可以使用原生的语法:

在控制器的方法里面使用:

include_once ‘./ThinkPHP/Library/Vendor/lib/aaa.bbb.php’;
————————————————
版权声明:本文为CSDN博主「小鹏程序」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35979073/article/details/79361600

Swoole整合ThinkPHP3.2系列教程六_一个不靠谱的程序员-CSDN博客

mikel阅读(1151)

来源: Swoole整合ThinkPHP3.2系列教程六_一个不靠谱的程序员-CSDN博客

终结篇
哈哈,我就说这一系列是完整的吧。

我们并没有用swoole框架重构系统代码,只是当成一个长连接扩展库来使用的。

swoole很强大,我们只是用了swoole很小很小的一部分。如果用swoole做更多精细化的处理,很麻烦。

毕竟我这种面向工资编程的开发者得服从leader的安排(尽快上线)。

如果有同学在使用过程中出现问题了欢迎留言讨论。

我踩过的坑
swoole_client只能在cli模式下运行,不要指望在浏览器里调用。

最早想要搭建一个TCP连接,然后用浏览器也访问这个server,虽然无法在页面输出,但是在swoole的onReceive回调里依然是可以接收到值的,想利用这个特性做浏览器访问。如果你也有这个思路,可以停止了。

swoole_client是没有办法连接websocket服务端的,必须使用swoole_http_client,设置项增加’websocket_mask’ => true

开发过程中临时补了一些TCP/IP协议的东西,望着这些东西,想起上学时浪费时间打的LOL,留下了悔恨的眼泪。

使用本教程里的案例 php swoole.php start启动时,请一定要确保在onWorkerStart回调了加载TP框架东西的时候,确保要在指定的模块里含有StartController并且里面有index方法,不然你就会看到惊喜的一幕。TP框架抛出错误exit,swoole重新拉起worker进程,TP框架抛出错误exit,swoole重新拉起worker进程,这种死循环简直过瘾。log里记录的错误信息是[zm_deactivate_swoole: worker process is terminated by exit()/die().]

当在线上部署时,将swoole设置为守护进程运行,此时记得把所有路径定义的部分 比如require(TP框架)的部分都换成绝对路径 dirname(__DIR__)这种的。。
————————————————
版权声明:本文为CSDN博主「一个不靠谱的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013705066/article/details/77680328

Swoole整合ThinkPHP3.2系列教程五_一个不靠谱的程序员-CSDN博客

mikel阅读(1223)

来源: Swoole整合ThinkPHP3.2系列教程五_一个不靠谱的程序员-CSDN博客

如何开启SSL
对于已经升级成了https协议的网站,我们需要使用websocket连接swoole的话,无法正常连接上swoole服务。因为https认为这是不安全的连接,所以我们必须为swoole开启SSL

1.检查当前的swoole扩展是否开启了openssl:
php –ri swoole
如果看到openssl=>enabled,则表示当前安装的swoole扩展已经开启了SSL。否则请重新编译,携带编译参数–enable-openssl

下载源代码包后,在终端进入源码目录,执行下面的命令进行编译和安装 (和重新编译安装步骤一样)
cd swoole
phpize
./configure –enable-openssl (如果这一步提示让你设置php-config的路径,在这加上就行了)
make
sudo make install

如果是之前安装过swoole,这里就不用配置php.ini文件里的extension=swoole.so,否则需要自己配置一下。
安装完成以后重新执行上述命令检测是否启用SSL

2.修改swoole.php里文件,定义crt证书路径和key密匙路径。

//笔者测试环境里是把这两个文件都放到了Swoole目录下,根据自定义设置修改
define(‘SSL_CRT’, SWOOLE_PATH.’/server.crt’);
define(‘SSL_KEY’, SWOOLE_PATH.’/server.key’);

3.修改Server.php里配置项$options数组,增加以下参数:

‘ssl_cert_file’ => SSL_CRT,
‘ssl_key_file’ => SSL_KEY,

4.修改Server.php里init()方法里的创建server时增加额外的参数

$this->swoole = new swoole_websocket_server($this->host, $this->port , SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);

5.将websocket客户端里的连接地址ws改成wss(注意这里只能走域名,走IP的话无法连接,因为SSL证书是绑定在域名上的)

6.重启swoole服务,打开浏览器测试一下吧。
————————————————
版权声明:本文为CSDN博主「一个不靠谱的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013705066/article/details/77774379

Swoole:PHP 协程框架 - DianThink点想网络

mikel阅读(1159)

来源: Swoole:PHP 协程框架 – DianThink点想网络

Swoole 是一个 PHP 的 协程 高性能 网络通信引擎,使用 C/C++ 语言编写,提供了多种通信协议的网络服务器和客户端模块。可以方便快速的实现 TCP/UDP服务、高性能Web、WebSocket服务、物联网、实时通讯、游戏、微服务等,使 PHP 不再局限于传统的 Web 领域。

Swoole 使 PHP 开发人员可以编写高性能高并发的 TCP、UDP、Unix Socket、HTTP、 WebSocket 等服务,让 PHP 不再局限于 Web 领域。

Swoole4 协程的成熟将 PHP 带入了前所未有的时期, 为性能的提升提供了独一无二的可能性。

Swoole 可以广泛应用于互联网、移动通信、云计算、 网络游戏、物联网(IOT)、车联网、智能家居等领域。

使用 PHP + Swoole 可以使企业 IT 研发团队的效率大大提升,更加专注于开发创新产品。

HTTP Server

//高性能HTTP服务器
$http = new Swoole\Http\Server("127.0.0.1", 9501);

$http->on("start", function ($server) {
    echo "Swoole http server is started at http://127.0.0.1:9501\n";
});

$http->on("request", function ($request, $response) {
    $response->header("Content-Type", "text/plain");
    $response->end("Hello World\n");
});

$http->start();

WebSocket Server

$server = new Swoole\Websocket\Server("127.0.0.1", 9502);

$server->on('open', function($server, $req) {
    echo "connection open: {$req->fd}\n";
});

$server->on('message', function($server, $frame) {
    echo "received message: {$frame->data}\n";
    $server->push($frame->fd, json_encode(["hello", "world"]));
});

$server->on('close', function($server, $fd) {
    echo "connection close: {$fd}\n";
});

$server->start();

TCP Server

$server = new Swoole\Server("127.0.0.1", 9503);
$server->on('connect', function ($server, $fd){
    echo "connection open: {$fd}\n";
});
$server->on('receive', function ($server, $fd, $reactor_id, $data) {
    $server->send($fd, "Swoole: {$data}");
    $server->close($fd);
});
$server->on('close', function ($server, $fd) {
    echo "connection close: {$fd}\n";
});
$server->start();

UDP Server

$serv = new Swoole\Server("127.0.0.1", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);

//监听数据接收事件
$serv->on('Packet', function ($serv, $data, $clientInfo) {
    $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server ".$data);
    var_dump($clientInfo);
});

//启动服务器
$serv->start();

Task

$server = new Swoole\Server("127.0.0.1", 9502);
$server->set(array('task_worker_num' => 4));
$server->on('receive', function($server, $fd, $reactor_id, $data) {
    $task_id = $server->task("Async");
    echo "Dispatch AsyncTask: [id=$task_id]\n";
});
$server->on('task', function ($server, $task_id, $reactor_id, $data) {
    echo "New AsyncTask[id=$task_id]\n";
    $server->finish("$data -> OK");
});
$server->on('finish', function ($server, $task_id, $data) {
    echo "AsyncTask[$task_id] finished: {$data}\n";
});
$server->start();

Coroutine

//睡眠 1 万次,读取,写入,检查和删除文件 1 万次,使用 PDO 和 MySQLi 与数据库通信 1 万次,创建 TCP 服务器和多个客户端相互通信 1 万次,
//创建 UDP 服务器和多个客户端到相互通信 1 万次...... 一切都在一个进程一秒内完美完成!

Swoole\Runtime::enableCoroutine();//此行代码后,文件操作,sleep,Mysqli,PDO,streams等都变成异步IO,见文档'一键协程化'章节
$s = microtime(true);
//Co/run()见文档'协程容器'章节
Co\run(function() {
// i just want to sleep...
for ($c = 100; $c--;) {
    go(function () {
        for ($n = 100; $n--;) {
            usleep(1000);
        }
    });
}

// 10k file read and write
for ($c = 100; $c--;) {
    go(function () use ($c) {
        $tmp_filename = "/tmp/test-{$c}.php";
        for ($n = 100; $n--;) {
            $self = file_get_contents(__FILE__);
            file_put_contents($tmp_filename, $self);
            assert(file_get_contents($tmp_filename) === $self);
        }
        unlink($tmp_filename);
    });
}

// 10k pdo and mysqli read
for ($c = 50; $c--;) {
    go(function () {
        $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root');
        $statement = $pdo->prepare('SELECT * FROM `user`');
        for ($n = 100; $n--;) {
            $statement->execute();
            assert(count($statement->fetchAll()) > 0);
        }
    });
}
for ($c = 50; $c--;) {
    go(function () {
        $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');
        $statement = $mysqli->prepare('SELECT `id` FROM `user`');
        for ($n = 100; $n--;) {
            $statement->bind_result($id);
            $statement->execute();
            $statement->fetch();
            assert($id > 0);
        }
    });
}

// php_stream tcp server & client with 12.8k requests in single process
function tcp_pack(string $data): string
{
    return pack('n', strlen($data)) . $data;
}

function tcp_length(string $head): int
{
    return unpack('n', $head)[1];
}

go(function () {
    $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);
    $socket = stream_socket_server(
        'tcp://0.0.0.0:9502',
        $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx
    );
    if (!$socket) {
        echo "$errstr ($errno)\n";
    } else {
        $i = 0;
        while ($conn = stream_socket_accept($socket, 1)) {
            stream_set_timeout($conn, 5);
            for ($n = 100; $n--;) {
                $data = fread($conn, tcp_length(fread($conn, 2)));
                assert($data === "Hello Swoole Server #{$n}!");
                fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!"));
            }
            if (++$i === 128) {
                fclose($socket);
                break;
            }
        }
    }
});
for ($c = 128; $c--;) {
    go(function () {
        $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1);
        if (!$fp) {
            echo "$errstr ($errno)\n";
        } else {
            stream_set_timeout($fp, 5);
            for ($n = 100; $n--;) {
                fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!"));
                $data = fread($fp, tcp_length(fread($fp, 2)));
                assert($data === "Hello Swoole Client #{$n}!");
            }
            fclose($fp);
        }
    });
}

// udp server & client with 12.8k requests in single process
go(function () {
    $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0);
    $socket->bind('127.0.0.1', 9503);
    $client_map = [];
    for ($c = 128; $c--;) {
        for ($n = 0; $n < 100; $n++) {
            $recv = $socket->recvfrom($peer);
            $client_uid = "{$peer['address']}:{$peer['port']}";
            $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1;
            assert($recv === "Client: Hello #{$id}!");
            $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!");
        }
    }
    $socket->close();
});
for ($c = 128; $c--;) {
    go(function () {
        $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1);
        if (!$fp) {
            echo "$errstr ($errno)\n";
        } else {
            for ($n = 0; $n < 100; $n++) {
                fwrite($fp, "Client: Hello #{$n}!");
                $recv = fread($fp, 1024);
                list($address, $port) = explode(':', (stream_socket_get_name($fp, true)));
                assert($address === '127.0.0.1' && (int)$port === 9503);
                assert($recv === "Server: Hello #{$n}!");
            }
            fclose($fp);
        }
    });
}
});
echo 'use ' . (microtime(true) - $s) . ' s';

Channel

Co\run(function(){
        //使用Channel进行协程间通讯
        $chan = new Swoole\Coroutine\Channel(1);
        Swoole\Coroutine::create(function () use ($chan) {
            for($i = 0; $i < 100000; $i++) {
                co::sleep(1.0);
                $chan->push(['rand' => rand(1000, 9999), 'index' => $i]);
                echo "$i\n";
            }
        });
        Swoole\Coroutine::create(function () use ($chan) {
            while(1) {
                $data = $chan->pop();
                var_dump($data);
            }
        });
  });

开源、高性能、高生产力

如果您有基于Swoole的优秀项目并想展现在下处,欢迎扫描侧边二维码来联系我们

Swoole源码      Swoole Plus        Swoole Tracker        Swoole Compiler

客服系统

即信在线客服系统是一套使用 Laravel+Swoole 编写的在线客服系统,支持 PC Web 和移动端 H5 网页,支持完善的权限管理和后台报表,聊天功能十分强大,支持语音聊天,微信留言等,并可以无缝接入微信公众号, 网页端只需嵌入一段 js 即可快速接入开源客服系统,可以无限添加用户,不限制坐席数 ,授权后可私有化部署,无数据安全问题, 并提供客服系统源码

聊天系统(JIM)

JIM 是一套使用 Swoole 编写的轻量级 IM 系统,界面简洁清爽,支持换肤,让在线办公更便捷,用 JIM 手机电脑上的文件都能收发自如,轻松完成你的工作和娱乐,支持丰富的表情包,视频聊天,语音聊天,群聊,单聊,消息漫游,输入检测等等常用的 IM 功能,授权后可私有化部署并二次开发,无数据安全问题,并提供一年的技术支持

物联网(IOT)解决方案

MQTT 是一个客户端服务端架构的发布 / 订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。可以使用 Swoole 作为 MQTT 服务端或客户端,实现一套完整物联网(IOT)解决方案

新冠疫情抓取

基于 Swoole + imi 框架的新型冠状病毒肺炎疫情实时动态爬虫抓取项目,采集数据来源为丁香园。演示地址:https://test.yurunsoft.com/ncov/

PHP微服务(Micro Service)

Hyperf 是基于 Swoole 4.4+ 实现的高性能、高灵活性的 PHP 协程框架,内置协程服务器及大量常用的组件,性能较传统基于 PHP-FPM 的框架有质的提升,提供超高性能的同时,也保持着极其灵活的可扩展性,标准组件均基于 PSR 标准 实现,基于强大的依赖注入设计,保证了绝大部分组件或类都是可替换与可复用的

斗地主

基于 Swoole + Hyperf 框架开发 demo 级的斗地主游戏,实现斗地主游戏服务端逻辑,并采用原生 js 和 WebSocket 实现简单的客户端打牌逻辑,可以做到简单的玩斗地主游戏

魔兽世界

魔兽模拟游戏服务器,项目采用 PHP 开发,TCP 长连接基于 Swoole,支持鉴权,角色的创建,地图的加载,NPC 和生物的构建及各种眼花缭乱的物品和技能等等

HyperfCMS

HyperfCMS 是基于 Swoole+Hyperf 框架前后端分离架构的一套开源且完美的建站系统,拥有简单大气设计、友好的交互体验、清晰明了的代码规范。组件化的封装应用,编写复杂的管理应用,效率是质的提升、时间成倍缩短,人员可以减半,事半功倍。可以提供定制化服务!

实时视频语音 (Webrtc)

基于 Swoole4 高性能协程的 demo 级实时视频和语音通话方案,采用 webrtc 协议,并已经做好 p2p 打洞,中继服务器,演示地址:https://webrtc.dingjw.com/room.php?cid=2

MySQL Proxy(MySQL中间件)

一个基于 MySQL 协议,Swoole 开发的 MySQL 数据库连接池,支持读写分离支持数据库连接池,能够有效解决 PHP 带来的数据库连接瓶颈,支持 SQL92 标准,采用协程调度,遵守 MySQL 原生协议,跨语言,跨平台的通用中间件代理,支持 MySQL 事务,完美兼容 MySQL5.5 – 8.0

分布式定时任务 (Swoole Crontab)

基于 Swoole 的定时器程序,支持秒级处理,完全兼容 crontab 语法,且支持秒的配置,可使用数组规定好精确操作时间,Web 界面管理,增删改查任务,完整的权限控制

ThinkCMF

ThinkCMF 是一款基于 ThinkPHP+Mysql 开发的 CMS,完美支持 Swoole,框架自身提供基础的管理功能,而开发者可以根据自身的需求以应用的形式进行扩展。每个应用都能独立的完成自己的任务,也可通过系统调用其他应用进行协同工作。在这种运行机制下,开发商场应用的用户无需关心开发 SNS 应用时如何工作的,但他们之间又可通过系统本身进行协调,大大的降低了开发成本和沟通成本

Markdown文档系统

软擎文档系统是基于 Swoole + Rangine 框架开发的开源版 MarkDown 文档管理系统,不同于 docsify.js 等前端文档系统,本文档系统是偏后端的文档系统,对于广大PHPer来说更加友好。支持多用户协同操作,管理员审核发布等功能。 让您的工作更高效,更智慧。

Ain’t Queue 异步队列

Ain’t Queue 借助 Swoole 提供的便捷多进程 API 和 CSP 编程能力实现了主进程+监控进程+多工作进程的进程模型,并且提供了各类事件自定义注册的能力(队列监控、快照记录、任务中间件等)。默认使用 Redis 驱动,全原子操作,可延时可重试,自带漂亮的仪表盘,稳定可靠,已经在公司生产环境使用。

PaySDK

PHP 集成支付 SDK ,集成了支付宝、微信支付的支付接口和其它相关接口的操作。支持 php-fpm 和 Swoole,所有框架通用。

ZooKeeper

基于 Swoole 协程的PHP ZooKeeper客户端

LaravelS

LaravelS 是 Swoole 和 Laravel/Lumen 之间开箱即用的适配器,内置 HTTP/WebSocket Server,支持 TCP/UDP Server、自定义进程、异步的事件监听、异步任务、毫秒级定时任务、平滑Reload等特性,让 Laravel 如虎添翼。

xlswriter

xlswriter是一个 PHP C 扩展,支持 Swoole 协程环境,可用于在 Excel 2007+ XLSX 文件中读取数据,插入多个工作表,写入文本、数字、公式、日期、图表、图片和超链接。

Swoole 特性

Swoole 使用 C/C++ 语言编写,提供了 PHP 语言的异步多线程服务器、异步 TCP/UDP 网络客户端、异步 MySQL、异步 Redis、数据库连接池、AsyncTask、消息队列、毫秒定时器、异步文件读写、异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。
除了异步 IO 的支持之外,Swoole 为 PHP 多进程的模式设计了多个并发数据结构和IPC通信机制,可以大大 简化多进程并发编程的工作。其中包括了并发原子计数器、并发 HashTable、Channel、Lock、进程间通信IPC 等丰富的功能特性。
Swoole4.0 支持了类似 Go 语言的协程,可以使用完全同步的代码实现异步程序。PHP 代码无需额外增加任何 关键词,底层自动进行协程调度,实现异步IO。

Swoole整合ThinkPHP3.2系列教程四_一个不靠谱的程序员-CSDN博客

mikel阅读(1128)

来源: Swoole整合ThinkPHP3.2系列教程四_一个不靠谱的程序员-CSDN博客

在cli环境下,PHP程序需要长时间运行,客户端与MySQL服务器之间的TCP连接是不稳定的。

MySQL-Server会在一定时间内自动切断连接
PHP程序遇到空闲期时长时间没有MySQL查询,MySQL-Server也会切断连接回收资源
其他情况,在MySQL服务器中执行kill process杀掉某个连接,MySQL服务器重启
所以我们要考虑数据库断线重连的问题,但是ThinkPHP3.2里DB驱动类里并没有断线重连的例子,好消息是TP5里有,于是我照着TP5里的源码,改了TP3.2里的DB驱动类,来达到数据库断线自动重连。

附上代码
完整的DB.class.php文件 保证可用。

<?php
// +———————————————————————-
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +———————————————————————-
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +———————————————————————-
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +———————————————————————-
// | Author: liu21st <liu21st@gmail.com>
// +———————————————————————-
// 2017-8-24 09:26:23 增加了断线重连功能

namespace Think\Db;
use Think\Config;
use Think\Debug;
use Think\Log;
use PDO;

abstract class Driver {
// PDO操作实例
protected $PDOStatement = null;
// 当前操作所属的模型名
protected $model = ‘_think_’;
// 当前SQL指令
protected $queryStr = ”;
protected $modelSql = array();
// 最后插入ID
protected $lastInsID = null;
// 返回或者影响记录数
protected $numRows = 0;
// 事务指令数
protected $transTimes = 0;
// 错误信息
protected $error = ”;
// 数据库连接ID 支持多个连接
protected $linkID = array();
// 当前连接ID
protected $_linkID = null;
// 数据库连接参数配置
protected $config = array(
‘type’ => ”, // 数据库类型
‘hostname’ => ‘127.0.0.1’, // 服务器地址
‘database’ => ”, // 数据库名
‘username’ => ”, // 用户名
‘password’ => ”, // 密码
‘hostport’ => ”, // 端口
‘dsn’ => ”, //
‘params’ => array(), // 数据库连接参数
‘charset’ => ‘utf8’, // 数据库编码默认采用utf8
‘prefix’ => ”, // 数据库表前缀
Debug’ => false, // 数据库调试模式
‘deploy’ => 0, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
‘rw_separate’ => false, // 数据库读写是否分离 主从式有效
‘master_num’ => 1, // 读写分离后 主服务器数量
‘slave_no’ => ”, // 指定从服务器序号
‘db_like_fields’ => ”,
);
// 数据库表达式
protected $exp = array(‘eq’=>’=’,’neq’=>'<>’,’gt’=>’>’,’egt’=>’>=’,’lt’=>'<‘,’elt’=>'<=’,’notlike’=>’NOT LIKE’,’like’=>’LIKE’,’in’=>’IN’,’notin’=>’NOT IN’,’not in’=>’NOT IN’,’between’=>’BETWEEN’,’not between’=>’NOT BETWEEN’,’notbetween’=>’NOT BETWEEN’);
// 查询表达式
protected $selectSql = ‘SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%’;
// 查询次数
protected $queryTimes = 0;
// 执行次数
protected $executeTimes = 0;
// PDO连接参数
protected $options = array(
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
);
protected $bind = array(); // 参数绑定

/**
* 架构函数 读取数据库配置信息
* @access public
* @param array $config 数据库配置数组
*/
public function __construct($config=”){
if(!empty($config)) {
$this->config = array_merge($this->config,$config);
if(is_array($this->config[‘params’])){
$this->options = $this->config[‘params’] + $this->options;
}
}
}

/**
* 连接数据库方法
* @access public
*/
public function connect($config=”,$linkNum=0,$autoConnection=false) {
if ( !isset($this->linkID[$linkNum]) ) {
if(empty($config)) $config = $this->config;
try{
if(empty($config[‘dsn’])) {
$config[‘dsn’] = $this->parseDsn($config);
}
if(version_compare(PHP_VERSION,’5.3.6′,'<=’)){
// 禁用模拟预处理语句
$this->options[PDO::ATTR_EMULATE_PREPARES] = false;
}
$this->linkID[$linkNum] = new PDO( $config[‘dsn’], $config[‘username’], $config[‘password’],$this->options);
}catch (\PDOException $e) {
if($autoConnection){
trace($e->getMessage(),”,’ERR’);
return $this->connect($autoConnection,$linkNum);
}elseif($config[‘debug’]){
E($e->getMessage());
}
}
}
return $this->linkID[$linkNum];
}

/**
* 解析pdo连接的dsn信息
* @access public
* @param array $config 连接信息
* @return string
*/
protected function parseDsn($config){}

/**
* 释放查询结果
* @access public
*/
public function free() {
$this->PDOStatement = null;
}

/**
* 执行查询 返回数据集
* @access public
* @param string $str sql指令
* @param boolean $fetchSql 不执行只是获取SQL
* @return mixed
*/
public function query($str,$fetchSql=false) {
$this->initConnect(false);
if ( !$this->_linkID ) return false;
$this->queryStr = $str;
if(!empty($this->bind)){
$that = $this;
$this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return ‘\”.$that->escapeString($val).’\”; },$this->bind));
}
if($fetchSql){
return $this->queryStr;
}
//释放前次的查询结果
if ( !empty($this->PDOStatement) ) $this->free();
$this->queryTimes++;
N(‘db_query’,1); // 兼容代码
// 调试开始
$this->debug(true);
$this->PDOStatement = $this->_linkID->prepare($str);
if(false === $this->PDOStatement){
$this->error(‘query’);
return false;
}
foreach ($this->bind as $key => $val) {
if(is_array($val)){
$this->PDOStatement->bindValue($key, $val[0], $val[1]);
}else{
$this->PDOStatement->bindValue($key, $val);
}
}
$this->bind = array();
$result = $this->PDOStatement->execute();
// 调试结束
$this->debug(false);
if ( false === $result ) {
$this->error(‘query’);
return false;
} else {
return $this->getResult();
}
}

/**
* 执行语句
* @access public
* @param string $str sql指令
* @param boolean $fetchSql 不执行只是获取SQL
* @return mixed
*/
public function execute($str,$fetchSql=false) {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
$this->queryStr = $str;
if(!empty($this->bind)){
$that = $this;
$this->queryStr = strtr($this->queryStr,array_map(function($val) use($that){ return ‘\”.$that->escapeString($val).’\”; },$this->bind));
}
if($fetchSql){
return $this->queryStr;
}
//释放前次的查询结果
if ( !empty($this->PDOStatement) ) $this->free();
$this->executeTimes++;
N(‘db_write’,1); // 兼容代码
// 记录开始执行时间
$this->debug(true);
$this->PDOStatement = $this->_linkID->prepare($str);
if(false === $this->PDOStatement) {
$this->error(‘execute’);
return false;
}
foreach ($this->bind as $key => $val) {
if(is_array($val)){
$this->PDOStatement->bindValue($key, $val[0], $val[1]);
}else{
$this->PDOStatement->bindValue($key, $val);
}
}
$this->bind = array();
$result = $this->PDOStatement->execute();
$this->debug(false);
if ( false === $result) {
$this->error(‘execute’);
return false;
} else {
$this->numRows = $this->PDOStatement->rowCount();
if(preg_match(“/^\s*(INSERT\s+INTO|REPLACE\s+INTO)\s+/i”, $str)) {
$this->lastInsID = $this->_linkID->lastInsertId();
}
return $this->numRows;
}
}

/**
* 启动事务
* @access public
* @return void
*/
public function startTrans() {
$this->initConnect(true);
if ( !$this->_linkID ) return false;
//数据rollback 支持
if ($this->transTimes == 0) {
$this->_linkID->beginTransaction();
}
$this->transTimes++;
return ;
}

/**
* 用于非自动提交状态下面的查询提交
* @access public
* @return boolean
*/
public function commit() {
if ($this->transTimes > 0) {
$result = $this->_linkID->commit();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}

/**
* 事务回滚
* @access public
* @return boolean
*/
public function rollback() {
if ($this->transTimes > 0) {
$result = $this->_linkID->rollback();
$this->transTimes = 0;
if(!$result){
$this->error();
return false;
}
}
return true;
}

/**
* 获得所有的查询数据
* @access private
* @return array
*/
private function getResult() {
//返回数据集
$result = $this->PDOStatement->fetchAll(PDO::FETCH_ASSOC);
$this->numRows = count( $result );
return $result;
}

/**
* 获得查询次数
* @access public
* @param boolean $execute 是否包含所有查询
* @return integer
*/
public function getQueryTimes($execute=false){
return $execute?$this->queryTimes+$this->executeTimes:$this->queryTimes;
}

/**
* 获得执行次数
* @access public
* @return integer
*/
public function getExecuteTimes(){
return $this->executeTimes;
}

/**
* 关闭数据库
* @access public
*/
public function close() {
$this->_linkID = null;
$this->linkID = [];
return $this;
}

/**
* 数据库错误信息
* 并显示当前的SQL语句
* @access public
* @return string
*/
public function error($method=”) {
if($this->PDOStatement) {
$error = $this->PDOStatement->errorInfo();
$this->error = $error[1].’:’.$error[2];
}else{
$this->error = ”;
}

//2017-8-24 09:18:45 判断是否断线 增加断线重连功能
if($this->isBreak($error[2]) && $method){
return $this->close()->{$method}($this->queryStr);
}

if(” != $this->queryStr){
$this->error .= “\n [ SQL语句 ] : “.$this->queryStr;
}
// 记录错误日志
trace($this->error,”,’ERR’);
if($this->config[‘debug’]) {// 开启数据库调试模式
E($this->error);
}else{
return $this->error;
}
}

/**
* 设置锁机制
* @access protected
* @return string
*/
protected function parseLock($lock=false) {
return $lock? ‘ FOR UPDATE ‘ : ”;
}

/**
* set分析
* @access protected
* @param array $data
* @return string
*/
protected function parseSet($data) {
foreach ($data as $key=>$val){
if(is_array($val) && ‘exp’ == $val[0]){
$set[] = $this->parseKey($key).’=’.$val[1];
}elseif(is_null($val)){
$set[] = $this->parseKey($key).’=NULL’;
}elseif(is_scalar($val)) {// 过滤非标量数据
if(0===strpos($val,’:’) && in_array($val,array_keys($this->bind)) ){
$set[] = $this->parseKey($key).’=’.$this->escapeString($val);
}else{
$name = count($this->bind);
$set[] = $this->parseKey($key).’=:’.$name;
$this->bindParam($name,$val);
}
}
}
return ‘ SET ‘.implode(‘,’,$set);
}

/**
* 参数绑定
* @access protected
* @param string $name 绑定参数名
* @param mixed $value 绑定值
* @return void
*/
protected function bindParam($name,$value){
$this->bind[‘:’.$name] = $value;
}

/**
* 字段名分析
* @access protected
* @param string $key
* @return string
*/
protected function parseKey(&$key) {
return $key;
}

/**
* value分析
* @access protected
* @param mixed $value
* @return string
*/
protected function parseValue($value) {
if(is_string($value)) {
$value = strpos($value,’:’) === 0 && in_array($value,array_keys($this->bind))? $this->escapeString($value) : ‘\”.$this->escapeString($value).’\”;
}elseif(isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == ‘exp’){
$value = $this->escapeString($value[1]);
}elseif(is_array($value)) {
$value = array_map(array($this, ‘parseValue’),$value);
}elseif(is_bool($value)){
$value = $value ? ‘1’ : ‘0’;
}elseif(is_null($value)){
$value = ‘null’;
}
return $value;
}

/**
* field分析
* @access protected
* @param mixed $fields
* @return string
*/
protected function parseField($fields) {
if(is_string($fields) && ” !== $fields) {
$fields = explode(‘,’,$fields);
}
if(is_array($fields)) {
// 完善数组方式传字段名的支持
// 支持 ‘field1’=>’field2′ 这样的字段别名定义
$array = array();
foreach ($fields as $key=>$field){
if(!is_numeric($key))
$array[] = $this->parseKey($key).’ AS ‘.$this->parseKey($field);
else
$array[] = $this->parseKey($field);
}
$fieldsStr = implode(‘,’, $array);
}else{
$fieldsStr = ‘*’;
}
//TODO 如果是查询全部字段,并且是join的方式,那么就把要查的表加个别名,以免字段被覆盖
return $fieldsStr;
}

/**
* table分析
* @access protected
* @param mixed $table
* @return string
*/
protected function parseTable($tables) {
if(is_array($tables)) {// 支持别名定义
$array = array();
foreach ($tables as $table=>$alias){
if(!is_numeric($table))
$array[] = $this->parseKey($table).’ ‘.$this->parseKey($alias);
else
$array[] = $this->parseKey($alias);
}
$tables = $array;
}elseif(is_string($tables)){
$tables = explode(‘,’,$tables);
array_walk($tables, array(&$this, ‘parseKey’));
}
return implode(‘,’,$tables);
}

/**
* where分析
* @access protected
* @param mixed $where
* @return string
*/
protected function parseWhere($where) {
$whereStr = ”;
if(is_string($where)) {
// 直接使用字符串条件
$whereStr = $where;
}else{ // 使用数组表达式
$operate = isset($where[‘_logic’])?strtoupper($where[‘_logic’]):”;
if(in_array($operate,array(‘AND’,’OR’,’XOR’))){
// 定义逻辑运算规则 例如 OR XOR AND NOT
$operate = ‘ ‘.$operate.’ ‘;
unset($where[‘_logic’]);
}else{
// 默认进行 AND 运算
$operate = ‘ AND ‘;
}
foreach ($where as $key=>$val){
if(is_numeric($key)){
$key = ‘_complex’;
}
if(0===strpos($key,’_’)) {
// 解析特殊条件表达式
$whereStr .= $this->parseThinkWhere($key,$val);
}else{
// 查询字段的安全过滤
// if(!preg_match(‘/^[A-Z_\|\&\-.a-z0-9\(\)\,]+$/’,trim($key))){
// E(L(‘_EXPRESS_ERROR_’).’:’.$key);
// }
// 多条件支持
$multi = is_array($val) && isset($val[‘_multi’]);
$key = trim($key);
if(strpos($key,’|’)) { // 支持 name|title|nickname 方式定义查询字段
$array = explode(‘|’,$key);
$str = array();
foreach ($array as $m=>$k){
$v = $multi?$val[$m]:$val;
$str[] = $this->parseWhereItem($this->parseKey($k),$v);
}
$whereStr .= ‘( ‘.implode(‘ OR ‘,$str).’ )’;
}elseif(strpos($key,’&’)){
$array = explode(‘&’,$key);
$str = array();
foreach ($array as $m=>$k){
$v = $multi?$val[$m]:$val;
$str[] = ‘(‘.$this->parseWhereItem($this->parseKey($k),$v).’)’;
}
$whereStr .= ‘( ‘.implode(‘ AND ‘,$str).’ )’;
}else{
$whereStr .= $this->parseWhereItem($this->parseKey($key),$val);
}
}
$whereStr .= $operate;
}
$whereStr = substr($whereStr,0,-strlen($operate));
}
return empty($whereStr)?”:’ WHERE ‘.$whereStr;
}

// where子单元分析
protected function parseWhereItem($key,$val) {
$whereStr = ”;
if(is_array($val)) {
if(is_string($val[0])) {
$exp = strtolower($val[0]);
if(preg_match(‘/^(eq|neq|gt|egt|lt|elt)$/’,$exp)) { // 比较运算
$whereStr .= $key.’ ‘.$this->exp[$exp].’ ‘.$this->parseValue($val[1]);
}elseif(preg_match(‘/^(notlike|like)$/’,$exp)){// 模糊查找
if(is_array($val[1])) {
$likeLogic = isset($val[2])?strtoupper($val[2]):’OR’;
if(in_array($likeLogic,array(‘AND’,’OR’,’XOR’))){
$like = array();
foreach ($val[1] as $item){
$like[] = $key.’ ‘.$this->exp[$exp].’ ‘.$this->parseValue($item);
}
$whereStr .= ‘(‘.implode(‘ ‘.$likeLogic.’ ‘,$like).’)’;
}
}else{
$whereStr .= $key.’ ‘.$this->exp[$exp].’ ‘.$this->parseValue($val[1]);
}
}elseif(‘bind’ == $exp ){ // 使用表达式
$whereStr .= $key.’ = :’.$val[1];
}elseif(‘exp’ == $exp ){ // 使用表达式
$whereStr .= $key.’ ‘.$val[1];
}elseif(preg_match(‘/^(notin|not in|in)$/’,$exp)){ // IN 运算
if(isset($val[2]) && ‘exp’==$val[2]) {
$whereStr .= $key.’ ‘.$this->exp[$exp].’ ‘.$val[1];
}else{
if(is_string($val[1])) {
$val[1] = explode(‘,’,$val[1]);
}
$zone = implode(‘,’,$this->parseValue($val[1]));
$whereStr .= $key.’ ‘.$this->exp[$exp].’ (‘.$zone.’)’;
}
}elseif(preg_match(‘/^(notbetween|not between|between)$/’,$exp)){ // BETWEEN运算
$data = is_string($val[1])? explode(‘,’,$val[1]):$val[1];
$whereStr .= $key.’ ‘.$this->exp[$exp].’ ‘.$this->parseValue($data[0]).’ AND ‘.$this->parseValue($data[1]);
}else{
E(L(‘_EXPRESS_ERROR_’).’:’.$val[0]);
}
}else {
$count = count($val);
$rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : ” ;
if(in_array($rule,array(‘AND’,’OR’,’XOR’))) {
$count = $count -1;
}else{
$rule = ‘AND’;
}
for($i=0;$i<$count;$i++) {
$data = is_array($val[$i])?$val[$i][1]:$val[$i];
if(‘exp’==strtolower($val[$i][0])) {
$whereStr .= $key.’ ‘.$data.’ ‘.$rule.’ ‘;
}else{
$whereStr .= $this->parseWhereItem($key,$val[$i]).’ ‘.$rule.’ ‘;
}
}
$whereStr = ‘( ‘.substr($whereStr,0,-4).’ )’;
}
}else {
//对字符串类型字段采用模糊匹配
$likeFields = $this->config[‘db_like_fields’];
if($likeFields && preg_match(‘/^(‘.$likeFields.’)$/i’,$key)) {
$whereStr .= $key.’ LIKE ‘.$this->parseValue(‘%’.$val.’%’);
}else {
$whereStr .= $key.’ = ‘.$this->parseValue($val);
}
}
return $whereStr;
}

/**
* 特殊条件分析
* @access protected
* @param string $key
* @param mixed $val
* @return string
*/
protected function parseThinkWhere($key,$val) {
$whereStr = ”;
switch($key) {
case ‘_string’:
// 字符串模式查询条件
$whereStr = $val;
break;
case ‘_complex’:
// 复合查询条件
$whereStr = substr($this->parseWhere($val),6);
break;
case ‘_query’:
// 字符串模式查询条件
parse_str($val,$where);
if(isset($where[‘_logic’])) {
$op = ‘ ‘.strtoupper($where[‘_logic’]).’ ‘;
unset($where[‘_logic’]);
}else{
$op = ‘ AND ‘;
}
$array = array();
foreach ($where as $field=>$data)
$array[] = $this->parseKey($field).’ = ‘.$this->parseValue($data);
$whereStr = implode($op,$array);
break;
}
return ‘( ‘.$whereStr.’ )’;
}

/**
* limit分析
* @access protected
* @param mixed $lmit
* @return string
*/
protected function parseLimit($limit) {
return !empty($limit)? ‘ LIMIT ‘.$limit.’ ‘:”;
}

/**
* join分析
* @access protected
* @param mixed $join
* @return string
*/
protected function parseJoin($join) {
$joinStr = ”;
if(!empty($join)) {
$joinStr = ‘ ‘.implode(‘ ‘,$join).’ ‘;
}
return $joinStr;
}

/**
* order分析
* @access protected
* @param mixed $order
* @return string
*/
protected function parSEOrder($order) {
if(is_array($order)) {
$array = array();
foreach ($order as $key=>$val){
if(is_numeric($key)) {
$array[] = $this->parseKey($val);
}else{
$array[] = $this->parseKey($key).’ ‘.$val;
}
}
$order = implode(‘,’,$array);
}
return !empty($order)? ‘ ORDER BY ‘.$order:”;
}

/**
* group分析
* @access protected
* @param mixed $group
* @return string
*/
protected function parseGroup($group) {
return !empty($group)? ‘ GROUP BY ‘.$group:”;
}

/**
* having分析
* @access protected
* @param string $having
* @return string
*/
protected function parseHaving($having) {
return !empty($having)? ‘ HAVING ‘.$having:”;
}

/**
* comment分析
* @access protected
* @param string $comment
* @return string
*/
protected function parseComment($comment) {
return !empty($comment)? ‘ /* ‘.$comment.’ */’:”;
}

/**
* distinct分析
* @access protected
* @param mixed $distinct
* @return string
*/
protected function parseDistinct($distinct) {
return !empty($distinct)? ‘ DISTINCT ‘ :”;
}

/**
* union分析
* @access protected
* @param mixed $union
* @return string
*/
protected function parseUnion($union) {
if(empty($union)) return ”;
if(isset($union[‘_all’])) {
$str = ‘UNION ALL ‘;
unset($union[‘_all’]);
}else{
$str = ‘UNION ‘;
}
foreach ($union as $u){
$sql[] = $str.(is_array($u)?$this->buildSelectSql($u):$u);
}
return implode(‘ ‘,$sql);
}

/**
* 参数绑定分析
* @access protected
* @param array $bind
* @return array
*/
protected function parseBind($bind){
$this->bind = array_merge($this->bind,$bind);
}

/**
* index分析,可在操作链中指定需要强制使用的索引
* @access protected
* @param mixed $index
* @return string
*/
protected function parseForce($index) {
if(empty($index)) return ”;
if(is_array($index)) $index = join(“,”, $index);
return sprintf(” FORCE INDEX ( %s ) “, $index);
}

/**
* ON DUPLICATE KEY UPDATE 分析
* @access protected
* @param mixed $duplicate
* @return string
*/
protected function parseDuplicate($duplicate){
return ”;
}

/**
* 插入记录
* @access public
* @param mixed $data 数据
* @param array $options 参数表达式
* @param boolean $replace 是否replace
* @return false | integer
*/
public function insert($data,$options=array(),$replace=false) {
$values = $fields = array();
$this->model = $options[‘model’];
$this->parseBind(!empty($options[‘bind’])?$options[‘bind’]:array());
foreach ($data as $key=>$val){
if(is_array($val) && ‘exp’ == $val[0]){
$fields[] = $this->parseKey($key);
$values[] = $val[1];
}elseif(is_null($val)){
$fields[] = $this->parseKey($key);
$values[] = ‘NULL’;
}elseif(is_scalar($val)) { // 过滤非标量数据
$fields[] = $this->parseKey($key);
if(0===strpos($val,’:’) && in_array($val,array_keys($this->bind))){
$values[] = $this->parseValue($val);
}else{
$name = count($this->bind);
$values[] = ‘:’.$name;
$this->bindParam($name,$val);
}
}
}
// 兼容数字传入方式
$replace= (is_numeric($replace) && $replace>0)?true:$replace;
$sql = (true===$replace?’REPLACE’:’INSERT’).’ INTO ‘.$this->parseTable($options[‘table’]).’ (‘.implode(‘,’, $fields).’) VALUES (‘.implode(‘,’, $values).’)’.$this->parseDuplicate($replace);
$sql .= $this->parseComment(!empty($options[‘comment’])?$options[‘comment’]:”);
return $this->execute($sql,!empty($options[‘fetch_sql’]) ? true : false);
}

/**
* 批量插入记录
* @access public
* @param mixed $dataSet 数据集
* @param array $options 参数表达式
* @param boolean $replace 是否replace
* @return false | integer
*/
public function insertAll($dataSet,$options=array(),$replace=false) {
$values = array();
$this->model = $options[‘model’];
if(!is_array($dataSet[0])) return false;
$this->parseBind(!empty($options[‘bind’])?$options[‘bind’]:array());
$fields = array_map(array($this,’parseKey’),array_keys($dataSet[0]));
foreach ($dataSet as $data){
$value = array();
foreach ($data as $key=>$val){
if(is_array($val) && ‘exp’ == $val[0]){
$value[] = $val[1];
}elseif(is_null($val)){
$value[] = ‘NULL’;
}elseif(is_scalar($val)){
if(0===strpos($val,’:’) && in_array($val,array_keys($this->bind))){
$value[] = $this->parseValue($val);
}else{
$name = count($this->bind);
$value[] = ‘:’.$name;
$this->bindParam($name,$val);
}
}
}
$values[] = ‘SELECT ‘.implode(‘,’, $value);
}
$sql = ‘INSERT INTO ‘.$this->parseTable($options[‘table’]).’ (‘.implode(‘,’, $fields).’) ‘.implode(‘ UNION ALL ‘,$values);
$sql .= $this->parseComment(!empty($options[‘comment’])?$options[‘comment’]:”);
return $this->execute($sql,!empty($options[‘fetch_sql’]) ? true : false);
}

/**
* 通过Select方式插入记录
* @access public
* @param string $fields 要插入的数据表字段名
* @param string $table 要插入的数据表名
* @param array $option 查询数据参数
* @return false | integer
*/
public function selectInsert($fields,$table,$options=array()) {
$this->model = $options[‘model’];
$this->parseBind(!empty($options[‘bind’])?$options[‘bind’]:array());
if(is_string($fields)) $fields = explode(‘,’,$fields);
array_walk($fields, array($this, ‘parseKey’));
$sql = ‘INSERT INTO ‘.$this->parseTable($table).’ (‘.implode(‘,’, $fields).’) ‘;
$sql .= $this->buildSelectSql($options);
return $this->execute($sql,!empty($options[‘fetch_sql’]) ? true : false);
}

/**
* 更新记录
* @access public
* @param mixed $data 数据
* @param array $options 表达式
* @return false | integer
*/
public function update($data,$options) {
$this->model = $options[‘model’];
$this->parseBind(!empty($options[‘bind’])?$options[‘bind’]:array());
$table = $this->parseTable($options[‘table’]);
$sql = ‘UPDATE ‘ . $table . $this->parseSet($data);
if(strpos($table,’,’)){// 多表更新支持JOIN操作
$sql .= $this->parseJoin(!empty($options[‘join’])?$options[‘join’]:”);
}
$sql .= $this->parseWhere(!empty($options[‘where’])?$options[‘where’]:”);
if(!strpos($table,’,’)){
// 单表更新支持order和lmit
$sql .= $this->parSEOrder(!empty($options[‘order’])?$options[‘order’]:”)
.$this->parseLimit(!empty($options[‘limit’])?$options[‘limit’]:”);
}
$sql .= $this->parseComment(!empty($options[‘comment’])?$options[‘comment’]:”);
return $this->execute($sql,!empty($options[‘fetch_sql’]) ? true : false);
}

/**
* 删除记录
* @access public
* @param array $options 表达式
* @return false | integer
*/
public function delete($options=array()) {
$this->model = $options[‘model’];
$this->parseBind(!empty($options[‘bind’])?$options[‘bind’]:array());
$table = $this->parseTable($options[‘table’]);
$sql = ‘DELETE FROM ‘.$table;
if(strpos($table,’,’)){// 多表删除支持USING和JOIN操作
if(!empty($options[‘using’])){
$sql .= ‘ USING ‘.$this->parseTable($options[‘using’]).’ ‘;
}
$sql .= $this->parseJoin(!empty($options[‘join’])?$options[‘join’]:”);
}
$sql .= $this->parseWhere(!empty($options[‘where’])?$options[‘where’]:”);
if(!strpos($table,’,’)){
// 单表删除支持order和limit
$sql .= $this->parSEOrder(!empty($options[‘order’])?$options[‘order’]:”)
.$this->parseLimit(!empty($options[‘limit’])?$options[‘limit’]:”);
}
$sql .= $this->parseComment(!empty($options[‘comment’])?$options[‘comment’]:”);
return $this->execute($sql,!empty($options[‘fetch_sql’]) ? true : false);
}

/**
* 查找记录
* @access public
* @param array $options 表达式
* @return mixed
*/
public function select($options=array()) {
$this->model = $options[‘model’];
$this->parseBind(!empty($options[‘bind’])?$options[‘bind’]:array());
$sql = $this->buildSelectSql($options);
$result = $this->query($sql,!empty($options[‘fetch_sql’]) ? true : false);
return $result;
}

/**
* 生成查询SQL
* @access public
* @param array $options 表达式
* @return string
*/
public function buildSelectSql($options=array()) {
if(isset($options[‘page’])) {
// 根据页数计算limit
list($page,$listRows) = $options[‘page’];
$page = $page>0 ? $page : 1;
$listRows= $listRows>0 ? $listRows : (is_numeric($options[‘limit’])?$options[‘limit’]:20);
$offset = $listRows*($page-1);
$options[‘limit’] = $offset.’,’.$listRows;
}
$sql = $this->parseSql($this->selectSql,$options);
return $sql;
}

/**
* 替换SQL语句中表达式
* @access public
* @param array $options 表达式
* @return string
*/
public function parseSql($sql,$options=array()){
$sql = str_replace(
array(‘%TABLE%’,’%DISTINCT%’,’%FIELD%’,’%JOIN%’,’%WHERE%’,’%GROUP%’,’%HAVING%’,’%ORDER%’,’%LIMIT%’,’%UNION%’,’%LOCK%’,’%COMMENT%’,’%FORCE%’),
array(
$this->parseTable($options[‘table’]),
$this->parseDistinct(isset($options[‘distinct’])?$options[‘distinct’]:false),
$this->parseField(!empty($options[‘field’])?$options[‘field’]:’*’),
$this->parseJoin(!empty($options[‘join’])?$options[‘join’]:”),
$this->parseWhere(!empty($options[‘where’])?$options[‘where’]:”),
$this->parseGroup(!empty($options[‘group’])?$options[‘group’]:”),
$this->parseHaving(!empty($options[‘having’])?$options[‘having’]:”),
$this->parseOrder(!empty($options[‘order’])?$options[‘order’]:”),
$this->parseLimit(!empty($options[‘limit’])?$options[‘limit’]:”),
$this->parseUnion(!empty($options[‘union’])?$options[‘union’]:”),
$this->parseLock(isset($options[‘lock’])?$options[‘lock’]:false),
$this->parseComment(!empty($options[‘comment’])?$options[‘comment’]:”),
$this->parseForce(!empty($options[‘force’])?$options[‘force’]:”)
),$sql);
return $sql;
}

/**
* 获取最近一次查询的sql语句
* @param string $model 模型名
* @access public
* @return string
*/
public function getLastSql($model=”) {
return $model?$this->modelSql[$model]:$this->queryStr;
}

/**
* 获取最近插入的ID
* @access public
* @return string
*/
public function getLastInsID() {
return $this->lastInsID;
}

/**
* 获取最近的错误信息
* @access public
* @return string
*/
public function getError() {
return $this->error;
}

/**
* SQL指令安全过滤
* @access public
* @param string $str SQL字符串
* @return string
*/
public function escapeString($str) {
return addslashes($str);
}

/**
* 设置当前操作模型
* @access public
* @param string $model 模型名
* @return void
*/
public function setModel($model){
$this->model = $model;
}

/**
* 数据库调试 记录当前SQL
* @access protected
* @param boolean $start 调试开始标记 true 开始 false 结束
*/
protected function debug($start) {
if($this->config[‘debug’]) {// 开启数据库调试模式
if($start) {
G(‘queryStartTime’);
}else{
$this->modelSql[$this->model] = $this->queryStr;
//$this->model = ‘_think_’;
// 记录操作结束时间
G(‘queryEndTime’);
trace($this->queryStr.’ [ RunTime:’.G(‘queryStartTime’,’queryEndTime’).’s ]’,”,’SQL’);
}
}
}

/**
* 初始化数据库连接
* @access protected
* @param boolean $master 主服务器
* @return void
*/
protected function initConnect($master=true) {
if(!empty($this->config[‘deploy’]))
// 采用分布式数据库
$this->_linkID = $this->multiConnect($master);
else
// 默认单数据库
if ( !$this->_linkID ) $this->_linkID = $this->connect();
}

/**
* 连接分布式服务器
* @access protected
* @param boolean $master 主服务器
* @return void
*/
protected function multiConnect($master=false) {
// 分布式数据库配置解析
$_config[‘username’] = explode(‘,’,$this->config[‘username’]);
$_config[‘password’] = explode(‘,’,$this->config[‘password’]);
$_config[‘hostname’] = explode(‘,’,$this->config[‘hostname’]);
$_config[‘hostport’] = explode(‘,’,$this->config[‘hostport’]);
$_config[‘database’] = explode(‘,’,$this->config[‘database’]);
$_config[‘dsn’] = explode(‘,’,$this->config[‘dsn’]);
$_config[‘charset’] = explode(‘,’,$this->config[‘charset’]);

$m = floor(mt_rand(0,$this->config[‘master_num’]-1));
// 数据库读写是否分离
if($this->config[‘rw_separate’]){
// 主从式采用读写分离
if($master)
// 主服务器写入
$r = $m;
else{
if(is_numeric($this->config[‘slave_no’])) {// 指定服务器读
$r = $this->config[‘slave_no’];
}else{
// 读操作连接从服务器
$r = floor(mt_rand($this->config[‘master_num’],count($_config[‘hostname’])-1)); // 每次随机连接的数据库
}
}
}else{
// 读写操作不区分服务器
$r = floor(mt_rand(0,count($_config[‘hostname’])-1)); // 每次随机连接的数据库
}

if($m != $r ){
$db_master = array(
‘username’ => isset($_config[‘username’][$m])?$_config[‘username’][$m]:$_config[‘username’][0],
‘password’ => isset($_config[‘password’][$m])?$_config[‘password’][$m]:$_config[‘password’][0],
‘hostname’ => isset($_config[‘hostname’][$m])?$_config[‘hostname’][$m]:$_config[‘hostname’][0],
‘hostport’ => isset($_config[‘hostport’][$m])?$_config[‘hostport’][$m]:$_config[‘hostport’][0],
‘database’ => isset($_config[‘database’][$m])?$_config[‘database’][$m]:$_config[‘database’][0],
‘dsn’ => isset($_config[‘dsn’][$m])?$_config[‘dsn’][$m]:$_config[‘dsn’][0],
‘charset’ => isset($_config[‘charset’][$m])?$_config[‘charset’][$m]:$_config[‘charset’][0],
);
}
$db_config = array(
‘username’ => isset($_config[‘username’][$r])?$_config[‘username’][$r]:$_config[‘username’][0],
‘password’ => isset($_config[‘password’][$r])?$_config[‘password’][$r]:$_config[‘password’][0],
‘hostname’ => isset($_config[‘hostname’][$r])?$_config[‘hostname’][$r]:$_config[‘hostname’][0],
‘hostport’ => isset($_config[‘hostport’][$r])?$_config[‘hostport’][$r]:$_config[‘hostport’][0],
‘database’ => isset($_config[‘database’][$r])?$_config[‘database’][$r]:$_config[‘database’][0],
‘dsn’ => isset($_config[‘dsn’][$r])?$_config[‘dsn’][$r]:$_config[‘dsn’][0],
‘charset’ => isset($_config[‘charset’][$r])?$_config[‘charset’][$r]:$_config[‘charset’][0],
);
return $this->connect($db_config,$r,$r == $m ? false : $db_master);
}

/**
* 是否断线
* 2017-8-24 09:12:40 摘自TP5 DB/Connection.php
* 修改传入的参数 来适配TP3.2
* @access protected
* @param String $error 返回的错误信息
* @return bool
*/
protected function isBreak($error)
{
$info = [
‘server has gone away’,
‘no connection to the server’,
‘Lost connection’,
‘is dead or not enabled’,
‘Error while sending’,
‘decryption failed or bad record mac’,
‘server closed the connection unexpectedly’,
‘SSL connection has been closed unexpectedly’,
‘Error writing data to the connection’,
‘Resource deadlock avoided’,
];

foreach ($info as $msg) {
if (false !== stripos($error, $msg)) {
return true;
}
}
return false;
}

/**
* 析构方法
* @access public
*/
public function __destruct() {
// 释放查询
if ($this->PDOStatement){
$this->free();
}
// 关闭连接
$this->close();
}
}
————————————————
版权声明:本文为CSDN博主「一个不靠谱的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013705066/article/details/77679827

Swoole整合ThinkPHP3.2系列教程三_一个不靠谱的程序员-CSDN博客

mikel阅读(1172)

来源: Swoole整合ThinkPHP3.2系列教程三_一个不靠谱的程序员-CSDN博客

使用说明:
作为管理员:
0.安装swoole扩展
swoole项目已收录到PHP官方扩展库,直接使用:
– pecl install swoole

1.启动swoole服务
在cli模式下,进入Swoole目录,执行以下命令行:
– php swoole.php start

2.柔性重启swoole服务
用于SwooleController框架里的代码更新时,执行以下命令行重新加载代码:
– php swoole.php reload

3.关闭swoole服务
– 直接用kill命令杀死swoole主进程即可(除非特殊情况),一般使用柔性重启就会重启worker进程

4.常用的一些命令:
– lsof -i:9501 查看端口的使用情况
– ps -aux|grep swoole 查看swoole的进程(通常是有一共会创建2 + n + m个进程,其中n为Worker进程数,m为TaskWorker进程数,2为一个Master进程和一个Manager进程,需要修改worker进程和task进程的数量,修改Server.php里的option)

作为开发者:
尽管放心大胆地把你的耗时操作的业务逻辑代码写到Cli模块下的SwooleController里。
当浏览器需要请求耗时任务之前,必须去业务服务器swooleLog表里备案一下,即获取swoole_log表里的ID,携带这个ID去请求swoole服务,否则swoole忽略此次请求,但并不会关闭连接。
我们约定请求耗时操作或者任务的过程按照如下规则进行:
1. 浏览器请求业务服务器,备案此次操作,主要包括耗时操作的function名字以及参数存入业务服务器那边的SwooleLog表(注意重复任务不要多次提交,记得排重)
2. 业务服务器返回浏览器swoole服务的连接地址,以及备案的log_id
3. 浏览器收到业务服务器的返回值以后,创建websocket客户端:
ws = new WebSocket(config.server+’?uid=1′);//请求的连接地址使用get请求加上uid
请求swoole服务执行该任务我们约定请求swoole时的数据格式按如下规则统一:
{
cmd:”test” ,//SwooleController里必须有对应的方法名称,
args:{
id: 1,//SwooleLog表里的id
…//其他需要携带的参数

}
}
4. 处理websocket收到消息时的回调
wx.onmessage=function(e){
var message = JSON.parse(e.data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
swoole服务的返回值
code info
202 任务已经提交,请等待服务器计算数据!
404 任务提交失败,请联系管理员进行处理!
200 任务执行完毕!
附上一份JS版的websocket代码案例(暂不支持低版本IE,近期会想办法解决)
function swooleClient(){
var config = {
‘server’ : ‘ws://192.168.0.166:9501′,
};

var ws = {};

$(document).ready(function () {
ws = new WebSocket(config.server+’?uid=1′);
listenEvent();
});

function listenEvent() {
/**
* 连接建立时触发
*/
ws.onopen = function (e) {
//连接成功
console.log(“connect webim server success.”);
//发送登录信息

ws.send(JSON.stringify({
cmd : ‘test’,
args : {
id : 3,
name: ‘哈哈’
}
}));
};

//有消息到来时触发
ws.onmessage = function (e) {
console.log(e);
var message = JSON.parse(e.data);
$(document.body).html(“<h1 style=’text-align: center’>”+message.info+”</h1>”);
};

/**
* 连接关闭事件
*/
ws.onclose = function (e) {
$(document.body).html(“<h1 style=’text-align: center’>连接已断开,请刷新页面重新登录。</h1>”);
};

/**
* 异常事件
*/
ws.onerror = function (e) {
$(document.body).html(“<h1 style=’text-align: center’>服务器”+
“: 拒绝了连接. 请检查服务器是否启动. </h1>”);
console.log(“onerror: ” + e.data);
};
}
————————————————
版权声明:本文为CSDN博主「一个不靠谱的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013705066/article/details/77679670

Swoole整合ThinkPHP3.2系列教程二_一个不靠谱的程序员-CSDN博客

mikel阅读(1094)

来源: Swoole整合ThinkPHP3.2系列教程二_一个不靠谱的程序员-CSDN博客

swoole和ThinkPHP的整合
先上一份在我们系统内部的swoole整合的架构预览

├─Application 应用目录
│ │
│ ├─Cli Cli模块
│ │ ├─Controller 制器类
│ │ │ ├─StartController TP框架加载时默认加载的控制器
│ │ │ ├─SwooleController 我们的业务逻辑写在这里面
│ │ └─ …
├─Swoole
│ │
│ ├─log swoole运行日志
│ ├─Server.php swoole的服务代码
│ ├─swoole.php 用于cli模式下启动和软重启swoole服务
1
2
3
4
5
6
7
8
9
10
11
12
最早测试的时候不是这样搭建的,而是把Server.php里的关于服务的东西放在了TP控制器里,在cli模式下调用

php index.php(入口文件) Swoole/start (控制器/方法)
1
这种模式是把swoole套在了TP里运行,也是可行的,但是总觉得启动个swoole服务为毛还要告诉TP一声?

我们想要的一种模式是业务服务器独立运行,swoole服务作为守护进程常驻内存,当浏览器需要运行比较耗时的操作时,需要跟swoole服务进程建立长连接,当耗时的任务执行完毕时会通知浏览器已经完毕。

整个过程下来,swoole服务和业务服务不应该耦合在一起的。最完美的状态是swoole独立运行(使用swoole框架重新写耗时操作的业务逻辑代码),独立连接数据库。当浏览器执行这些任务时,就不再找业务服务器了,而是直接跟swoole服务打交道。

可是我们没有那么多的时间和精力,我们只能在swoole服务的进程里调用TP框架的东西,来执行我们在TP里写的代码。最后参考了网上的一些方案,选择了这样与TP结合。

代码逻辑
/Swoole/Server.php
关于swoole服务的一些配置,比如监听地址,端口号(默认为9501),和一些基础配置

一些回调函数,里面的代码注释很完整,可以直接看代码

<?php
// +———————————————————————-
// 2017-8-23 09:03:40
// 此次修改为只作为websocket的服务端
// +———————————————————————-
class Server{
protected $swoole;
// 监听所有地址
protected $host = ‘0.0.0.0’;
// 监听 9501 端口
protected $port = 9501;
// 配置项
protected $option = [
//设置启动的worker进程数
‘worker_num’ => 2,
//task进程的数量
‘task_worker_num’ => 4,
//指定task任务使用消息队列模式,3表示完全争抢模式,task进程会争抢队列,无法使用定向投递
‘task_ipc_mode’ => 3,
//task进程的最大任务数
‘task_max_request’ => 1000,
// 守护进程化
‘daemonize’ => false,
// 监听队列的长度
‘backlog’ => 128,
//绑定uid时用
‘dispatch_mode’ => 5,
//设置日志路径
‘log_file’ => SWOOLE_LOG_PATH,
];
protected function init(){
//异步非阻塞多进程的websocket
$this->swoole = new swoole_websocket_server($this->host, $this->port);
$eventList = [‘Open’, ‘Message’, ‘Close’, ‘HandShake’ , ‘Task’, ‘Finish’ , ‘WorkerStart’ , ‘Receive’];

// 设置参数
if (!empty($this->option)) {
$this->swoole->set($this->option);
}
// 设置回调
foreach ($eventList as $event) {
if (method_exists($this, ‘on’ . $event)) {
$this->swoole->on($event, [$this, ‘on’ . $event]);
}
}
}
public function start(){
$this->init();
$this->swoole->start();
}
public function getHost(){
return $this->host;
}
public function getPort(){
return $this->port;
}
/**
* [onOpen 建立连接时的回调函数]
* @method onOpen
* @param swoole_server $serv [description]
* @param swoole_http_request $req [description]
* @return [type] [description]
*/
public function onOpen(swoole_server $serv, swoole_http_request $req){
//将连接绑定到uid上面
if(!empty($req->get)&&$req->get[‘uid’]){
$serv->bind($req->fd , $req->get[‘uid’]);
}
}
/**
* [onMessage 接收到socket客户端发送数据的回调函数]
* @method onMessage
* @param swoole_server $serv [description]
* @param swoole_websocket_frame $frame [description]
* @return [type] [description]
*/
public function onMessage(swoole_server $serv, swoole_websocket_frame $frame){
//收到数据时处理数据
//根据收到的cmd名字去调用指定的方法
$receive = json_decode($frame->data,true);
//为了避免数据处理量过大阻塞当前进程,导致服务响应变慢,我们把耗时的操作扔到TaskWorker进程池中执行
//当要执行的方法存在并且已经在swoole_log表里备案过的可以丢到task进程池
$swooleLog = D(‘SwooleLog’);
$swooleController = A(‘Swoole’);
//$receive[‘args’][‘id’]是业务服务器那边数据库SwooleLog表里的id
if (method_exists($swooleController, $receive[‘cmd’]) && $receive[‘args’][‘id’]) {
$task_id = $serv->task($receive);
//记录task_id信息
//…其他你想要做的精细化处理
}else{
if($receive[‘cmd’] === ‘reload’){
//利用Swoole提供的柔性终止/重启的机制
$rs = $serv -> reload();
$serv->push($frame->fd , $rs);
}elseif($receive[‘args’][‘id’]){
$returnData = $this->_returnStr(‘submit_error’);
$serv->push($frame->fd , $returnData);
}elseif(method_exists($this, $receive[‘cmd’])){
$this->{$receive[‘cmd’]}($serv , $frame);
}
}
}
/**
* [onTask 在task_worker进程池内被调用时的回调函数]
* @method onTask
* @param swoole_server $serv [description]
* @param int $task_id [description]
* @param int $src_worker_id [description]
* @param mixed $data [description]
* @return [type] [description]
*/
public function onTask(swoole_server $serv, $task_id, $src_worker_id, $data){
//记录任务开始执行的时间
//…自己发挥 我们项目的业务逻辑部分已经删除
//
try{
$swooleController = A(‘Swoole’);
$rs = $swooleController->{$data[‘cmd’]}($data[‘args’]);
return json_encode([‘id’=>$data[‘args’][‘id’] , ‘status’ => true , ‘other’ => $rs]);
}catch(\Exception $e){
return json_encode([‘id’=>$data[‘args’][‘id’] , ‘status’ => false , ‘other’ => $e->getMessage()]);
}
}
/**
* [onFinish 任务执行完毕时的回调函数]
* @method onFinish
* @param swoole_server $serv [description]
* @param int $task_id [description]
* @param string $data [description]
* @return [type] [description]
*/
public function onFinish(swoole_server $serv, $task_id, string $data){
$rs = json_decode($data , true);
$swooleLog = D(‘SwooleLog’);
//…
//通知用户该任务已经执行完毕了 可以来查看数据了
//检查客户端链接是否存在 如果存在的话发送消息
$returnData = $this->_returnStr(‘task_excute_success’);
foreach($serv->connections as $fd){
$conn = $serv->connection_info($fd);
//根据uid判断当前连接是活跃的
//这里是业务服务器那边能取到的用户信息,这里已经删除,保护我们的项目
$serv->push($fd , $returnData);
}
}
/**
* [onWorkerStart 为了应用能热加载 把框架的东西放到worker进程启动]
* @method onWorkerStart
* @param swoole_server $server [description]
* @param [type] $worker_id [description]
* @return [type] [description]
*/
public function onWorkerStart(swoole_server $server, $worker_id){
// 定义应用目录
define(‘APP_PATH’, ‘../Application/’);
// 定义应用模式
define(‘APP_Mode’, ‘cli’);
define(‘BIND_MODULE’,’Cli’);
// 引入ThinkPHP入口文件
require ‘../ThinkPHP/ThinkPHP.php’;
}
/**
* [_returnStr 返回给客户端的数据]
* @method _returnStr
* @param [type] $type [description]
* @return [type] [description]
*/
private function _returnStr($type){
switch ($type) {
case ‘submit_success’:
$returnData = [
‘code’ => 202,
‘info’ => ‘任务已经提交,请等待服务器计算数据!’
];
break;
case ‘submit_error’:
$returnData = [
‘code’ => 404,
‘info’ => ‘任务提交失败,请联系管理员进行处理!’
];
break;
case ‘task_excute_success’:
$returnData = [
‘code’ => 200,
‘info’ => ‘任务执行完毕!’
];
break;
}
return json_encode($returnData);
}
/**
* [clients 获取所有的客户端连接信息,返回我们的uid]
* @method clients
* @return [type] [description]
*/
public function clients(swoole_server $serv , swoole_websocket_frame $frame){
//…
$serv->push($frame->fd , json_encode($serv->connections));
}
public function __call($method, $args){
call_user_func_array([$this->swoole, $method], $args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/Swoole/swoole.php
是一个面向cli编程的代码,主要提供了start和reload两个命令

#!/bin/env php
<?php
/**
* 定义项目根目录&swoole-task pid
*/
define(‘SWOOLE_PATH’, __DIR__);
define(‘SWOOLE_LOG_PATH’, SWOOLE_PATH . DIRECTORY_SEPARATOR . ‘log’ . DIRECTORY_SEPARATOR . ‘Swoole’ . ‘.log’);

/**
* 加载 swoole server
*/
include SWOOLE_PATH . DIRECTORY_SEPARATOR . ‘Server.php’;

//提示帮助信息
if ($argc != 2 || in_array($argv[1], array(‘–help’, ‘-help’, ‘-h’, ‘-?’))) {
echo <<<HELP
用法:php swoole.php 选项 … 可选的命令[start|reload|list]

–help|-help|-h|-? 显示本帮助说明

选项说明
start, 启动swoole服务[默认监测9501端口]
reload, 柔性重启所有workder进程
list, 查看当前所有连接的客户端数

HELP;
exit;
}

//执行命令行选项
switch($argv[1]){
case ‘start’:
start();
break;
case ‘reload’:
reload();
break;
case ‘list’:
clients();
break;
default:
exit(“输入命令有误 : {$argv[1]}, 请查看帮助文档\n”);
break;
}

//启动swoole服务
function start(){
$server = new Server();
$server->start();
}

//柔性重启swoole服务
function reload(){
echo “正在柔性重启swoole的worker进程…” . PHP_EOL;
try {
$server = new Server();
$port = $server->getPort();
$host = ‘127.0.0.1’;
$cli = new swoole_http_client($host, $port);
$cli->set([‘websocket_mask’ => true]);
$cli->on(‘message’, function ($_cli, $frame) {
if($frame->data){
exit(‘swoole重启成功!’.PHP_EOL);
}
});
$cli->upgrade(‘/’, function ($cli) {
$cli->push(json_encode([‘cmd’ => ‘reload’]));
});
} catch (Exception $e) {
exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
}
}

function clients(){
try {
$server = new Server();
$port = $server->getPort();
$host = ‘127.0.0.1’;
$cli = new swoole_http_client($host, $port);
$cli->set([‘websocket_mask’ => true]);
$cli->on(‘message’, function ($_cli, $frame) {
$users = json_decode($frame->data , true);
if(empty($users)){
echo ‘当前没有客户端连接’.PHP_EOL;
}else{
foreach($users as $user){
echo ‘—‘.$user.PHP_EOL;
}
}
exit();
});
$cli->upgrade(‘/’, function ($cli) {
$cli->push(json_encode([‘cmd’ => ‘clients’]));

});
} catch (Exception $e) {
exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
存在的问题
0.以上的代码是从项目里摘出来的,大体思路还在,业务部分代码删除了。不保证正常运行,但是问题不大,如果报错了聪明的你一定一看就知道是什么错误。

1.由于我们将TP框架的东西放在workstart回调函数里启动,这里只要启动worker进程和task进程,都会加载一次TP框架的东西到内存里,对内存的占用问题还需要仔细研究。

2.目前还不支持那些不支持HTML5 WebSocket的浏览器 我会尽快想办法解决。用flash模拟socket请求的方法没有试成功,近期重新再搞一波。
————————————————
版权声明:本文为CSDN博主「一个不靠谱的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013705066/article/details/77679233

thinkPHP3.2.3集成swoole扩展 - 林中侠客 - 博客园

mikel阅读(815)

来源: thinkPHP3.2.3集成swoole扩展 – 林中侠客 – 博客园

swoole.php

复制代码
  1 #!/bin/env php
  2 <?php
  3 /**
  4  * 默认时区定义
  5  */
  6 date_default_timezone_set('Asia/Shanghai');
  7 
  8 /**
  9  * 设置错误报告模式
 10  */
 11 error_reporting(0);
 12 
 13 /**
 14  * 设置默认区域
 15  */
 16 setlocale(LC_ALL, "zh_CN.utf-8");
 17 
 18 /**
 19  * 检测 PDO_MYSQL
 20  */
 21 if (!extension_loaded('pdo_mysql')) {
 22     exit('PDO_MYSQL extension is not installed' . PHP_EOL);
 23 }
 24 /**
 25  * 检查exec 函数是否启用
 26  */
 27 if (!function_exists('exec')) {
 28     exit('exec function is disabled' . PHP_EOL);
 29 }
 30 /**
 31  * 检查命令 lsof 命令是否存在
 32  */
 33 exec("whereis lsof", $out);
 34 if ($out[0] == 'lsof:') {
 35     exit('lsof is not found' . PHP_EOL);
 36 }
 37 /**
 38  * 定义项目根目录&swoole-task pid
 39  */
 40 define('SWOOLE_PATH', __DIR__);
 41 define('SWOOLE_TASK_PID_PATH', SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . 'swoole-task.pid');
 42 define('SWOOLE_TASK_NAME_PRE', 'swooleServ');
 43 
 44 /**
 45  * 加载 swoole server
 46  */
 47 include SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR . 'SwooleServer.php';
 48 
 49 function portBind($port) {
 50     $ret = [];
 51     $cmd = "lsof -i :{$port}|awk '$1 != \"COMMAND\"  {print $1, $2, $9}'";
 52     exec($cmd, $out);
 53     if ($out) {
 54         foreach ($out as $v) {
 55             $a = explode(' ', $v);
 56             list($ip, $p) = explode(':', $a[2]);
 57             $ret[$a[1]] = [
 58                 'cmd' => $a[0],
 59                 'ip' => $ip,
 60                 'port' => $p,
 61             ];
 62         }
 63     }
 64 
 65     return $ret;
 66 }
 67 
 68 function servStart($host, $port, $daemon, $name) {
 69     echo "正在启动 swoole-task 服务" . PHP_EOL;
 70     if (!is_writable(dirname(SWOOLE_TASK_PID_PATH))) {
 71         exit("swoole-task-pid文件需要目录的写入权限:" . dirname(SWOOLE_TASK_PID_PATH) . PHP_EOL);
 72     }
 73     if (file_exists(SWOOLE_TASK_PID_PATH)) {
 74         $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
 75         $cmd = "ps ax | awk '{ print $1 }' | grep -e \"^{$pid[0]}$\"";
 76         exec($cmd, $out);
 77         if (!empty($out)) {
 78             exit("swoole-task pid文件 " . SWOOLE_TASK_PID_PATH . " 存在,swoole-task 服务器已经启动,进程pid为:{$pid[0]}" . PHP_EOL);
 79         } else {
 80             echo "警告:swoole-task pid文件 " . SWOOLE_TASK_PID_PATH . " 存在,可能swoole-task服务上次异常退出(非守护模式ctrl+c终止造成是最大可能)" . PHP_EOL;
 81             unlink(SWOOLE_TASK_PID_PATH);
 82         }
 83     }
 84     $bind = portBind($port);
 85     if ($bind) {
 86         foreach ($bind as $k => $v) {
 87             if ($v['ip'] == '*' || $v['ip'] == $host) {
 88                 exit("端口已经被占用 {$host}:$port, 占用端口进程ID {$k}" . PHP_EOL);
 89             }
 90         }
 91     }
 92     unset($_SERVER['argv']);
 93     $_SERVER['argc'] = 0;
 94     echo "启动 swoole-task 服务成功" . PHP_EOL;
 95     $server = new SwooleServer('127.0.0.1', 9501);
 96     $server->run();
 97     //确保服务器启动后swoole-task-pid文件必须生成
 98     /*if (!empty(portBind($port)) && !file_exists(SWOOLE_TASK_PID_PATH)) {
 99         exit("swoole-task pid文件生成失败( " . SWOOLE_TASK_PID_PATH . ") ,请手动关闭当前启动的swoole-task服务检查原因" . PHP_EOL);
100     }*/
101 }
102 
103 function servStop($host, $port, $isRestart = false) {
104     echo "正在停止 swoole-task 服务" . PHP_EOL;
105     if (!file_exists(SWOOLE_TASK_PID_PATH)) {
106         exit('swoole-task-pid文件:' . SWOOLE_TASK_PID_PATH . '不存在' . PHP_EOL);
107     }
108     $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
109     $bind = portBind($port);
110     if (empty($bind) || !isset($bind[$pid[0]])) {
111         exit("指定端口占用进程不存在 port:{$port}, pid:{$pid[0]}" . PHP_EOL);
112     }
113     $cmd = "kill {$pid[0]}";
114     exec($cmd);
115     do {
116         $out = [];
117         $c = "ps ax | awk '{ print $1 }' | grep -e \"^{$pid[0]}$\"";
118         exec($c, $out);
119         if (empty($out)) {
120             break;
121         }
122     } while (true);
123     //确保停止服务后swoole-task-pid文件被删除
124     if (file_exists(SWOOLE_TASK_PID_PATH)) {
125         unlink(SWOOLE_TASK_PID_PATH);
126     }
127     $msg = "执行命令 {$cmd} 成功,端口 {$host}:{$port} 进程结束" . PHP_EOL;
128     if ($isRestart) {
129         echo $msg;
130     } else {
131         exit($msg);
132     }
133 }
134 
135 function servReload($host, $port, $isRestart = false) {
136     echo "正在平滑重启 swoole-task 服务" . PHP_EOL;
137     try {
138         $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
139         $ret = $client->connect($host, $port);
140         if (empty($ret)) {
141             exit("{$host}:{$port} swoole-task服务不存在或者已经关闭" . PHP_EOL);
142         } else {
143             $client->send(json_encode(array('action' => 'reload')));
144         }
145         $msg = "执行命令reload成功,端口 {$host}:{$port} 进程重启" . PHP_EOL;
146         if ($isRestart) {
147             echo $msg;
148         } else {
149             exit($msg);
150         }
151     } catch (Exception $e) {
152         exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
153     }
154 }
155 
156 function servClose($host, $port, $isRestart = false) {
157     echo "正在关闭 swoole-task 服务" . PHP_EOL;
158     try {
159         $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
160         $ret = $client->connect($host, $port);
161         if (empty($ret)) {
162             exit("{$host}:{$port} swoole-task服务不存在或者已经关闭" . PHP_EOL);
163         } else {
164             $client->send(json_encode(array('action' => 'close')));
165         }
166         //确保停止服务后swoole-task-pid文件被删除
167         if (file_exists(SWOOLE_TASK_PID_PATH)) {
168             unlink(SWOOLE_TASK_PID_PATH);
169         }
170         $msg = "执行命令close成功,端口 {$host}:{$port} 进程结束" . PHP_EOL;
171         if ($isRestart) {
172             echo $msg;
173         } else {
174             exit($msg);
175         }
176     } catch (\Exception $e) {
177         exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
178     }
179 }
180 
181 function servStatus($host, $port) {
182     echo "swoole-task {$host}:{$port} 运行状态" . PHP_EOL;
183     $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
184     $bind = portBind($port);
185     if (empty($bind) || !isset($bind[$pid[0]])) {
186         exit("指定端口占用进程不存在 port:{$port}, pid:{$pid[0]}" . PHP_EOL);
187     }
188     $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
189     $ret = $client->connect($host, $port);
190     if (empty($ret)) {
191         exit("{$host}:{$port} swoole-task服务不存在或者已经停止" . PHP_EOL);
192     } else {
193         $client->send(json_encode(array('action' => 'status')));
194         $out = $client->recv();
195         $a = json_decode($out);
196         $b = array(
197             'start_time' => '服务器启动的时间',
198             'connection_num' => '当前连接的数量',
199             'accept_count' => '接受的连接数量',
200             'close_count' => '关闭的连接数量',
201             'tasking_num' => '当前正在排队的任务数',
202             'request_count' => '请求的连接数量',
203             'worker_request_count' => 'worker连接数量',
204             'task_process_num' => '任务进程数量'
205         );
206         foreach ($a as $k1 => $v1) {
207             if ($k1 == 'start_time') {
208                 $v1 = date("Y-m-d H:i:s", $v1);
209             }
210             echo $b[$k1] . ":\t$v1" . PHP_EOL;
211         }
212     }
213     exit();
214 }
215 
216 function servList() {
217     echo "本机运行的swoole-task服务进程" . PHP_EOL;
218     $cmd = "ps aux|grep " . SWOOLE_TASK_NAME_PRE . "|grep -v grep|awk '{print $1, $2, $6, $8, $9, $11}'";
219     exec($cmd, $out);
220     if (empty($out)) {
221         exit("没有发现正在运行的swoole-task服务" . PHP_EOL);
222     }
223     echo "USER PID RSS(kb) STAT START COMMAND" . PHP_EOL;
224     foreach ($out as $v) {
225         echo $v . PHP_EOL;
226     }
227     exit();
228 }
229 
230 //可执行命令
231 $cmds = [
232     'start',
233     'stop',
234     'restart',
235     'reload',
236     'close',
237     'status',
238     'list',
239 ];
240 $shortopts = "dDh:p:n:";
241 $longopts = [
242     'help',
243     'daemon',
244     'nondaemon',
245     'host:',
246     'port:',
247     'name:',
248 ];
249 $opts = getopt($shortopts, $longopts);
250 
251 if (isset($opts['help']) || $argc < 2) {
252     echo <<<HELP
253 用法:php swoole.php 选项 ... 命令[start|stop|restart|reload|close|status|list]
254 管理swoole-task服务,确保系统 lsof 命令有效
255 如果不指定监听host或者port,使用配置参数
256 
257 参数说明
258     --help  显示本帮助说明
259     -d, --daemon    指定此参数,以守护进程模式运行,不指定则读取配置文件值
260     -D, --nondaemon 指定此参数,以非守护进程模式运行,不指定则读取配置文件值
261     -h, --host  指定监听ip,例如 php swoole.php -h127.0.0.1
262     -p, --port  指定监听端口port, 例如 php swoole.php -h127.0.0.1 -p9520
263     -n, --name  指定服务进程名称,例如 php swoole.php -ntest start, 则进程名称为SWOOLE_TASK_NAME_PRE-name
264 启动swoole-task 如果不指定 host和port,读取默认配置
265 强制关闭swoole-task 必须指定port,没有指定host,关闭的监听端口是  *:port,指定了host,关闭 host:port端口
266 平滑关闭swoole-task 必须指定port,没有指定host,关闭的监听端口是  *:port,指定了host,关闭 host:port端口
267 强制重启swoole-task 必须指定端口
268 平滑重启swoole-task 必须指定端口
269 获取swoole-task 状态,必须指定port(不指定host默认127.0.0.1), tasking_num是正在处理的任务数量(0表示没有待处理任务)
270 
271 HELP;
272     exit;
273 }
274 //参数检查
275 foreach ($opts as $k => $v) {
276     if (($k == 'h' || $k == 'host')) {
277         if (empty($v)) {
278             exit("参数 -h --host 必须指定值\n");
279         }
280     }
281     if (($k == 'p' || $k == 'port')) {
282         if (empty($v)) {
283             exit("参数 -p --port 必须指定值\n");
284         }
285     }
286     if (($k == 'n' || $k == 'name')) {
287         if (empty($v)) {
288             exit("参数 -n --name 必须指定值\n");
289         }
290     }
291 }
292 
293 //命令检查
294 $cmd = $argv[$argc - 1];
295 if (!in_array($cmd, $cmds)) {
296     exit("输入命令有误 : {$cmd}, 请查看帮助文档\n");
297 }
298 
299 //监听ip 127.0.0.1,空读取配置文件
300 $host = '127.0.0.1';
301 if (!empty($opts['h'])) {
302     $host = $opts['h'];
303     if (!filter_var($host, FILTER_VALIDATE_IP)) {
304         exit("输入host有误:{$host}");
305     }
306 }
307 if (!empty($opts['host'])) {
308     $host = $opts['host'];
309     if (!filter_var($host, FILTER_VALIDATE_IP)) {
310         exit("输入host有误:{$host}");
311     }
312 }
313 //监听端口,9501 读取配置文件
314 $port = 9501;
315 if (!empty($opts['p'])) {
316     $port = (int)$opts['p'];
317     if ($port <= 0) {
318         exit("输入port有误:{$port}");
319     }
320 }
321 if (!empty($opts['port'])) {
322     $port = (int)$opts['port'];
323     if ($port <= 0) {
324         exit("输入port有误:{$port}");
325     }
326 }
327 //进程名称 没有默认为 SWOOLE_TASK_NAME_PRE;
328 $name = SWOOLE_TASK_NAME_PRE;
329 if (!empty($opts['n'])) {
330     $name = $opts['n'];
331 }
332 if (!empty($opts['name'])) {
333     $name = $opts['n'];
334 }
335 //是否守护进程 -1 读取配置文件
336 $isdaemon = -1;
337 if (isset($opts['D']) || isset($opts['nondaemon'])) {
338     $isdaemon = 0;
339 }
340 if (isset($opts['d']) || isset($opts['daemon'])) {
341     $isdaemon = 1;
342 }
343 //启动swoole-task服务
344 if ($cmd == 'start') {
345     servStart($host, $port, $isdaemon, $name);
346 }
347 //强制停止swoole-task服务
348 if ($cmd == 'stop') {
349     if (empty($port)) {
350         exit("停止swoole-task服务必须指定port" . PHP_EOL);
351     }
352     servStop($host, $port);
353 }
354 //关闭swoole-task服务
355 if ($cmd == 'close') {
356     if (empty($port)) {
357         exit("停止swoole-task服务必须指定port" . PHP_EOL);
358     }
359     servClose($host, $port);
360 }
361 //强制重启swoole-task服务
362 if ($cmd == 'restart') {
363     if (empty($port)) {
364         exit("重启swoole-task服务必须指定port" . PHP_EOL);
365     }
366     echo "重启swoole-task服务" . PHP_EOL;
367     servStop($host, $port, true);
368     servStart($host, $port, $isdaemon, $name);
369 }
370 //平滑重启swoole-task服务
371 if ($cmd == 'reload') {
372     if (empty($port)) {
373         exit("平滑重启swoole-task服务必须指定port" . PHP_EOL);
374     }
375     echo "平滑重启swoole-task服务" . PHP_EOL;
376     servReload($host, $port, true);
377 }
378 //查看swoole-task服务状态
379 if ($cmd == 'status') {
380     if (empty($host)) {
381         $host = '127.0.0.1';
382     }
383     if (empty($port)) {
384         exit("查看swoole-task服务必须指定port(host不指定默认使用127.0.0.1)" . PHP_EOL);
385     }
386     servStatus($host, $port);
387 }
388 //查看swoole-task服务进程列表
389 if ($cmd == 'list') {
390     servList();
391 }
复制代码

 

SwooleServer.php

复制代码
  1 <?php
  2 
  3 /**
  4  * Swoole服务端
  5  */
  6 class SwooleServer {
  7 
  8     private $_serv = null;
  9     private $_setting = array();
 10 
 11     public function __construct($host = '0.0.0.0', $port = 9501) {
 12         $this->_setting = array(
 13             'host' => $host,
 14             'port' => $port,
 15             'env' => 'dev', //环境 dev|test|prod
 16             'process_name' => SWOOLE_TASK_NAME_PRE,  //swoole 进程名称
 17             'worker_num' => 4, //一般设置为服务器CPU数的1-4倍
 18             'task_worker_num' => 4,  //task进程的数量
 19             'task_ipc_mode' => 3,  //使用消息队列通信,并设置为争抢模式
 20             'task_max_request' => 10000,  //task进程的最大任务数
 21             'daemonize' => 1, //以守护进程执行
 22             'max_request' => 10000,
 23             'dispatch_mode' => 2,
 24             'log_file' => SWOOLE_PATH . DIRECTORY_SEPARATOR . 'App' . DIRECTORY_SEPARATOR . 'Runtime' . DIRECTORY_SEPARATOR . 'Logs' . DIRECTORY_SEPARATOR . 'Swoole' . date('Ymd') . '.log',  //日志
 25         );
 26     }
 27 
 28     /**
 29      * 运行swoole服务
 30      */
 31     public function run() {
 32         $this->_serv = new \swoole_server($this->_setting['host'], $this->_setting['port']);
 33         $this->_serv->set(array(
 34             'worker_num' => $this->_setting['worker_num'],
 35             'task_worker_num' => $this->_setting['task_worker_num'],
 36             'task_ipc_mode ' => $this->_setting['task_ipc_mode'],
 37             'task_max_request' => $this->_setting['task_max_request'],
 38             'daemonize' => $this->_setting['daemonize'],
 39             'max_request' => $this->_setting['max_request'],
 40             'dispatch_mode' => $this->_setting['dispatch_mode'],
 41             'log_file' => $this->_setting['log_file']
 42         ));
 43         $this->_serv->on('Start', array($this, 'onStart'));
 44         $this->_serv->on('Connect', array($this, 'onConnect'));
 45         $this->_serv->on('WorkerStart', array($this, 'onWorkerStart'));
 46         $this->_serv->on('ManagerStart', array($this, 'onManagerStart'));
 47         $this->_serv->on('WorkerStop', array($this, 'onWorkerStop'));
 48         $this->_serv->on('Receive', array($this, 'onReceive'));
 49         $this->_serv->on('Task', array($this, 'onTask'));
 50         $this->_serv->on('Finish', array($this, 'onFinish'));
 51         $this->_serv->on('Shutdown', array($this, 'onShutdown'));
 52         $this->_serv->on('Close', array($this, 'onClose'));
 53         $this->_serv->start();
 54     }
 55 
 56     /**
 57      * 设置swoole进程名称
 58      * @param string $name swoole进程名称
 59      */
 60     private function setProcessName($name) {
 61         if (function_exists('cli_set_process_title')) {
 62             cli_set_process_title($name);
 63         } else {
 64             if (function_exists('swoole_set_process_name')) {
 65                 swoole_set_process_name($name);
 66             } else {
 67                 trigger_error(__METHOD__ . " failed. require cli_set_process_title or swoole_set_process_name.");
 68             }
 69         }
 70     }
 71 
 72     /**
 73      * Server启动在主进程的主线程回调此函数
 74      * @param $serv
 75      */
 76     public function onStart($serv) {
 77         if (!$this->_setting['daemonize']) {
 78             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server master worker start\n";
 79         }
 80         $this->setProcessName($this->_setting['process_name'] . '-master');
 81         //记录进程id,脚本实现自动重启
 82         $pid = "{$serv->master_pid}\n{$serv->manager_pid}";
 83         file_put_contents(SWOOLE_TASK_PID_PATH, $pid);
 84     }
 85 
 86     /**
 87      * worker start 加载业务脚本常驻内存
 88      * @param $server
 89      * @param $workerId
 90      */
 91     public function onWorkerStart($serv, $workerId) {
 92         if ($workerId >= $this->_setting['worker_num']) {
 93             $this->setProcessName($this->_setting['process_name'] . '-task');
 94         } else {
 95             $this->setProcessName($this->_setting['process_name'] . '-event');
 96         }
 97         // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
 98         define('APP_DEBUG', True);
 99         // 定义应用目录
100         define('APP_PATH', SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Application' . DIRECTORY_SEPARATOR);
101         // 定义应用模式
102         define('APP_MODE', 'cli');
103         // 引入ThinkPHP入口文件
104         require SWOOLE_PATH . DIRECTORY_SEPARATOR . 'ThinkPHP' . DIRECTORY_SEPARATOR . 'ThinkPHP.php';
105     }
106 
107     /**
108      * 监听连接进入事件
109      * @param $serv
110      * @param $fd
111      */
112     public function onConnect($serv, $fd) {
113         if (!$this->_setting['daemonize']) {
114             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server connect[" . $fd . "]\n";
115         }
116     }
117 
118     /**
119      * worker 进程停止
120      * @param $server
121      * @param $workerId
122      */
123     public function onWorkerStop($serv, $workerId) {
124         if (!$this->_setting['daemonize']) {
125             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server[{$serv->setting['process_name']}  worker:{$workerId} shutdown\n";
126         }
127     }
128 
129     /**
130      * 当管理进程启动时调用
131      * @param $serv
132      */
133     public function onManagerStart($serv) {
134         if (!$this->_setting['daemonize']) {
135             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server manager worker start\n";
136         }
137         $this->setProcessName($this->_setting['process_name'] . '-manager');
138     }
139 
140     /**
141      * 此事件在Server结束时发生
142      */
143     public function onShutdown($serv) {
144         if (file_exists(SWOOLE_TASK_PID_PATH)) {
145             unlink(SWOOLE_TASK_PID_PATH);
146         }
147         if (!$this->_setting['daemonize']) {
148             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server shutdown\n";
149         }
150     }
151 
152     /**
153      * 监听数据发送事件
154      * @param $serv
155      * @param $fd
156      * @param $from_id
157      * @param $data
158      */
159     public function onReceive($serv, $fd, $from_id, $data) {
160         if (!$this->_setting['daemonize']) {
161             echo "Get Message From Client {$fd}:{$data}\n\n";
162         }
163         $result = json_decode($data, true);
164         switch ($result['action']) {
165             case 'reload':  //重启
166                 $serv->reload();
167                 break;
168             case 'close':  //关闭
169                 $serv->shutdown();
170                 break;
171             case 'status':  //状态
172                 $serv->send($fd, json_encode($serv->stats()));
173                 break;         
174             default:             
175                 $serv->task($data);
176                 break;
177         }
178     }
179 
180     /**
181      * 监听连接Task事件
182      * @param $serv
183      * @param $task_id
184      * @param $from_id
185      * @param $data
186      */
187     public function onTask($serv, $task_id, $from_id, $data) {
188         $result = json_decode($data, true);
189         //用TP处理各种逻辑
190         $serv->finish($data);
191     }
192 
193     /**
194      * 监听连接Finish事件
195      * @param $serv
196      * @param $task_id
197      * @param $data
198      */
199     public function onFinish($serv, $task_id, $data) {
200         if (!$this->_setting['daemonize']) {
201             echo "Task {$task_id} finish\n\n";
202             echo "Result: {$data}\n\n";
203         }
204     }
205 
206     /**
207      * 监听连接关闭事件
208      * @param $serv
209      * @param $fd
210      */
211     public function onClose($serv, $fd) {
212         if (!$this->_setting['daemonize']) {
213             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server close[" . $fd . "]\n";
214         }
215     }
216 
217 
218 }
复制代码

相关命令:

1、服务启动
#启动服务,不指定绑定端口和ip,则使用默认配置
php swoole.php start
#启动服务 指定ip 和 port
php swoole.php -h127.0.0.1 -p9501 start
#启动服务 守护进程模式
php swoole.php -h127.0.0.1 -p9501 -d start
#启动服务 非守护进程模式
php swoole.php -h127.0.0.1 -p9501 -D start
#启动服务 指定进程名称(显示进程名为 swooleServ-9510-[master|manager|event|task]
php swoole.php -h127.0.0.1 -p9501 -n 9501 start

2、强制服务停止
php swoole.php stop
php swoole.php -p9501 stop
php swoole.php -h127.0.0.1 -p9501 stop

3、关闭服务
php swoole.php close
php swoole.php -p9501 close
php swoole.php -h127.0.0.1 -p9501 close

4、强制服务重启
php swoole.php restart
php swoole.php -p9501 restart
php swoole.php -h127.0.0.1 -p9501 restart

5、平滑服务重启
php swoole.php reload
php swoole.php -p9501 reload
php swoole.php -h127.0.0.1 -p9501 reload

6、服务状态
php swoole.php status
php swoole.php -h127.0.0.1 -p9501 status

7、swoole-task所有启动实例进程列表(一台服务器swoole-task可以有多个端口绑定的实例)
php swoole.php list

 

Swoole-ThinkPHP.zip ( 1.14 MB )