官網(wǎng)文檔
thinkphp6文檔 https://www.kancloud.cn/manual/thinkphp6_0/1037479 swoole文檔 https://wiki.swoole.com/#/ think-swoole文檔 https://www.kancloud.cn/manual/thinkphp6_0/1359700
安裝
composer require topthink/think-swoole
命令行
php think swoole [start|stop|reload|restart]
服務(wù)啟動(dòng)
當(dāng)你在命令行php think swoole
下執(zhí)行完成之后就會(huì)啟動(dòng)一個(gè)HTTP Server,可以直接訪(fǎng)問(wèn)當(dāng)前的應(yīng)用【推薦教程:thinkphp】
'server' => [ 'host' => env('SWOOLE_HOST', '0.0.0.0'), // 監(jiān)聽(tīng)地址 'port' => env('SWOOLE_PORT', 9501), // 監(jiān)聽(tīng)端口 'mode' => SWOOLE_PROCESS, // 運(yùn)行模式 默認(rèn)為SWOOLE_PROCESS 'sock_type' => SWOOLE_SOCK_TCP, // sock type 默認(rèn)為SWOOLE_SOCK_TCP 'options' => [ // 服務(wù)啟動(dòng)后,進(jìn)程ID存放文件 'pid_file' => runtime_path() . 'swoole.pid', // swoole 的日志文件 'log_file' => runtime_path() . 'swoole.log', // 守護(hù)進(jìn)程模式設(shè)置 true 后臺(tái)運(yùn)行 'daemonize' => false, // 設(shè)置啟動(dòng)的reactor線(xiàn)程數(shù) 'reactor_num' => swoole_cpu_num(), // 設(shè)置啟動(dòng)的worker進(jìn)程數(shù) 'worker_num' => swoole_cpu_num(), //配置Task進(jìn)程的數(shù)量 'task_worker_num' => swoole_cpu_num(), //開(kāi)啟靜態(tài)文件請(qǐng)求處理,需配合document_root 'enable_static_handler' => true, //靜態(tài)文件根目錄 'document_root' => root_path('public'), // 設(shè)置最大數(shù)據(jù)包尺寸,單位字節(jié) 'package_max_length' => 20 * 1024 * 1024, //配置發(fā)送輸出緩沖區(qū)內(nèi)存尺寸 'buffer_output_size' => 10 * 1024 * 1024, //設(shè)置客戶(hù)端連接最大允許占用的內(nèi)存數(shù)量 'socket_buffer_size' => 128 * 1024 * 1024, ], ],
熱更新
swoole服務(wù)器運(yùn)行過(guò)程中php文件是常駐內(nèi)存運(yùn)行,這樣就可以避免重復(fù)的讀取磁盤(pán),重復(fù)的解釋編譯php,以便達(dá)到最高的性能,所以修改代碼需要重啟服務(wù)
think-swoole擴(kuò)展提供熱更新功能,在檢測(cè)相關(guān)文件有更新會(huì)自動(dòng)重啟,不在需要手動(dòng)完成重啟,方便開(kāi)發(fā)調(diào)試
生產(chǎn)環(huán)境下不建議開(kāi)始文件監(jiān)控,性能損耗,正常情況下你所修改的文件需要確認(rèn)無(wú)誤才能進(jìn)行更新部署
.env
里面設(shè)置APP_DEBUG = true
會(huì)默認(rèn)開(kāi)啟熱更新
'hot_update' => [ 'enable' => env('APP_DEBUG', false), 'name' => ['*.php'], 'include' => [app_path()], 'exclude' => [], ],
參數(shù)說(shuō)明
參數(shù) | 說(shuō)明 |
---|---|
enable | 是否開(kāi)啟熱更新 |
name | 監(jiān)聽(tīng)哪些類(lèi)型的文件變動(dòng) |
include | 監(jiān)聽(tīng)哪些目錄下的文件變動(dòng) |
exclude | 排除目錄 |
websocket
先來(lái)一個(gè)官方的例子
$server = new SwooleWebSocketServer("0.0.0.0", 9501); $server->on('open', function (SwooleWebSocketServer $server, $request) { echo "server: handshake success with fd{$request->fd}n"; }); $server->on('message', function (SwooleWebSocketServer $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} closedn"; }); $server->start();
開(kāi)啟think-swoole的websocket功能 configswoole.php
'websocket' => [ 'enable' => true, ],
創(chuàng)建三個(gè)事件
php think make:listener SwWsConnect php think make:listener SwWsClose php think make:listener SwWsMessage
然后將這三個(gè)事件寫(xiě)到到事件監(jiān)聽(tīng)中,分別有以下2中文件可以修改方式,注意二選一
thinkphp6自帶的事件綁定appevent.php
'listen' => [ ........ // 監(jiān)聽(tīng)鏈接 'swoole.websocket.Connect' => [ applistenerSwWsConnect::class ], //關(guān)閉連接 'swoole.websocket.Close' => [ applistenerSwWsClose::class ], //發(fā)送消息場(chǎng)景 'swoole.websocket.Message' => [ applistenerSwWsMessage::class ] ],
think-swoole事件綁定configswoole.php
'listen' => [ 'connect'=>applistenerSwWsConnect::class, 'close'=>applistenerSwWsClose::class, 'message'=> applistenerSwWsMessage::class ],
怎么選擇是保存在
configswoole.php
還是appevent.php
配置中呢?首先我們 我們確定一下我們這個(gè)項(xiàng)目中存在有幾個(gè)實(shí)時(shí)通訊,
如果只是存在一個(gè)實(shí)時(shí)通訊 個(gè)人建議 保存在
configswoole.php
如果是存在多個(gè)實(shí)時(shí)通訊,就保存在
appevent.php
key值 必須是
swoole.websocket.事件名稱(chēng)
例如swoole.websocket.Message
開(kāi)始寫(xiě)事件中中方法
連接事件applistenerSwWsConnect.php
public function handle($event, thinkswoolewebsocket $ws) { // 獲取當(dāng)前發(fā)送者的fd $fd = $ws->getSender(); echo "server: handshake success with fd{$fd}n"; }
關(guān)閉事件applistenerSwWsClose.php
public function handle($event, thinkswoolewebsocket $ws) { $fd = $ws->getSender(); echo "client {$fd} closedn"; }
message事件applistenerSwWsMessage.php
public function handle($event, thinkswoolewebsocket $ws) { $fd = $ws->getSender(); $data = json_encode($event); echo "receive from {$fd}:{$data}n"; $ws->emit("this is server", $fd); }
啟動(dòng)php think swoole
進(jìn)行測(cè)試
think-swoole中的websocket方法總結(jié)
//給自己發(fā)消息 $ws->emit("this is server", $ws->getSender()); //給指定一個(gè)fd發(fā)消息 $ws->to($to)->emit("messagecallback",$data); //給指定多個(gè)人發(fā)消息 $ws->to([1,2,3])->emit("messagecallback",$data); //發(fā)送給所有的(不包含自己) $ws->broadcast()->emit("messagecallback",$data); //模擬formfd 給tofd 發(fā)送消息 $ws->setSender($formfd)->to($tofd)->emit("messagecallback",$data);
注意:在多個(gè)實(shí)時(shí)通訊場(chǎng)景下使用
emit
第一個(gè)參數(shù)傳入 傳入 事件名稱(chēng)callback 例如
messagecallback
如果你發(fā)現(xiàn)你think-swoole中有些沒(méi)有swoole中的方法可以這么干
$sw = app('swoole.server'); $sw = app("thinkswooleManager")->getServer(); //以上二選一 $es = $sw->isEstablished($fd); //檢查連接是否為有效的WebSocket客戶(hù)端連接 var_dump($es);
聊天室room實(shí)現(xiàn)
前端文件參考 htmlroom.html
或 htmlroom-socket-io.html
php think make:listener SwRoomJoin php think make:listener SwRoomLeave php think make:listener SwRoomMessage
事件綁定
// 加入房間 'swoole.websocket.RoomJoin' => [ applistenerSwRoomJoin::class ], // 離開(kāi)房間 'swoole.websocket.Roomleave' => [ applistenerSwRoomLeave::class ], // 在房間發(fā)消息 'swoole.websocket.RoomMessage' => [ applistenerSwRoomMessage::class ]
加入房間邏輯
public function handle($event, thinkswoolewebsocket $ws, thinkswoolewebsocketroom $room) { $fd = $ws->getSender(); //客戶(hù)端假如定的room $roomid = $event['room']; //獲取指定房間下有哪些客戶(hù)端 $roomfds = $room->getClients($roomid); // 判斷這個(gè)房間有沒(méi)有自己 如果有自己就不需要再次發(fā)送通知 if (in_array($fd, $roomfds)) { $ws->to($roomfds)->emit("roomjoincallback", "房間{$roomid}已加入"); return; } //加入房間 $ws->join($roomid); $ws->to($roomfds)->emit("roomjoincallback", "{$fd}加入房間{$roomid}成功"); }
離開(kāi)房間邏輯
public function handle($event, thinkswoolewebsocket $ws, thinkswoolewebsocketRoom $room) { $roomid = $event['room']; $fd = $ws->getSender(); $roomfds = $room->getClients($roomid); if (!in_array($fd, $roomfds)) { $ws->emit("roomleavecallback", "{$fd}不在{$roomid}房間內(nèi),怎么離開(kāi)~"); return; } //離開(kāi)房間 $ws->leave($roomid); //獲取當(dāng)前客戶(hù)端加入了哪些客戶(hù)端 $rooms = $room->getRooms($fd); $ws->to($roomfds)->emit("roomleavecallback", "{$fd}已離開(kāi)了~~"); }
在房間發(fā)布聊天邏輯
public function handle($event, thinkswoolewebsocket $ws, thinkswoolewebsocketroom $room) { // $roomid = $event['room']; $text = $event['text']; $fd = $ws->getSender(); $roomfds = $room->getClients($roomid); if (!in_array($fd, $roomfds)) { $ws->emit("roommessagecallback", "{$fd}不在{$roomid}房間內(nèi),無(wú)法進(jìn)入發(fā)布聊天~"); return; } $ws->to($roomfds)->emit("roommessagecallback", $text); }
事件訂閱
php think make:listener SwSubscribe
applistenerSwSubscribe.php
<?php declare (strict_types = 1); namespace applistener; class SwSubscribe { protected $ws = null; // public function __construct() // { // $this->ws = app('thinkswooleWebsocket'); // } public function __construct(thinkContainer $c) { $this->ws = $c->make(thinkswooleWebsocket::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} closedn"; } 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); } }
有點(diǎn)類(lèi)似 將原生的swoole代碼改成面向?qū)ο蟠a,生效方法
configswoole.php
中在subscribe
加入applistenerSwSubscribe::class
'subscribe' => [ applistenerSwSubscribe::class ],在
appevent.php
文件中的swoole.websocket.Connect
相當(dāng)于applistenerSwSubscribe.php
文件中的onConnect
函數(shù)。如果同時(shí)存在的存在的話(huà),就會(huì)向客戶(hù)端發(fā)送2次以上的消息
Task任務(wù)投遞
https://wiki.swoole.com/#/start/start_task
生成事件
php think make:listener SwSendEmailTask
編寫(xiě)發(fā)送郵件方法applistenerSwSendEmailTask.php
public function handle($event) { var_dump($event); // echo "開(kāi)發(fā)發(fā)送郵件".time(); sleep(3); echo "結(jié)束發(fā)送郵件".time(); }
注冊(cè)事件appevent.php
'swoole.task'=>[ applistenerSwSendEmailTask::class ],
在控制器中投遞任務(wù)
public function doRegister() { $server = app('swoole.server'); $server->task(applistenerSwSendEmailTask::class); return "注冊(cè)成功"; } public function doRegister(thinkswooleManager $manager) { $server = $manager->getServer(); $server->task(applistenerSwSendEmailTask::class); return "注冊(cè)成功"; } public function doRegister(SwooleServer $server) { $server->task(applistenerSwSendEmailTask::class); return "注冊(cè)成功"; }
三種獲取
SwooleServer
,任意選其一
在swoole中還有一個(gè)事件叫finish
,它的作用就是把異步任務(wù)的結(jié)果返回,在think-swool是這么處理的
定義一個(gè)發(fā)送郵件異步任務(wù)處理結(jié)果的事件
php think make:listener SwSendEmailFinish
注冊(cè)事件appevent.php
'swoole.finish'=>[ applistenerSwSendEmailFinish::class ],
在task任務(wù)中調(diào)用
public function handle($event) { var_dump($event); // echo "開(kāi)發(fā)發(fā)送郵件".time(); sleep(3); echo "結(jié)束發(fā)送郵件".time(); $event->finish(applistenerSwSendEmailFinish::class); }
高性能共享內(nèi)存 Table
https://wiki.swoole.com/#/mem…
先定結(jié)構(gòu)在進(jìn)行操作數(shù)據(jù)(原生swoole操作)
$table = new SwooleTable(1024); //創(chuàng)建表 $table->column("id", SwooleTable::TYPE_INT); $table->column("name", SwooleTable::TYPE_STRING); $table->column("money", SwooleTable::TYPE_FLOAT); $table->create(); //添加數(shù)據(jù) $table->set("zq", [ 'id' => 1, 'name' => "zhiqiang", 'money' => 100, ]); //獲取一行數(shù)據(jù) $table->get("zq"); // 修改數(shù)據(jù) // 字段遞增 $table->incr("zq","money",2); //遞減 $table->decr("zq","money",2); // 返回 table 中存在的條目數(shù)。 $table->count(); //遍歷table中的數(shù)據(jù) foreach($table as $item){ var_dump($item); }
think-swoole中的操作
先對(duì)table表結(jié)構(gòu)進(jìn)行初始化configswoole.php
'tables' => [ 'user'=>[ 'size'=>1024, 'columns'=>[ [ 'name'=>'id', 'type'=>SwooleTable::TYPE_INT ], [ 'name'=>'name', 'type'=>SwooleTable::TYPE_STRING, 'size'=>32 ], [ 'name'=>'money', 'type'=>SwooleTable::TYPE_FLOAT ], ], ], ],
操作數(shù)據(jù)
$table = app('swoole.table.user'); $table->set("zq", [ 'id' => 1, 'name' => "zhiqiang", 'money' => 100 ]); //獲取一行數(shù)據(jù) $table->get("zq"); // 修改數(shù)據(jù) // 字段遞增 $table->incr("zq", "money", 2); //遞減 $table->decr("zq", "money", 2); // 返回 table 中存在的條目數(shù)。 $table->count(); //遍歷table中的數(shù)據(jù) foreach ($table as $item) { var_dump($item); } // 檢查 table 中是否存在某一個(gè) key。 $table->exist('zq'); //獲取實(shí)際占用內(nèi)存尺寸,單位字節(jié) $table->momorySize();
RPC
RPC(Remote Procedure Call):遠(yuǎn)程過(guò)程調(diào)用,它是一種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的思想。
詳細(xì)介紹:https://developer.51cto.com/a…
- 解決分布式系統(tǒng)中,服務(wù)之間的調(diào)用問(wèn)題。
- 遠(yuǎn)程調(diào)用時(shí),要能夠像本地調(diào)用一樣方便,讓調(diào)用者感知不到遠(yuǎn)程調(diào)用的邏輯。
- 節(jié)點(diǎn)角色說(shuō)明:
- Server: 暴露服務(wù)的服務(wù)提供方
- Client: 調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方
- Registry: 服務(wù)注冊(cè)與發(fā)現(xiàn)的注冊(cè)中心
think-swoole實(shí)現(xiàn)RPC功能
服務(wù)器端
接口定義app/rpc/interfaces/UserInterface.php
<?php namespace apprpcinterfaces; interface UserInterface { public function create(); public function find(int $id); }
實(shí)現(xiàn)接口app/rpc/services/UserService.php
<?php namespace apprpcservices; use apprpcinterfacesUserInterface; 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. "查詢(xún)數(shù)據(jù)遍歷"; } }
注冊(cè)rpc服務(wù)config/swoole.php
'rpc' => [ 'server' => [ //開(kāi)啟rpc服務(wù) 'enable' => true, //rpc端口 'port' => 9000, 'services' => [ //注冊(cè)服務(wù) apprpcservicesUserService::class ], ], // 如果填寫(xiě)也是可以調(diào)用其他服務(wù)端 'client' => [ ], ],
啟動(dòng)服務(wù)端
php think swoole start / php think swoole:rpc
客戶(hù)端
'rpc' => [ 'server' => [ ], 'client' => [ 'tp6'=>[ //服務(wù)端的ip地址 'host'=>'127.0.0.1', //服務(wù)端對(duì)應(yīng)的端口 'port'=>'9000' ] //
相關(guān)推薦
- AI工具導(dǎo)航網(wǎng)站,未來(lái)的發(fā)展前景怎么樣?
- 多線(xiàn)服務(wù)器具有什么優(yōu)勢(shì)?5大隱藏優(yōu)勢(shì)告別單線(xiàn)卡頓
- 在RAKsmart服務(wù)器上怎么管理數(shù)據(jù)科學(xué)工作流
- 機(jī)房托管業(yè)務(wù)包括哪些
- raksmart日本云服務(wù)器產(chǎn)品優(yōu)勢(shì)
- 當(dāng)分成收益腰斬:站長(zhǎng)必須掌握的3條自媒體變現(xiàn)新通路
- 算力服務(wù)器多少錢(qián)一臺(tái)??jī)r(jià)格因素與選購(gòu)指南
- 美國(guó)站群服務(wù)器搭建sk5需要什么配置?