共计 11005 个字符,预计需要花费 28 分钟才能阅读完成。
官网文档
thinkphp6 文档
https://www.kancloud.cn/manua…swoole 文档
https://wiki.swoole.com/#/think-swoole 文档
https://www.kancloud.cn/manua…
装置
composer require topthink/think-swoole
命令行
php think swoole [start|stop|reload|restart]
服务启动
当你在命令行 php think swoole
下执行实现之后就会启动一个 HTTP Server,能够间接拜访以后的利用
'server' => ['host' => env('SWOOLE_HOST', '0.0.0.0'), // 监听地址 | |
'port' => env('SWOOLE_PORT', 9501), // 监听端口 | |
'mode' => SWOOLE_PROCESS, // 运行模式 默认为 SWOOLE_PROCESS | |
'sock_type' => SWOOLE_SOCK_TCP, // sock type 默认为 SWOOLE_SOCK_TCP | |
'options' => [ | |
// 服务启动后,过程 ID 寄存文件 | |
'pid_file' => runtime_path() . 'swoole.pid', | |
// swoole 的日志文件 | |
'log_file' => runtime_path() . 'swoole.log', | |
// 守护过程模式设置 true 后盾运行 | |
'daemonize' => false, | |
// 设置启动的 reactor 线程数 | |
'reactor_num' => swoole_cpu_num(), | |
// 设置启动的 worker 过程数 | |
'worker_num' => swoole_cpu_num(), | |
// 配置 Task 过程的数量 | |
'task_worker_num' => swoole_cpu_num(), | |
// 开启动态文件申请解决,需配合 document_root | |
'enable_static_handler' => true, | |
// 动态文件根目录 | |
'document_root' => root_path('public'), | |
// 设置最大数据包尺寸,单位字节 | |
'package_max_length' => 20 * 1024 * 1024, | |
// 配置发送输入缓冲区内存尺寸 | |
'buffer_output_size' => 10 * 1024 * 1024, | |
// 设置客户端连贯最大容许占用的内存数量 | |
'socket_buffer_size' => 128 * 1024 * 1024, | |
], | |
], |
热更新
swoole 服务器运行过程中 php 文件是常驻内存运行,这样就能够防止反复的读取磁盘,反复的解释编译 php,以便达到最高的性能,所以批改代码须要重启服务
think-swoole 扩大提供热更新性能,在检测相干文件有更新会主动重启,不在须要手动实现重启,不便开发调试
生产环境下不倡议开始文件监控,性能损耗,失常状况下你所批改的文件须要确认无误能力进行更新部署
.env
外面设置 APP_DEBUG = true
会默认开启热更新
'hot_update' => ['enable' => env('APP_DEBUG', false), | |
'name' => ['*.php'], | |
'include' => [app_path()], | |
'exclude' => [],], |
参数阐明
参数 | 阐明 |
---|---|
enable | 是否开启热更新 |
name | 监听哪些类型的文件变动 |
include | 监听哪些目录下的文件变动 |
exclude | 排除目录 |
websocket
先来一个官网的例子
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); | |
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {echo "server: handshake success with fd{$request->fd}\n"; | |
}); | |
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {echo "receive from {$frame->fd}:{$frame->data}\n"; | |
$server->push($frame->fd, "this is server"); | |
}); | |
$server->on('close', function ($ser, $fd) {echo "client {$fd} closed\n"; | |
}); | |
$server->start(); |
开启 think-swoole 的 websocket 性能 \config\swoole.php
'websocket' => ['enable' => true,],
创立三个事件
php think make:listener SwWsConnect | |
php think make:listener SwWsClose | |
php think make:listener SwWsMessage |
而后将这三个事件写到到事件监听中,别离有以下 2 中文件能够批改形式,留神二选一
thinkphp6 自带的事件绑定app\event.php
'listen' => [ | |
........ | |
// 监听链接 | |
'swoole.websocket.Connect' => [\app\listener\SwWsConnect::class], | |
// 敞开连贯 | |
'swoole.websocket.Close' => [\app\listener\SwWsClose::class], | |
// 发送音讯场景 | |
'swoole.websocket.Message' => [\app\listener\SwWsMessage::class] | |
], |
think-swoole 事件绑定config\swoole.php
'listen' => [ | |
'connect'=>\app\listener\SwWsConnect::class, | |
'close'=>\app\listener\SwWsClose::class, | |
'message'=> \app\listener\SwWsMessage::class | |
], |
怎么抉择是保留在
config\swoole.php
还是app\event.php
配置中呢?首先咱们 咱们确定一下咱们这个我的项目中存在有几个实时通信,
如果只是存在一个实时通信 集体倡议 保留在
config\swoole.php
如果是存在多个实时通信,就保留在
app\event.php
key 值 必须是
swoole.websocket. 事件名称
例如swoole.websocket.Message
开始写事件中中办法
连贯事件app\listener\SwWsConnect.php
public function handle($event, \think\swoole\websocket $ws) | |
{ | |
// 获取以后发送者的 fd | |
$fd = $ws->getSender(); | |
echo "server: handshake success with fd{$fd}\n"; | |
} |
敞开事件app\listener\SwWsClose.php
public function handle($event, \think\swoole\websocket $ws) | |
{$fd = $ws->getSender(); | |
echo "client {$fd} closed\n"; | |
} |
message 事件app\listener\SwWsMessage.php
public function handle($event, \think\swoole\websocket $ws) | |
{$fd = $ws->getSender(); | |
$data = json_encode($event); | |
echo "receive from {$fd}:{$data}\n"; | |
$ws->emit("this is server", $fd); | |
} |
启动 php think swoole
进行测试
think-swoole 中的 websocket 办法总结
// 给本人发消息 | |
$ws->emit("this is server", $ws->getSender()); | |
// 给指定一个 fd 发消息 | |
$ws->to($to)->emit("messagecallback",$data); | |
// 给指定多集体发消息 | |
$ws->to([1,2,3])->emit("messagecallback",$data); | |
// 发送给所有的(不蕴含本人) | |
$ws->broadcast()->emit("messagecallback",$data); | |
// 模仿 formfd 给 tofd 发送音讯 | |
$ws->setSender($formfd)->to($tofd)->emit("messagecallback",$data); |
留神:在多个实时通信场景下应用
emit
第一个参数传入 传入 事件名称 callback 例如
messagecallback
如果你发现你 think-swoole 中有些没有 swoole 中的办法能够这么干
$sw = app('swoole.server'); | |
$sw = app("think\swoole\Manager")->getServer(); | |
// 以上二选一 | |
$es = $sw->isEstablished($fd); // 查看连贯是否为无效的 WebSocket 客户端连贯 | |
var_dump($es); |
聊天室 room 实现
前端文件参考 html\room.html
或 html\room-socket-io.html
php think make:listener SwRoomJoin | |
php think make:listener SwRoomLeave | |
php think make:listener SwRoomMessage |
事件绑定
// 退出房间 | |
'swoole.websocket.RoomJoin' => [\app\listener\SwRoomJoin::class], | |
// 来到房间 | |
'swoole.websocket.Roomleave' => [\app\listener\SwRoomLeave::class], | |
// 在房间发消息 | |
'swoole.websocket.RoomMessage' => [\app\listener\SwRoomMessage::class] |
退出房间逻辑
public function handle($event, \think\swoole\websocket $ws, \think\swoole\websocket\room $room) | |
{$fd = $ws->getSender(); | |
// 客户端如果定的 room | |
$roomid = $event['room']; | |
// 获取指定房间下有哪些客户端 | |
$roomfds = $room->getClients($roomid); | |
// 判断这个房间有没有本人 如果有本人就不须要再次发送告诉 | |
if (in_array($fd, $roomfds)) {$ws->to($roomfds)->emit("roomjoincallback", "房间 {$roomid} 已退出"); | |
return; | |
} | |
// 退出房间 | |
$ws->join($roomid); | |
$ws->to($roomfds)->emit("roomjoincallback", "{$fd}退出房间 {$roomid} 胜利"); | |
} |
来到房间逻辑
public function handle($event, \think\swoole\websocket $ws, \think\swoole\websocket\Room $room) | |
{$roomid = $event['room']; | |
$fd = $ws->getSender(); | |
$roomfds = $room->getClients($roomid); | |
if (!in_array($fd, $roomfds)) {$ws->emit("roomleavecallback", "{$fd}不在 {$roomid} 房间内,怎么来到~"); | |
return; | |
} | |
// 来到房间 | |
$ws->leave($roomid); | |
// 获取以后客户端退出了哪些客户端 | |
$rooms = $room->getRooms($fd); | |
$ws->to($roomfds)->emit("roomleavecallback", "{$fd}已来到了~~"); | |
} |
在房间公布聊天逻辑
public function handle($event, \think\swoole\websocket $ws, \think\swoole\websocket\room $room) | |
{ | |
// | |
$roomid = $event['room']; | |
$text = $event['text']; | |
$fd = $ws->getSender(); | |
$roomfds = $room->getClients($roomid); | |
if (!in_array($fd, $roomfds)) {$ws->emit("roommessagecallback", "{$fd}不在 {$roomid} 房间内,无奈进入公布聊天~"); | |
return; | |
} | |
$ws->to($roomfds)->emit("roommessagecallback", $text); | |
} |
事件订阅
php think make:listener SwSubscribe
applistenerSwSubscribe.php
<?php | |
declare (strict_types = 1); | |
namespace app\listener; | |
class SwSubscribe | |
{ | |
protected $ws = null; | |
// public function __construct() | |
// {// $this->ws = app('think\swoole\Websocket'); | |
// } | |
public function __construct(\think\Container $c) | |
{$this->ws = $c->make(\think\swoole\Websocket::class); | |
} | |
public function onConnect() | |
{$fd = $this->ws->getSender(); | |
echo "server: handshake success with fd{$fd}\n"; | |
} | |
public function onClose() | |
{$fd = $this->ws->getSender(); | |
echo "client {$fd} closed\n"; | |
} | |
public function onMessage($event) | |
{$fd = $this->ws->getSender(); | |
var_dump($event); | |
echo "server: handshake success with fd{$fd}\n"; | |
$this->ws->emit("this is server", $fd); | |
} | |
} |
有点相似 将原生的 swoole 代码改成面向对象代码,失效办法
config\swoole.php
中在subscribe
退出\app\listener\SwSubscribe::class
'subscribe' => [\app\listener\SwSubscribe::class],
在
app\event.php
文件中的swoole.websocket.Connect
相当于app\listener\SwSubscribe.php
文件中的onConnect
函数。如果同时存在的存在的话,就会向客户端发送 2 次以上的音讯
Task 工作投递
https://wiki.swoole.com/#/sta…
生成事件
php think make:listener SwSendEmailTask
编写发送邮件办法app\listener\SwSendEmailTask.php
public function handle($event) | |
{var_dump($event); | |
// | |
echo "开发发送邮件".time(); | |
sleep(3); | |
echo "完结发送邮件".time();} |
注册事件app\event.php
'swoole.task'=>[\app\listener\SwSendEmailTask::class],
在控制器中投递工作
public function doRegister() | |
{$server = app('swoole.server'); | |
$server->task(\app\listener\SwSendEmailTask::class); | |
return "注册胜利"; | |
} | |
public function doRegister(\think\swoole\Manager $manager) | |
{$server = $manager->getServer(); | |
$server->task(\app\listener\SwSendEmailTask::class); | |
return "注册胜利"; | |
} | |
public function doRegister(\Swoole\Server $server) | |
{$server->task(\app\listener\SwSendEmailTask::class); | |
return "注册胜利"; | |
} |
三种获取
\Swoole\Server
, 任意选其一
在 swoole 中还有一个事件叫finish
,它的作用就是把异步工作的后果返回,在 think-swool 是这么解决的
定义一个发送邮件异步工作处理结果的事件
php think make:listener SwSendEmailFinish
注册事件app\event.php
'swoole.finish'=>[\app\listener\SwSendEmailFinish::class],
在 task 工作中调用
public function handle($event) | |
{var_dump($event); | |
// | |
echo "开发发送邮件".time(); | |
sleep(3); | |
echo "完结发送邮件".time(); | |
$event->finish(\app\listener\SwSendEmailFinish::class); | |
} |
高性能共享内存 Table
https://wiki.swoole.com/#/mem…
先定结构在进行操作数据(原生 swoole 操作)
$table = new Swoole\Table(1024); | |
// 创立表 | |
$table->column("id", Swoole\Table::TYPE_INT); | |
$table->column("name", Swoole\Table::TYPE_STRING); | |
$table->column("money", Swoole\Table::TYPE_FLOAT); | |
$table->create(); | |
// 增加数据 | |
$table->set("zq", [ | |
'id' => 1, | |
'name' => "zhiqiang", | |
'money' => 100, | |
]); | |
// 获取一行数据 | |
$table->get("zq"); | |
// 批改数据 | |
// 字段递增 | |
$table->incr("zq","money",2); | |
// 递加 | |
$table->decr("zq","money",2); | |
// 返回 table 中存在的条目数。$table->count(); | |
// 遍历 table 中的数据 | |
foreach($table as $item){var_dump($item); | |
} |
think-swoole 中的操作
先对 table 表构造进行初始化config\swoole.php
'tables' => [ | |
'user'=>[ | |
'size'=>1024, | |
'columns'=>[ | |
[ | |
'name'=>'id', | |
'type'=>\Swoole\Table::TYPE_INT | |
], | |
[ | |
'name'=>'name', | |
'type'=>\Swoole\Table::TYPE_STRING, | |
'size'=>32 | |
], | |
[ | |
'name'=>'money', | |
'type'=>\Swoole\Table::TYPE_FLOAT | |
], | |
], | |
], | |
], |
操作数据
$table = app('swoole.table.user'); | |
$table->set("zq", [ | |
'id' => 1, | |
'name' => "zhiqiang", | |
'money' => 100 | |
]); | |
// 获取一行数据 | |
$table->get("zq"); | |
// 批改数据 | |
// 字段递增 | |
$table->incr("zq", "money", 2); | |
// 递加 | |
$table->decr("zq", "money", 2); | |
// 返回 table 中存在的条目数。$table->count(); | |
// 遍历 table 中的数据 | |
foreach ($table as $item) {var_dump($item); | |
} | |
// 查看 table 中是否存在某一个 key。$table->exist('zq'); | |
// 获取理论占用内存尺寸, 单位字节 | |
$table->momorySize(); |
RPC
RPC(Remote Procedure Call):近程过程调用,它是一种通过网络从近程计算机程序上申请服务,而不须要理解底层网络技术的思维。
具体介绍:https://developer.51cto.com/a…
- 解决分布式系统中,服务之间的调用问题。
- 近程调用时,要可能像本地调用一样不便,让调用者感知不到近程调用的逻辑。
- 节点角色阐明:
- Server: 裸露服务的服务提供方
- Client: 调用近程服务的服务生产方
- Registry: 服务注册与发现的注册核心
think-swoole 实现 RPC 性能
服务器端
接口定义app/rpc/interfaces/UserInterface.php
<?php | |
namespace app\rpc\interfaces; | |
interface UserInterface | |
{public function create(); | |
public function find(int $id); | |
} |
实现接口app/rpc/services/UserService.php
<?php | |
namespace app\rpc\services; | |
use app\rpc\interfaces\UserInterface; | |
class UserService implements UserInterface | |
{public function create() | |
{// TODO: Implement create() method. | |
return "service create success"; | |
} | |
public function find(int $id) | |
{// TODO: Implement find() method. | |
return $id. "查问数据遍历"; | |
} | |
} |
注册 rpc 服务config/swoole.php
'rpc' => [ | |
'server' => [ | |
// 开启 rpc 服务 | |
'enable' => true, | |
//rpc 端口 | |
'port' => 9000, | |
'services' => [ | |
// 注册服务 | |
\app\rpc\services\UserService::class | |
], | |
], | |
// 如果填写也是能够调用其余服务端 | |
'client' => [],], |
启动服务端
php think swoole start / php think swoole:rpc
客户端
'rpc' => ['server' => [], | |
'client' => [ | |
'tp6'=>[ | |
// 服务端的 ip 地址 | |
'host'=>'127.0.0.1', | |
// 服务端对应的端口 | |
'port'=>'9000' | |
] | |
// 更多服务端 | |
], | |
], |
运行 php think rpc:interface
生成 RPC 接口文件app\rpc.php
<?php | |
/** | |
* This file is auto-generated. | |
*/ | |
declare(strict_types=1); | |
namespace rpc\contract\tp6; | |
interface UserInterface | |
{public function create(); | |
public function find(int $id); | |
} | |
return ['tp6' => ['rpc\contract\tp6\UserInterface']]; |
在控制器调用
public function index(\rpc\contract\tp6\UserInterface $user) | |
{ | |
// | |
$user->find(1); | |
// $user->create();} |
定时工作
在 think-swoole 2.0 版本的时候还是反对自定义定时工作配置, 具体参考 https://github.com/top-think/…
在 3.0 就不反对了,在这里介绍一个通用的命令行启动定时工作
php think make:command SwooleTimer
加载命令行config/console.php
'commands' => [ | |
'swooletimer'=>app\command\SwooleTimer::class | |
........... | |
], |
书写命令脚本app/command/SwooleTimer.php
<?php | |
declare (strict_types = 1); | |
namespace app\command; | |
use think\console\Command; | |
use think\console\input\Argument; | |
class SwooleTimer extends Command | |
{protected function configure() | |
{ | |
// 指令配置 | |
$this->setName('app\command\swooletimer') | |
->addArgument('action', Argument::OPTIONAL, "start | stop", 'start') | |
->setDescription('Swoole Timer for ThinkPHP'); | |
} | |
public function handle() | |
{$action = $this->input->getArgument('action'); | |
if (in_array($action, ['start','stopall'])) {$this->app->invokeMethod([$this, $action], [], true); | |
} else {$this->output->writeln("<error>Invalid argument action:{$action}, Expected start</error>"); | |
} | |
} | |
/** | |
* 启动定时工作 次要工作打算在这里书写 | |
*/ | |
protected function start() | |
{ | |
// https://wiki.swoole.com/#/timer | |
$timer_id=swoole_timer_tick(2000,function (){echo "2s 循环执行须要做的事件".time()."\n"; | |
}); | |
$this->output->writeln("Swoole Timer_id:{$timer_id}"); | |
} | |
/** | |
* 革除所有的定时工作 | |
*/ | |
protected function stop(){swoole_timer_clear_all(); | |
$this->output->writeln("Swoole Timer clear all ok"); | |
} | |
} |