windows環境下TP5.1使用think-worker(Workerman/GatewayWorker)

文章目錄

    • 首先是解決如何運行gatewayworker
    • 調試gatewayworker程序
    • 向指定客戶端發送消息
    • 在TP框架中調用Gateway的API
    • 總結說明

測試環境 windows10;PHP7.2;TP5.1;

這里只介紹如何使用TP集成的workerman擴展庫think-worker,原生workerman的使用請參考官方文檔

TP5.1集成了workerman,使用composer require topthink/think-worker=2.0.*安裝即可。
TP5.1只能安裝think-worker2.0版本,最新的think-worker3.0版本是給TP6.0用的,但依賴安裝workerman的版本是最新的。

雖然集成了,但是在windows下使用還是有許多問題,比如直接運行命令php think woker:gateway會報錯GatewayWorker Not Support On Windows. windows解決方案 ,Linux下可以直接運行(應該吧~)。
官方的使用文檔也不夠詳細,只列舉了workerworker:server兩種運行方式的簡單示列。但是大部分使用workerman都是奔著GatewayWorker去的,畢竟自己用workerman完全搭建還是需要技術和時間的。

單純的使用workerman,直接運行php think workerphp think worker:server就可以,調試也非常簡單,TP官方文檔有說明就不贅述了,重點是gatewayworker。

首先是解決如何運行gatewayworker

根據workerman的文檔,windows下不能在同一個php文件中運行多個worker,所以需要修改tinkphp的命令行
新建自定義命令行文件application\common\command\Workerman.php

