## **logo Swoole新手包开发手册 ** [TOC] #### **前言** Swoole新手包(以下简称新手包)虽名为新手包,但本意是倡导低代码情况下完成进阶开发,最大限度降低学习成本,掌握核心技术。新手包对Swoole进行高度封装,对于之前没有或较少接触过多进程、协程、多种通信协议模块等技术实现的开发者使其快速掌握进阶开发,可以在任何PHP框架或原生PHP中进行开发,完美兼容。 #### **部署新手包** ------ ##### 环境要求 - 保证**php**版本>=**7.0** - 保证**关系型数据库**采用**Mysql** - 保证**缓存数据库**采用**Redis** - 保证**Swoole**扩展版本>= **4.6.7**同时<**5.0** ##### 下载部署 1、通过以下几种方式下载新手包: - [https://www.easylt.cn](https://www.easylt.cn)官网直接下载,在技术支持-新手包系列中选择Swoole新手包进入并下载。 - Git下载指令:git clone https://github.com/myframe1002223338/swoole-novice.git - composer安装指令:composer create-project "swoole-novice/swoole-novice":"dev-master" PS:composer安装显示以下错误: ​ [Symfony\Component\Process\Exception\RuntimeException] ​ The Process class relies on proc_open, which is not available on your PHP installation. ​ 解决方法:打开php.ini,搜索disable_functions,找到disable_functions = xxx,xxx,xxx...删除其中的 ​ proc_open保存并重启服务器即可。 2、将下载的Swoole新手包复制到项目任意目录下即可。 #### **使用新手包** ------ ##### 配置文件 - 请在新手包根目录打开src/config目录,在config_db.php中对数据库连接常量进行相应的配置更改。 - config_swoole.php是Swoole配置文件,默认无需配置更改;请打开查看每一项配置说明便于后期进行特殊化配置。 ##### 引入依赖 使用新手包开发需引入依赖,autoload.php为加载文件,在项目中引入该文件即可;如Swoole新手包放置到项目根目录下,此时在项目根目录下创建test.php并在文件顶部require('swoole-novice/autoload.php'); **PS:require('')中的路径根据实际项目目录结构调整** ##### **创建服务** ###### 运行模式 除TCP、UDP、HTTP、WebSocket、RPC的客户端可以选择通过WEB服务器(FPM)运行,且WebSocket客户端仅支持WEB服务器(FPM)运行,其他必须通过CLI模式运行。 **PS:服务器、客户端中不可创建容器,如多进程、协程/异步Mysql/Redis容器,但多进程容器中可以包含服务器、客户端、协程/异步Mysql/Redis容器。** ###### DB操作 - **通过CLI模式运行新手包创建的服务支持异步Mysql、异步Redis、同步Mysql、同步Redis的DB操作;服务器中的TASK异步任务仅支持同步Mysql、同步Redis。** - **通过WEB服务器运行新手包创建的TCP、UDP、HTTP、WebSocket、RPC客户端仅支持同步Mysql、同步Redis。** - **异步Mysql、异步Redis、同步Mysql、同步Redis具体操作请阅读以下相关文档。** ###### TCP服务器 - 创建服务器【必须实现】 $tcp_serv = new Tcp_server($ip,$port); //$ip为服务器监听ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Tcp_server('',9501);** - 当客户端连接成功时触发【可选项】 $tcp_serv->connect(function(){ //匿名回调函数中写你的业务 }); - 当接收到请求数据时【必须实现】 $tcp_serv->receive(function($request_data){ //$request_data为客户端发送的数据 //匿名回调函数中写你的业务 //如需给客户端返回数据请return $var }); - 处理task异步任务,具体操作请阅读**异步任务**部分文档【可选项】 $tcp_serv->task(function($request_data,$task_id,$reactor_id){ //$request_data为客户端发送的数据 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - task异步任务完成事件【如启用task则必须实现】 $tcp_serv->finish(function($state,$task_id,$reactor_id){ //$state为task异步任务运行结果状态,当$state等于true时,异步任务运行完成。 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - 当客户端关闭成功时触发【可选项】 $tcp_serv->close(function(){ //匿名回调函数中写你的业务 }); - 启动服务器【必须实现】 $tcp_serv->start(); ###### TCP客户端 - 创建客户端【必须实现】 $tcp_cli = new Tcp_client($ip,$port); //$ip为服务器ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Tcp_client('',9501);** - 像服务器发送数据【必须实现】 ​ $tcp_cli->send(function(){ ​ //匿名回调函数中写你的业务 ​ //如需发送数据请return $var ​ }); - 从服务器接收数据【必须实现】 $tcp_cli->receive(function($response_data){ //$response_data为服务器返回的数据 //匿名回调函数中写你的业务 }); - 关闭连接【必须实现】 $tcp_cli->close(); ###### UDP服务器 - 引入服务器配置文件【必须实现】 include('server.ini'); - 创建服务器【必须实现】 $udp_serv = new Udp_server($ip,$port); //$ip为服务器监听ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Udp_server('',9501);** - 当接收到请求数据时【必须实现】 $udp_serv->receive(function($request_data){ //$request_data为客户端发送的数据 //匿名回调函数中写你的业务 //如需给客户端返回数据请return $var }); - 处理task异步任务,具体操作请阅读**异步任务**部分文档【可选项】 $udp_serv->task(function($request_data,$task_id,$reactor_id){ //$request_data为客户端发送的数据 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - task异步任务完成事件【如启用task则必须实现】 $udp_serv->finish(func ###### UDP客户端 - 创建客户端【必须实现】 $udp_cli = new Udp_client($ip,$port); //$ip为服务器ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Udp_client('',9501);** - 像服务器发送数据【必须实现】 ​ $udp_cli->send(function(){ ​ //匿名回调函数中写你的业务 ​ //如需发送数据请return $var ​ }); - 从服务器接收数据【必须实现】 $udp_cli->receive(function($response_data){ //$response_data为服务器返回的数据 //匿名回调函数中写你的业务 }); ###### HTTP服务器 - 创建服务器【必须实现】 $http_serv = new Http_server($ip,$port); //$ip为服务器监听ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Http_server('',9501);** - 当接收到请求数据时【必须实现】 $http_serv->receive(function($request_post,$request_get,$headers_message){ //$request_post为客户端发送的post请求数据,$request_get为客户端发送的get请求数据; //$headers_message为获取头信息请求参数 //匿名回调函数中写你的业务 //如需给客户端返回数据请return $var }); - 处理task异步任务,具体操作请阅读**异步任务**部分文档【可选项】 $http_serv->task(function($request_post,$request_get,$task_id,$reactor_id){ //$request_post为客户端发送的post请求数据,$request_get为客户端发送的get请求数据; //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - task异步任务完成事件【如启用task则必须实现】 $http_serv->finish(function($state,$task_id,$reactor_id){ //$state为task异步任务运行结果状态,当$state等于true时,异步任务运行完成。 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - 启动服务器【必须实现】 $http_serv->start(); ###### HTTP客户端 - 创建客户端【必须实现】 $http_cli = new Http_client($ip,$port); //$ip为服务器ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Http_client('',9501);** - 像服务器发送数据并接收服务器返回数据【必须实现】 ​ $result = $http_cli->post(['username'=>'easylt'],$headers); ​ //发送post请求,如无POST请求数据,请传入空数组[]即可。请求的URL请在config_swoole.php中配置; ​ //$headers为头信息请求参数,选填项,数据类型必须为array,且为一维数组,元素值用冒号分隔键值对, ​ 如:['Authorization:init'] ​ //$result为服务器返回数据 ​ $result = $http_cli->get('?username=easylt',$headers); ​ //发送get请求,如无GET请求数据,请传入空字符串''即可。请求的URL请在config_swoole.php中配置,这里 ​ 仅跟URI参数,如:?username=easylt&work=frame ​ //$headers为头信息请求参数,选填项,数据类型必须为array,且为一维数组,元素值用冒号分隔键值对, ​ 如:['Authorization:init'] ​ //$result为服务器返回数据 ###### WebSocket服务器 - 创建服务器【必须实现】 $websocket_serv = new Websocket_server($ip,$port); //$ip为服务器监听ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Websocket_server('',9501);** - 当客户端连接成功时触发【可选项】 $websocket_serv->connect(function($request_data,$fd){ //$request_data为客户端发送的数据 //$fd为客户端ID //匿名回调函数中写你的业务 //如需给客户端返回数据请return $var }); - 当接收到请求数据时【必须实现】 $websocket_serv->receive(function($request_data,$fd){ //$request_data为客户端发送的数据 **PS:开发一对一发送消息业务时,客户端连接成功时服务器端将userid与socket-fd进行关联缓存到Redis,每次发送数据通过服务器端查询对方的fd并组装对象字符串JSON.stringify({fd:fd})发送给服务器,如果不发送fd则默认fd为自己;** //$fd为客户端ID //匿名回调函数中写你的业务 //如需给客户端返回数据请return $var **//$websocket_serv->receive(function(){});的方法体中可实现一对一(单聊),一对多(群聊)业务,请在config_swoole.php中配置,默认为单聊模式。** }); - 处理task异步任务,具体操作请阅读**异步任务**部分文档【可选项】 $websocket_serv->task(function($request_data,$task_id,$reactor_id){ //$request_data为客户端发送的数据 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - task异步任务完成事件【如启用task则必须实现】 $websocket_serv->finish(function($state,$task_id,$reactor_id){ //$state为task异步任务运行结果状态,当$state等于true时,异步任务运行完成。 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - 当客户端关闭成功时触发【可选项】 $websocket_serv->close(function($fd){ //$fd为客户端ID //匿名回调函数中写你的业务 }); - 启动服务器【必须实现】 $websocket_serv->start(); ###### WebSocket客户端 - WebSocket客户端请参考官方文档:[https://www.easylt.cn/?href=document3-11](https://www.easylt.cn/?href=document3-11) ###### RPC服务器 远程过程调用,较RESTful风格API接口效率更高,适用于大型系统、多系统间的业务往来。 - 创建服务器【必须实现】 $rpc_serv = new Rpc_server($ip,$port); //$ip为服务器监听ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Rpc_server('',9501);** - 当客户端连接成功时触发【可选项】 $rpc_serv->connect(function(){ //匿名回调函数中写你的业务 }); - 当接收到请求数据时【必须实现】 $rpc_serv->receive(function($function_name,$function_param){ //$function_name为客户端请求的方法名,数据类型为string; //$function_param为客户端请求的实参,数据类型为array; //如客户端RPC调用发送请求数据为'func1(liteng,10)',则$function_name为func1,$function_param[0]为liteng,$function_param[1]为10,数字索引下标取值。 //以下为RPC方法定义默认语法示例,统一采用switch 多分支条件结构 : switch($function_name){ case 'func1': //定义的方法名供客户端调用,以下为方法体用于实现业务; $name = $function_param[0]; $old_num = $function_param[1]; $int = intval($old_num); $num = $int + 20; $result = $name.'一共有'.$num.'颗糖果'; return $result; //如需返回给客户端数据请return $var break; case 'func2': //定义的方法名供客户端调用,以下为方法体用于实现业务; $name = $function_param[0]; $old_num = $function_param[1]; $int = intval($old_num); $num = $int + 10; $result = $name.'一共有'.$num.'颗糖果'; return $result; //如需返回给客户端数据请return $var break; default: return '方法调用失败'; //客户端请求不传参的返回值,如需返回给客户端数据请return $var; } }); - 处理task异步任务,具体操作请阅读**异步任务**部分文档【可选项】 $rpc_serv->task(function($request_data,$task_id,$reactor_id){ //$request_data为客户端发送的数据 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - task异步任务完成事件【如启用task则必须实现】 $rpc_serv->finish(function($state,$task_id,$reactor_id){ //$state为task异步任务运行结果状态,当$state等于true时,异步任务运行完成。 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); - 当客户端关闭成功时触发【可选项】 $rpc_serv->close(function(){ //匿名回调函数中写你的业务 }); - 启动服务器【必须实现】 $rpc_serv->start(); ###### RPC客户端 - 创建客户端【必须实现】 $rpc_cli = new Rpc_client($ip,$port); //$ip为服务器ip地址,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 //$port为服务器端口号,选填项,当不填入时自动填入配置文件config_swoole.php中的常量值。 **PS:当填入$ip时,必须填入$port;当填入$port时,可忽略$ip,如:new Rpc_client('',9501);** - 像服务器发送数据【必须实现】 ​ $rpc_cli->send(function(){ ​ //匿名回调函数中写你的业务 ​ //如需发送数据请return $var,发送的请求数据格式为方法调用写法,类型为string,如:'func1(liteng,10)' ​ }); - 从服务器接收数据【必须实现】 $rpc_cli->receive(function($response_data){ //$response_data为服务器返回的数据 //匿名回调函数中写你的业务 }); - 关闭连接【必须实现】 $rpc_cli->close(); ###### 多进程 - 有两种创建模式,分别为单独进程模式与进程池模式:**单独进程模式的进程间通信支持QUEUE消息队列;进程池模式的进程间通信支持PIPE管道。** **//进程模式通过$process>create()中的第二个参数切换,'single'为单独进程模式,'pool'为进程池模式。** ​ **单独进程模式:** - 实例化进程【必须实现】 $process = new Process;//单独进程模式下创建多进程运行为异步非阻塞模式,可在每个进程中创建服务器,进程间用QUEUE消息队列通信。 - 创建进程【必须实现】 $process->create(function($process){**//创建第一个进程** //$process为进程对象 //匿名回调函数中写你的业务 ​ $process->push(mt_rand(1,9));//写入数据到QUEUE消息队列 },'single'); **//'single'为单独进程模式,'pool'为进程池模式。** $process->create(function($process){**//创建第二个进程** ​ $read = $process->pop();//读取消息队列中的数据,多进程消息队列为争抢模式,无法将消息投递给指定 ​ 进程,但消息是共享的,可一次性读取出来。 ​ echo $read; },'single'); ​ **进程池模式:** ​ **PS:pool进程池模式下请勿在进程中创建如TCP等服务器,进程池模式适用于处理有状态消费业务,如从** ​ **消息队列中读取消息并消费。需要在进程容器中创建如TCP等服务器请选择single单独进程模式。** - 实例化进程【必须实现】 $process = new Process;//进程池模式运行为异步非阻塞模式,进程间用PIPE管道通信。 - 创建进程池【必须实现】 $process->create(function($process){ //$process为进程对象 //匿名回调函数中写你的业务 //如需写入数据到PIPE管道请return $var },'pool'); - 读取PIPE管道中的数据【可选项】 $process->pipe(function($response_data){ //匿名回调函数中写你的业务 //$response_data为读取PIPE管道中的数据 }); ###### 协程 new Async(function($channel){ **//通过new Async()创建协程容器,将协程放置到容器的匿名回调函数中自动实现异步。** **//$channel为协程通信通道变量。** **PS:** ​ **1、在new Async()协程容器中创建协程才可自动实现异步,但协程容器不可在服务器中创建,如TCP服务器,** ​ **会发生冲突导致运行错误。** ​ **2、未创建协程容器时,在配置文件config_swoole.php中配置常量ASYNC_CO开启异步协程同样使协程自动实** ​ **现异步。** ​ **3、以下异步Mysql、异步Redis基于协程开发,用法同理;通过异步Mysql、异步Redis读取的数据需要进行发** ​ **送时,请先判断该数据是否存在,因为是异步非阻塞运行,后续代码优先执行,可能第一次会发送空数据。** ​ Async::co(function()use($channel){**//创建协程一,同一协程内为同步阻塞执行** ​ **//协程通信的消费者应小于等于生产者,否则会发生异常,且消费按顺序读取数据;** ​ $channel->push('发送数据到管道');//先use闭包引入$channel变量,push发送数据到通道; ​ sleep(10); ​ echo '这是协程一'; ​ }); ​ Async::co(function()use($channel){**//创建协程二** ​ $channel->pop();//先use闭包引入$channel变量,pop从通道读取数据; ​ echo '这是协程二'; ​ }); }); //以上运行协程一执行要等待10秒,则协程一挂起执行协程二,先后输出:这是协程二 这是协程一 ###### 异步 Mysql - 当在如TCP等服务器、协程(不包含多进程容器)容器中创建异步Mysql时,无需创建异步Mysql容器,直接创建异步Mysql,否则会报错。 - 调用匿名函数外的变量请使用闭包写法(同步Mysql同理),如: new Async_mysql(function()use($var){Async_mysql::co($mysql_conn,$mysql_orm)use($var){}}); new Async_mysql(function(){**//创建异步Mysql容器** ​ Async_mysql::co(function($mysql_conn,$mysql_orm){**//创建异步Mysql一** ​ **//$mysql_conn为mysql的mysqli面向过程连接变量,如下数据库操作示例:** ​ $sql = "insert into account(username) values('异步Mysql操作一')"; ​ mysqli_query($mysql_conn,$sql); ​ mysqli_close($mysql_conn); ​ **//$mysql_orm为mysql的orm操作变量,具体操作请阅读ORM文档。** ​ },10);**//10代表异步Mysql操作时,为避免发生同一时间片资源争抢而导致部分IO执行失败,设置的** ​ **IO运行周期时间,以秒为单位,非必填项,可自行设置值;** ​ **PS:如果不设置IO运行周期时间,也默认自动实现IO异步非阻塞模式。** ​ Async_mysql::co(function($mysql_conn,$mysql_orm){**//创建异步Mysql二** ​ $sql = "insert into account(username) values('异步Mysql操作二')"; ​ mysqli_query($mysql_conn,$sql); ​ mysqli_close($mysql_conn); ​ },5); }); //以上异步Mysql运行结果为:数据库5秒后写入'异步Mysql操作二',10秒后写入'异步Mysql操作一'。 ###### 异步 Redis - 当在如TCP等服务器、协程(不包含多进程容器)容器中创建异步Redis时,无需创建异步Redis容器,直接创建异步Redis,否则会报错。 - 调用匿名函数外的变量请使用闭包写法(同步Redis同理),如: new Async_redis(function()use($var){Async_redis::co($mysql_conn,$mysql_orm)use($var){}}); new Async_redis(function(){**//创建异步Redis容器** ​ Async_redis::co(function($redis){**//创建异步Redis一** ​ **//$redis为redis连接变量,如下数据库操作示例:** ​ $redis->set('async1','异步Redis操作一'); ​ echo $redis->get('async1'); ​ $redis->close(); ​ },10);**//10代表异步Redis操作时,为避免发生同一时间片资源争抢而导致部分IO执行失败,设置的** ​ **IO运行周期时间,以秒为单位,非必填项,可自行设置值;** ​ **PS:如果不设置IO运行周期时间,也默认自动实现IO异步非阻塞模式。** ​ Async_redis::co(function($redis){**//创建异步Redis二** ​ $redis->set('async2','异步Redis操作二'); ​ echo $redis->get('async2'); ​ $redis->close(); ​ },5); }); //以上异步Redis运行结果为:数据库5秒后写入读取'异步Redis操作二',10秒后写入读取'异步Redis操作一'。 ###### 异步cURL new Async(function($channel){ **PS:** ​ **1、在new Async()协程容器中创建协程才可自动实现异步,但协程容器不可在服务器中创建,如TCP服务器,** ​ **会发生冲突导致运行错误。** ​ **2、未创建协程容器时,在配置文件config_swoole.php中配置常量ASYNC_CO开启异步协程同样使协程自动实** ​ **现异步。** ​ Async::co(function()use($channel){**//创建协程一,同一协程内为同步阻塞执行** ​ **//实例化POST请求的cURL对象;** ​ $curl_post = new Curl; ​ $result = $curl_post->post($url,$data,$headers,$status); ​ //第一个参数为目标文件的url、第二个参数为发送的数据、第三个参数为头信息请求参数(非必填)、第四 ​ 个参数为$data数据转换状态值(非必填),$result为请求返回值。 ​ //$headers的数据类型必须为array,且为一维数组,元素值用冒号分隔键值对,如:['Authorization:init'] ​ //$status参数默认可填值为'json'、'form',当头信息请求包含以下参数时: ​ ['Contenttype:application/json','Accept:application/json'],请填入'json',此时body请求参数自动转换为 ​ json格式; ​ //当头信息请求包含以下参数时: ​ ['Content-Type:application/x-www-form-urlencoded'],请填入'form',此时body请求参数自动转换为uri ​ 拼接参数,如name=easylt&type=php; ​ }); ​ Async::co(function()use($channel){**//创建协程二** ​ **//实例化GET请求的cURL对象;** ​ $curl_post = new Curl_get; ​ $result = $curl_get->get($url,$headers),第一个参数为目标文件的url、第二个参数为头信息请求参数(非 ​ 必填),$result为请求返回值。 ​ //$headers的数据类型必须为array,且为一维数组,元素值用冒号分隔键值对,如:['Authorization:init'] ​ }); }); ###### 异步任务(同步Mysql、同步Redis) - 以上TCP等服务器可选择性创建TASK异步任务,用于异步非阻塞处理一些耗时的任务,启用时需在配置文件config_swoole.php中相应的服务器配置开启异步任务。 - TASK异步任务中不支持协程、异步Mysql、异步Redis,所以当需要对DB操作时,请选择同步Mysql、同步Redis,以TCP服务器为例: $tcp_serv = new Tcp_server; $tcp_serv->receive(function($request_data){ return $request_data; }); $tcp_serv->task(function($request_data,$task_id,$reactor_id){ //$request_data为客户端发送的数据 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 **//创建同步Mysql** **PS:当配置文件config_swoole.php中配置常量ASYNC_CO为开启异步协程时,创建同步Mysql必须在容器** **中,即在如TCP等服务器或协程容器中(不含多进程容器,当放置多进程容器中也需包含服务器或协程容器),** **否则会发生冲突导致运行错误。** Sync_mysql::co(function($mysql_conn,$mysql_orm){ **//$mysql_conn为mysql的mysqli面向过程连接变量,如下数据库操作示例:** ​ $sql = "insert into account(username) values('同步Mysql操作')"; ​ mysqli_query($mysql_conn,$sql); ​ mysqli_close($mysql_conn); **//$mysql_orm为mysql的orm操作变量,具体操作请阅读ORM文档。** ​ }); **//创建同步Redis** **PS:当配置文件config_swoole.php中配置常量ASYNC_CO为开启异步协程时,创建同步Redis必须在容器中,** **即在如TCP等服务器或协程容器中(不含多进程容器,当放置多进程容器中也需包含服务器或协程容器),否 ** **则会发生冲突导致运行错误。** Sync_redis::co(function($redis){ **//$redis为redis连接变量,如下数据库操作示例:** ​ $redis->set('sync','同步Redis操作'); ​ echo $redis->get('sync'); ​ $redis->close(); ​ }); }); $tcp_serv->finish(function($state,$task_id,$reactor_id){ //$state为task异步任务运行结果状态,当$state等于true时,异步任务运行完成。 //$task_id为异步任务ID //$reactor_id为异步任务线程ID //匿名回调函数中写你的业务 }); $tcp_serv->start(); ###### 定时任务 - 多个定时任务在TCP等服务器/协程容器中,将异步非阻塞执行。 有两种定时器模式,**循环执行定时器** 与 **单次执行定时器**,如下: $tcp_serv = new Tcp_server; $tcp_serv->receive(function($request_data){ **//创建循环执行定时器** ​ Timer::loop(function(){ ​ $mt_rand = mt_rand(1,9); ​ if($mt_rand==9){ ​ Timer::clear();**//设置条件手动中止执行定时器** ​ **PS:也可在Timer::loop()中填入第三个选填参数,自动中止执行定时器。** ​ } ​ file_get_contents('https://www.baidu.com');//1000毫秒(1秒)后访问一次百度 ​ },1000,10000); ​ **//1000为定时器循环周期时间,以毫秒为单位,为必填项,可自行设置;10000为定时器中止执行周期时** ​ **间,以毫秒为单位,为选填项,可自行设置值。** **//创建单次执行定时器** ​ Timer::single(function(){ ​ file_get_contents('https://www.baidu.com');//3000毫秒(3秒)后访问一次百度 ​ },3000); ​ **//3000为定时器中止执行周期时间,以毫秒为单位,为必填项,可自行设置值。** ​ return $request_data; }); $tcp_serv->start(); ##### **ORM(MySQL)** ORM实现了mysql增删改查、事务、悲观锁操作,避免书写有漏洞的sql、优化实体类结构,如防止sql注入,同时让业务易于理解、更改。 ###### ORM原生写法 **$mysql_orm->db('sql');** //【必填项】直接在类方法实参中传入原生sql - insert操作,返回成功写入数据的行数量。 - delete操作,必须在sql中设置where条件。返回成功删除数据的行数量。 - update操作,必须在sql中设置where条件。返回成功更新数据的行数量。 - select操作,返回类型为array,返回数据查询结果。 ###### ORM非原生写法 ORM非原生写法操作时,首先选择操作模式,写法为:$mysql_orm->model(''); 形参有四种,分别为:insert、delete、update、select,model(),操作模式后的类方法顺序请按照文档顺序调用。 **PS:有两种写法,可组装或分组,$mysql_orm->model('select')->from('*,user')->query();为组装写法,以下为分组写法:** ​ $mysql_ob = $mysql_orm->model('select'); ​ $mysql_ob->from('*,user'); ​ $mysql_ob->query(); **insert写入操作** - **$mysql_ob = $mysql_orm->model('insert');** //【必填项】选择操作模式为写入 - **$mysql_ob->table('username&sex,user');** //【必填项】输入写入的字段与表名,用逗号分隔;多字 段用&分隔。 - **$mysql_ob->values('liteng,nan&leiting,nv');** //【与select()方法二选一必填项】输入字段相应写入的 值,用逗号分隔;如果写入多条数据请用&分隔;如参数为变量,拼接如下: **$mysql_ob->values($username.','.$sex.'&'.$username2.','.$sex2);** **$mysql_ob->select('select username,sex from user2 where sex=&$nan&');** //【与values()方法 二选一必填项】直接输入sql查询语句,条件值无论为变量、常量、%%模糊查询、[]正则查询或直接输入 标量都需要包含在&$&中间。 - **$mysql_ob->query();** //【必填项】返回成功写入数据的行数量 **delete删除操作** - **$mysql_ob = $mysql_orm->model('delete');** //【必填项】选择操作模式为删除 - **$mysql_ob->table('user');** //【必填项】输入表名 - **$mysql_ob->where('username=&$'.$username.'& or sex=&$nan&');** //【必填项】条件值无论为变 量、常量、%%模糊查询、[]正则查询或直接输入标量都需要包含在&$&中间,如: 'username=&$'.$username.'&'或'username=&$liteng&'或'username like &$%李%&' - **$mysql_ob->query();** //【必填项】返回成功删除数据的行数量 **update更新操作** - **$mysql_ob = $mysql_orm->model('update');** //【必填项】选择操作模式为更新,可多表更新。 - **$mysql_ob->table('user,user2');** //【必填项】输入表名,多表更新用逗号分隔表名。 - **$mysql_ob->set('user.sex=&$nv&,user2.sex=&$nv&');** //【必填项】输入更新字段与值,值无论为 变量、常量或直接输入标量都需要包含在&$&中间且用逗号分隔每组更新参数。 - **$mysql_ob->where('user.username=&$'.$username.'& and user2.username=&$.$username2** **.'&');** //【必填项】条件值无论为变量、常量、%%模糊查询 、[]正则查询或直接输入标量都需要包含在 &$&中间,如: 'username=&$'.$username.'&'或'username=&$liteng&'或'username like &$%李%&' - **$mysql_ob->query();** //【必填项】返回成功更新数据的行数量 **select查询操作** - **$mysql_ob = $mysql_orm->model('select');** //【必填项】选择操作模式为查询,支持全连接查询(PHP 不支持并行查询,内连接查询、外连接查询请用全连接查询拼接sql串行查询)、子 查询、分组查询、模糊 及正则查询;全连接查询、子查询、模糊及正则查询sql语句直接写在where()方法中。 - **$mysql_ob->from('username&sex,user');** //【必填项】输入字段与表名,用逗号分隔;多字段用&分隔。 - **$mysql_ob->where('username=&$'.$username.'& or sex=&$nan&');** //【选填项】条件值无论为变 量、常量、%%模糊查询、[]正则查询或直接输入标量都需要包含在&$&中间,如: 'username=&$'.$username.'&'或'username=&$liteng&'或'username like &$%李%&' - **$mysql_ob->group_by('username');** //【选填项】输入分组字段名,from()、order_by()方法中字段要和 group_by()中的字段相同,且from()方法中字段格式如下: 'username as 姓名&count(product) as 购物次数,user' - **$mysql_ob->order_by('username,desc');** //【选填项】输入排序字段名及排序规则-desc\asc,用逗号分 隔。 - **$mysql_ob->limit('0,10');** //【选填项】输入限制结果集参数-offset,num,用逗号分隔,仅输入一个参数 则默认为num。 - **$mysql_ob->query();** //【必填项】返回类型为array,返回数据查询结果。 ###### 事务 事务支持ORM的原生、非原生写法,开启事务后必须commit提交或rollback回滚才能结束事务。 - 开启事务:$mysql_orm->trans(); - 事务回滚:$mysql_orm->rollback(); - 事务提交:$mysql_orm->commit(); 示例: $mysql_orm->trans(); $result = $mysql_orm->db("insert into account(username) values('init')"); $result2 = $mysql_orm->db("insert into account(username) values('init2')"); if(!$result || !$result2){ $mysql_orm->rollback(); } $mysql_orm->commit(); //如上示例,当$result、$result2有一个未执行成功则数据回滚不进行提交。 ###### 悲观锁 ORM原生写法的悲观锁请用原生sql自行实现,如select * from account where id=1 for update; 此处仅支持ORM非原生写法且必须开启事务。 - 共享锁(其他事务可以读但不能写),如下: $mysql_orm->model('select')->from('*,account')->where('id=&$1&')->lock('read')->query(); - 排他锁(其他事务不能读也不能写),如下: $mysql_orm->model('select')->from('*,account')->where('id=&$1&')->lock('write')->query(); //通过lock('')开启悲观锁,传入参数read为共享锁,write为排他锁。 ------ ​ **感谢使用EASYLT 让开发更简单** ​ EASYLT官网:[https://www.easylt.cn](https://www.easylt.cn)