共计 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");
}
}