## 官網文檔
> thinkphp6文檔
> http://www.hmoore.net/manual/thinkphp6_0/1037479
> swoole文檔
> https://wiki.swoole.com/#/
> think-swoole文檔
> http://www.hmoore.net/manual/thinkphp6_0/1359700
## 安裝
~~~
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
~~~
app\listener\SwSubscribe.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/#/start/start_task
生成事件
~~~
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/#/memory/table
先定結構在進行操作數據(原生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/art/201906/597963.htm
- 解決分布式系統中,服務之間的調用問題。
- 遠程調用時,要能夠像本地調用一樣方便,讓調用者感知不到遠程調用的邏輯。
- 節點角色說明:
- 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();
}
~~~
殺死所有指定名稱進程
~~~
ps -ef | grep swoole | grep -v grep | awk '{print $2}' | xargs kill -9
~~~
- 序言
- 基礎知識
- thinkphp基礎知識
- Thinkphp5CURD
- 數據庫創建
- 數據庫刪除
- 數據庫更新
- 數據庫查詢
- thinkphp5控制器
- 空操作空控制器
- 控制器基類
- 請求信息
- 行為和鉤子
- thinkphp5路由設置
- 變量路由
- 常用方法清單
- 環境搭建
- lnmp
- 升級php
- window環境
- Thinkphp小案例
- 分類管理
- 數據庫設計
- 模型
- 控制器
- 視圖
- 文件上傳
- 上傳接口
- 視圖
- 表單提交
- 視圖設計
- 控制器
- 權限控制
- 案例解釋說明
- 登錄驗證
- Laravel5.3登錄模式
- redis使用
- 一鍵安裝
- 程序設計與實現
- 頁面設計
- 功能設計
- 安裝功能實際
- 函數庫
- 配置文件
- 清除緩存
- 狀態值修改
- 數據庫備份還原
- controller.php
- common.php
- index.html
- importlist.html
- 完整的增刪查改
- 查詢語句
- 多語言支持
- JpGraph圖表類庫
- 微信支付
- payBase.php
- Order.php
- Oauth.php
- Jspay.php
- 下載遠程地址中的圖片
- URL重寫隱藏入口文件
- 圖片水印
- 整合百度編輯器
- Ueditor
- ueditor完整配置項
- 配置信息常見的方式
- HTTP 斷點續傳(PHP實現)
- layui.upload上傳文件或圖片
- QQ微信域名防封 預防域名封禁 強制跳轉至瀏覽器
- 蜘蛛篇
- 超簡單實現php谷歌驗證
- 采集金山詞霸每日一句
- think-swoole
- 原生PHP小案例
- 查詢修改數據庫
- mysql支付回調源碼
- pdo連接微信退款
- 前端小案例
- html快捷查詢
- layui經驗總結
- layui 表單增強插件
- Vue列表Ajax實戰教程
- PHP基礎
- 類的自動載入
- php基礎函數- 字符串函數
- php基礎函數-數學函數
- php基礎函數-數組函數
- PHP常見排序算法學習
- 請求第三方
- 從網絡下載文件
- 檢查網站是否宕機
- file_get_contents
- 算法
- php 抽獎算法(適合九宮格和大轉盤)
- 自己動手豐衣足食
- 入口文件
- start.php
- app.php
- load.php
- route.php
- JqHttp
- Jqfile
- Jqutil
- pdo連接數據庫類
- 常見的php類
- php數據接口類
- 生成多層樹狀下拉選框的工具模型
- 上傳下載類
- 微信用戶相關類
- Zip壓縮類
- 列表樹生成工具類
- 日期時間操作類
- 文件及文件夾處理類
- 字符串處理類
- php守護進程類
- RSA算法類
- php支持中英文的加密解密類
- CURL多線程請求
- 通用數據庫操作類
- 緩存類
- cookie類
- 常見的驗證方法
- 隨機密鑰
- 日志Log
- php-redis 操作類 封裝
- OpensslRsa 加密、解密、簽名、驗簽類
- 模板輸出類
- 發送郵件
- 封裝的mysqli類
- PHP時間段分割類庫
- PHP apk解包識版本號信息和ipa包信息
- 訪問客戶端信息
- http請求
- PHP 無數據庫讀寫配置文件
- 自己動手寫一個jwt類
- php實現對圖片對稱加解密(適用身份證加密等場景)
- 常見php函數
- 無限分類
- 獲取文章圖片
- 加密解密
- JSON數據輸出(適合在tp中)
- 刪除目錄和文件
- 判斷是否為手機訪問
- 獲取客戶端真實IP
- 隨機生成ip地址
- 字符串與二進制進行轉換
- 對數組進行排序
- 格式化字節大小
- 時間戳格式化
- 獲取數據的所有子孫數據的id值
- 取得視頻文件的縮略圖
- 圖片裁剪函數
- 按照每過0:00算一天
- 下載文件
- PHP隨機密碼生成
- 判斷數字大小
- 報文組成
- 通過ip定位城市
- PDO方式連接MySQL數據庫
- 數組與xml
- php字符串處理函數
- 判斷是否ajax提交
- 生成概率,用于抽獎
- 斷點續傳
- PHP使用星號替代用戶名手機和郵箱
- 獲取毫秒級別的時間戳
- php日志函數
- 隨機顏色生成器
- 時間差異計算函數
- 黑名單過濾
- 常見PHP 正則表達式
- php獲取瀏覽器類型
- 郵件發送
- 獲取qq昵稱
- 正則獲取手機號歸屬地
- 判斷是否是移動客戶端 移動設備
- gbk和utf8編碼自動識別方法
- 人性化時間顯示
- 請求API接口
- 數據庫備份
- PHP并發下安全讀寫文件函數
- PHP讀取exe軟件版本號
- PHP為任意頁面設置訪問密碼
- PHP利用百度當圖床
- 秒/分鐘/小時前
- 常見的js函數
- 短信驗證函數
- 上下收縮菜單
- jQuery 樹插件zTree
- 頁面刷新跳轉
- jquery導出報表
- js實現定時效果
- 獲取當前經緯度
- JQuery實現圖片大小自適應
- 網站運行時間
- 判斷瀏覽器類型
- 百度推送
- js對指定數據進行排序
- 常見工具方法
- JSPinyin
- 技術相關文章
- 高級PHP工程師所應該具備哪些技能
- 最簡潔的PHP程序員學習路線及建議
- 優化PHP代碼的一些建議
- TP5性能優化建議
- 程序猿專用代碼注釋:佛祖保佑,永無BUG
- 一組匹配中國大陸手機號碼的正則表達式
- Apache/Nginx/PHP服務器反爬蟲代碼大全
- 番外
- 配置shadowsocks服務端
- python
- go
- 如何在1分鐘內黑掉任何網站!
- 百度貼吧敏感詞
- 貼吧手工養號發帖教程
- 搞笑的注釋代碼
- Heroku