<?phpnamespace app\common\command;use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Config;
use Workerman\Worker;/*** Worker 命令行*/
class Workerman extends Command
{protected function configure(){$this->setName('workerman')->addArgument('service', Argument::OPTIONAL, 'workerman service: gateway|register|businessworker', null)->addOption('host', 'H', Option::VALUE_OPTIONAL, 'the host of workerman server', null)->addOption('port', 'P', Option::VALUE_OPTIONAL, 'the port of workerman server', null)->addOption('daemon', 'd', Option::VALUE_OPTIONAL, 'Run the workerman server in daemon mode.')->setDescription('workerman Server for ThinkPHP');}public function execute(Input $input, Output $output){$service = $input->getArgument('service');$option = Config::pull('gateway_worker');if ($input->hasOption('host')) {$host = $input->getOption('host');} else {$host = !empty($option['host']) ? $option['host'] : '0.0.0.0';}if ($input->hasOption('port')) {$port = $input->getOption('port');} else {$port = !empty($option['port']) ? $option['port'] : '2347';}$registerAddress = !empty($option['registerAddress']) ? $option['registerAddress'] : '127.0.0.1:1236';switch ($service) {case 'register':$this->register($registerAddress);break;case 'businessworker':$this->businessWorker($registerAddress, isset($option['businessWorker']) ? $option['businessWorker'] : []);break;case 'gateway':$this->gateway($registerAddress, $host, $port, $option);break;default:$output->writeln("<error>Invalid argument action:{$service}, Expected gateway|register|businessworker .</error>");exit(1);break;}Worker::runAll();}/*** 啟動register* @access public* @param  string   $registerAddress* @return void*/public function register($registerAddress){// 初始化registernew Register('text://' . $registerAddress);}/*** 啟動businessWorker* @access public* @param  string   $registerAddress registerAddress* @param  array    $option 參數* @return void*/public function businessWorker($registerAddress, $option = []){// 初始化 bussinessWorker 進程$worker = new BusinessWorker();$this->option($worker, $option);$worker->registerAddress = $registerAddress;}/*** 啟動gateway* @access public* @param  string   $registerAddress registerAddress* @param  string   $host 服務地址* @param  integer  $port 監聽端口* @param  array    $option 參數* @return void*/public function gateway($registerAddress, $host, $port, $option = []){// 初始化 gateway 進程if (!empty($option['socket'])) {$socket = $option['socket'];unset($option['socket']);} else {$protocol = !empty($option['protocol']) ? $option['protocol'] : 'websocket';$socket   = $protocol . '://' . $host . ':' . $port;unset($option['host'], $option['port'], $option['protocol']);}$gateway = new Gateway($socket, isset($option['context']) ? $option['context'] : []);// 以下設置參數都可以在配置文件中重新定義覆蓋$gateway->name                 = 'Gateway';$gateway->count                = 4;$gateway->lanIp                = '127.0.0.1';$gateway->startPort            = 2000;$gateway->pingInterval         = 30;$gateway->pingNotResponseLimit = 0;$gateway->pingData             = '{"type":"ping"}';$gateway->registerAddress      = $registerAddress;// 全局靜態屬性設置foreach ($option as $name => $val) {if (in_array($name, ['stdoutFile', 'daemonize', 'pidFile', 'logFile'])) {Worker::${$name} = $val;unset($option[$name]);}}$this->option($gateway, $option);}/*** 設置參數* @access protected* @param  Worker   $worker Worker對象* @param  array    $option 參數* @return void*/protected function option($worker, array $option = []){// 設置參數if (!empty($option)) {foreach ($option as $key => $val) {$worker->$key = $val;}}}
}

application\command.php命令行參數配置文件中添加

return ['workerman' => '\\app\\common\\command\\Workerman',
];

打開三個cmd命令窗口,分別運行
php think workerman register
php think workerman businessworker
php think workerman gateway

運行結果
在這里插入圖片描述

調試gatewayworker程序

添加Events監聽事件文件application\workerman\Events.php,這里偷懶直接復制了官方的Events文件,自己寫的話,方法沒寫全運行時會報錯退出,所以干脆直接全部復制,修改一下命名空間即可。

<?phpnamespace app\workerman;use GatewayWorker\Lib\Gateway;
use think\worker\Application;
use Workerman\Worker;/*** Worker 命令行服務類*/
class Events
{/*** onWorkerStart 事件回調* 當businessWorker進程啟動時觸發。每個進程生命周期內都只會觸發一次** @access public* @param  \Workerman\Worker    $businessWorker* @return void*/public static function onWorkerStart(Worker $businessWorker){$app = new Application;$app->initialize();}/*** onConnect 事件回調* 當客戶端連接上gateway進程時(TCP三次握手完畢時)觸發** @access public* @param  int       $client_id* @return void*/public static function onConnect($client_id){Gateway::sendToCurrentClient("Your client_id is $client_id");}/*** onWebSocketConnect 事件回調* 當客戶端連接上gateway完成websocket握手時觸發** @param  integer  $client_id 斷開連接的客戶端client_id* @param  mixed    $data* @return void*/public static function onWebSocketConnect($client_id, $data){var_export($data);}/*** onMessage 事件回調* 當客戶端發來數據(Gateway進程收到數據)后觸發** @access public* @param  int       $client_id* @param  mixed     $data* @return void*/public static function onMessage($client_id, $data){Gateway::sendToAll($data);}/*** onClose 事件回調 當用戶斷開連接時觸發的方法** @param  integer $client_id 斷開連接的客戶端client_id* @return void*/public static function onClose($client_id){GateWay::sendToAll("client[$client_id] logout\n");}/*** onWorkerStop 事件回調* 當businessWorker進程退出時觸發。每個進程生命周期內都只會觸發一次。** @param  \Workerman\Worker    $businessWorker* @return void*/public static function onWorkerStop(Worker $businessWorker){echo "WorkerStop\n";}
}

修改配置監聽文件config\gateway_worker.php

     // BusinsessWorker配置'businessWorker'        => ['name'         => 'BusinessWorker','count'        => 1,'eventHandler' => '\app\workerman\Events', // 原來是\think\worker\Events,改成自己的監聽文件位置],

添加前端測試文件,這里使用的是vue,關于前端如何使用webSocket,網上到處都是,也很簡單。

// vue測試代碼片段
export default {data () {return {websocket: null}},mounted () {this.websocket = new WebSocket('ws://127.0.0.1:2348') // 使用gateway的地址this.websocket.onmessage = evt => {console.log(evt.data) // 打印接收的消息}}
}

重啟businessworker服務,運行vue,前端控制臺會打印

Your client_id is 7f00000107d000000001

這是在Events文件監聽事件onConnect中的程序,當客戶端連接時,向當前客戶端發送信息,多開幾個窗口,測試多客戶端連接時的效果。

向指定客戶端發送消息

首先需要明確的是,gatewayworker只能通過client_id識別客戶端,每產生一次連接,就會生成一個client_id,即便是同一個頁面,發生了多次連接,gatewayworker也會認為是不同的客戶端。
實際業務中客戶端往往是以用戶id或其他形式的id作為區分,所以實際業務中需要將client_id和業務id進行綁定并做判斷,這里做測試就不深入討論了,直接用client_id進行測試

修改前端文件

// vue模板代碼片段
<template><el-row type="flex"><el-select v-model="selectClientId"><el-option v-for="(item, index) in clients" :key="index" :value="item" :label="item" /></el-select><el-input v-model="message"></el-input><el-button @click="submit">發送</el-button></el-row>
</template>
// vue js代碼片段data () {return {websocket: null,clients: [], // client用戶列表selectClientId: '', // 選擇的用戶message: '' // 需要發送的消息}},methods: {submit () {const data = {client_id: this.selectClientId, // 指定的客戶端idmessage: this.message}this.websocket.send(JSON.stringify(data))}},mounted () {this.websocket = new WebSocket('ws://127.0.0.1:2348') // 使用gateway的地址this.websocket.onmessage = evt => {const data = JSON.parse(evt.data)if (data.type === 'login') {this.clients.push(data.client_id)}console.log(data.message)}}

修改監聽文件,修改了onConnect和onMessage兩個監聽回調

    # ...public static function onConnect($client_id){// Gateway::sendToCurrentClient("Your client_id is $client_id");$message = ['type' => 'login','client_id' => $client_id,'message' => 'user ' . $client_id . ' is login',];Gateway::sendToAll(json_encode($message));}# ...public static function onMessage($client_id, $data){// Gateway::sendToAll($data);$data = json_decode($data, true);$form_client = $client_id;$to_client = $data['client_id'];$message = $data['message'];$send_message = ['type' => 'message','message' => "user {$form_client} send {$message} to you",];if ($to_client) {// 如果有指定用戶,則發送給指定用戶Gateway::sendToClient($to_client, json_encode($send_message));} else {// 沒有指定用戶,發送給全部Gateway::sendToAll($data);}}

重啟worker服務,測試效果
在這里插入圖片描述

workerman的官方文檔中明確指出不建議直接通過客戶端發送消息,而是通過原來的框架處理業務邏輯
與ThinkPHP等框架結合

總體原則:

現有mvc框架項目與GatewayWorker獨立部署互不干擾

所有的業務邏輯都由網站頁面post/get到mvc框架中完成

GatewayWorker不接受客戶端發來的數據,即GatewayWorker不處理任何業務邏輯,GatewayWorker僅僅當做一個單向的推送通道

僅當mvc框架需要向瀏覽器主動推送數據時才在mvc框架中調用Gateway的API(GatewayClient)完成推送

在TP框架中調用Gateway的API

workerman官方文檔建議使用GatewayClient提供的API發送數據,這個需要額外安裝composer require workerman/gatewayclient,使用方法在官方文檔中有說明,和使用gateway一樣。但在TP的實際測試中,無需安裝也可以正常使用,這里使用的是GatewayWorker\Lib\Gateway,也不需要配置參數,可以直接使用。

TP處理業務邏輯的控制器

<?phpnamespace app\index\controller;use GatewayWorker\Lib\Gateway;
use think\Controller;class Index extends Controller
{public function index(){$client_id = $this->request->get('client_id');$send_message = $this->request->get('message');$message = ['type' => 'message','message' => $this->request->get('message'),];Gateway::sendToClient($client_id, json_encode($message));}
}

瀏覽器直接訪問或ajax訪問效果一致,運行結果
在這里插入圖片描述

至此,TP5.1中使用think-worker調試基本通過,剩下的就是根據實際業務邏輯進行處理了。

總結說明

在官方文檔 與ThinkPHP等框架結合 的使用說明中和案例中發現,不需要在Events監聽文件中寫業務邏輯和判斷,所有的業務邏輯都可以在TP框架中完成,Events的作用僅僅是將client_id告訴客戶端。

而在tink-worker原來的Events文件中,當客戶端連接時,就向當前客戶端發送過一條信息"Your client_id is 7f00000107d000000001",使用正則匹配就能拿到client_id,無需更改文件。

那么TP5.1的think-worker的使用可以簡化如下

  1. windows下修改gatewayworker的啟動方式,Linux無需更改(我也沒有測試)。
  2. php業務邏輯中使用GatewayWorker\Lib\Gateway調用gateway的API給客戶端發送消息。

所以不需要過多的更改gateway的配置文件,也不需要額外的建立監聽文件,就可以直接使用gateway了,當然windows環境下因為機制問題,所以更改了啟動方式。饒了一大圈回來,發現think-worker的使用方式是如此簡單,所以官方文檔是覺得太簡單了所以沒有給使用說明的必要么😓

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281261.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281261.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281261.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

webpack之DefinePlugin使用

DefinePlugin是webpack注入全局變量的插件&#xff0c;通常使用該插件來判別代碼運行的環境變量。在使用該插件需要注意的是&#xff0c;如果在該插件配置了相關的參數&#xff0c;必須要源碼中使用&#xff0c;webpack才會注入。例如&#xff1a; new webpack.DefinePlugin({p…

Magicodes.IE 2.7.0發布

2.7.02022.11.07使用SkiaSharp替代SixLabors.ImageSharp移除SixLabors.Fonts感謝linch90的大力支持&#xff08;具體見pr#462&#xff09;部分方法改為虛方法2.7.0-beta2022.10.27使用SixLabors.ImageSharp替代System.Drawing&#xff0c;感謝linch90 &#xff08;見pr#454&…

Mobx 與 Redux 的性能對比

在本文中你將看到我最終得出的結論是 Mobx 的性能優于 Redux。但很明顯這樣的結論是片面的&#xff0c;甚至是有失偏頗的&#xff0c;因為我只選取了一個的場景對兩者進行測試。可能真實的情況恰恰相反&#xff0c;Mobx 僅僅在我測試的這個場景中優于 Redux&#xff0c;但是在我…

linux lsof/netstat查看進程和端口號相關命令:

本文為博主原創&#xff0c;未經允許不得轉載&#xff1a; 在linux操作時&#xff0c;經常要查看運行的項目的進程和端口號&#xff0c;在這里總結了以下常用到的相關命令&#xff1a; 1.查看系統運行的java項目&#xff0c;并查看進程號 這個用到的命令為&#xff1a; ps -ef|…

C#高級編程9 第17章 使用VS2013-C#特性

C#高級編程9 第17章 使用VS2013 編輯定位到 如果默認勾選了這項&#xff0c;請去掉勾選&#xff0c;因為勾選之后解決方案的目錄會根據當前文件選中。 可以設置項目并行生成數 版本控制軟件設置 所有文本編輯器行號顯示 啟用編輯繼續 收集調試信息&#xff0c;將影響性能 Code …

還在手畫C#依賴關系圖嗎?快來試試這個工具吧!

還在手畫C#依賴關系圖嗎&#xff1f;快來試試這個工具吧&#xff01;筆者最近見到了一個不錯的工具&#xff0c;可以讓大家在看代碼的時候一鍵生成C#依賴的類圖。非常適合編寫文檔、查看和學習開源項目設計時使用&#xff0c;比如下方就是筆者通過這個工具生成的Microsoft.Exte…

Web服務器 - Apache配置介紹

基本語法 常量的定義與使用&#xff0c;使用關鍵詞 Define 可以定義常量&#xff0c;使用 ${} 插入常量&#xff0c;如下 語法規則說明示列Define定義常量Define SRVROOT “D:/srv/Apache24”${}使用常量ServerRoot “${SRVROOT}”/表示路徑時使用 / 而不使用 \D:/srv/Apache…

點火開關分為4個檔位,分別是off,acc,IG-on,和ST

off全車除了常火&#xff08;如應急燈&#xff0c;時鐘等的記憶功能&#xff09;外&#xff0c;均不供電。acc 是附件檔&#xff0c;部分車載附屬設備供電&#xff0c;如視聽系統&#xff0c;儀表燈&#xff0c;燈光等。也就是說&#xff0c;車停在哪里&#xff0c;發動機不轉&…

h5的formData 上傳文件及.net后臺

先來前端的代碼&#xff1a; html 代碼&#xff1a; <input type"file" id"files" value"" multiple/> js代碼&#xff1a; function init() {var ele_files document.querySelector("#files");ele_files.addEventListener(&qu…

51 Nod 1027 大數乘法【Java大數亂搞】

1027 大數乘法 基準時間限制&#xff1a;1 秒 空間限制&#xff1a;131072 KB 分值: 0 難度&#xff1a;基礎題 給出2個大整數A,B&#xff0c;計算A*B的結果。Input第1行&#xff1a;大數A 第2行&#xff1a;大數B (A,B的長度 < 1000&#xff0c;A,B > 0&#xff09; Out…

關于ASP.NET Core WebSocket實現集群的思考

前言提到WebSocket相信大家都聽說過&#xff0c;它的初衷是為了解決客戶端瀏覽器與服務端進行雙向通信&#xff0c;是在單個TCP連接上進行全雙工通訊的協議。在沒有WebSocket之前只能通過瀏覽器到服務端的請求應答模式比如輪詢&#xff0c;來實現服務端的變更響應到客戶端&…

windows環境下Apache+PHP+MySQL搭建服務器

相關文件下載 下載地址Apachehttps://www.apachehaus.com/cgi-bin/download.plxPHPhttps://windows.php.net/downloadMySQLhttps://dev.mysql.com/downloads/mysql/MySQL MySQL配置 當前使用的MySQL版本是8.0.18&#xff0c;在MySQL根目錄下新建my.ini文件&#xff0c;下面是…

angular.js國際化模塊

最近需要將一個項目轉化成英文的&#xff0c; 于是發現一個angular模塊angular-translate&#xff0c;實現如下&#xff1a; 1.安裝包 bower install angular-translate bower install angular-translate-loader-static-files //然后在頁面引用進去 <script src"/angul…

觸屏網站如何實現返回并刷新

目的 在會員中心等頁面常常會遇到進入內頁修改信息&#xff0c;返回前一個頁面需要更新信息的場景。 思路 用COOKIE記錄當前頁面是否需要刷新&#xff0c;返回之后再刷新一次頁面。 方案 下載js.cookie.js然后引入到項目中 https://github.com/js-cookie/js-cookie 先來一個最簡…

更快,更強的.NET 7 發布了

.NET Conf 2022 在昨晚(11?8?) 11 點 正式開始了&#xff0c;為期三天的會議&#xff08;11?8-10?&#xff09;&#xff0c; 圍繞 .NET 7 展開。相信各位?伙伴都已經開始安裝 .NET 7 正式版本還有以及相關的開發?具。這次 .NET 7 圍繞傳統的 C# &#xff0c;ASP.NET Core…

Web服務器 - Nginx配置介紹

nginx的配置相對簡單&#xff0c;總體來說分為5種模塊 全局塊&#xff1a;配置影響nginx全局的指令。一般有運行nginx服務器的用戶組&#xff0c;nginx進程pid存放路徑&#xff0c;日志存放路徑&#xff0c;配置文件引入&#xff0c;允許生成worker process數等。events塊&…

jvm(Java virtual machine) JVM架構解釋

2019獨角獸企業重金招聘Python工程師標準>>> JVM 架構解釋 每個Java開發者都知道通過JRE【Java運行環境】執行字節碼。 但是很多人都不知道JRE是JVM實現的事實。JVM負責執行字節碼的分析 代碼的解釋和運行。 我們應該了解JVM的架構&#xff0c;這對開發者來說是很重…

Hyper-V 嵌套虛擬化

先決條件運行 Windows Server 2016 或Windows 10 周年更新的 Hyper-V 主機。運行 Windows Server 2016 或Windows 10 周年更新的 Hyper-V VM。配置版本為 8.0 或更高的 Hyper-V VM。采用 VT-x 和 EPT 技術的 Intel 處理器&#xff08;AMD-V技術的暫時不支持&#xff09;>Set…

簡單的面試題簡解思路(搜集)

1. 統計字符串中單詞出現次數 "hi how are you i am fine thank you youtube am am "&#xff0c;統計"you"出現的次數。 方法一 : split() function wordCount(str,word){var str str || "";var word word || "";var strArr s…

WinForm(十五)窗體間通信

在很多WinForm的程序中&#xff0c;會有客戶端之間相互通信的需求&#xff0c;或服務端與客戶端通信的需求&#xff0c;這時就要用到TCP/IP的功能。在.NET中&#xff0c;主要是通過Socket來完成的&#xff0c;下面的例子是通過一個TcpListerner作為監聽&#xff0c;等待TcpClie…