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.mzph.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&…

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…

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;来实现服务端的变更响应到客户端&…

更快,更强的.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…

jvm(Java virtual machine) JVM架构解释

2019独角兽企业重金招聘Python工程师标准>>> JVM 架构解释 每个Java开发者都知道通过JRE【Java运行环境】执行字节码。 但是很多人都不知道JRE是JVM实现的事实。JVM负责执行字节码的分析 代码的解释和运行。 我们应该了解JVM的架构&#xff0c;这对开发者来说是很重…

WinForm(十五)窗体间通信

在很多WinForm的程序中&#xff0c;会有客户端之间相互通信的需求&#xff0c;或服务端与客户端通信的需求&#xff0c;这时就要用到TCP/IP的功能。在.NET中&#xff0c;主要是通过Socket来完成的&#xff0c;下面的例子是通过一个TcpListerner作为监听&#xff0c;等待TcpClie…

关于Java开发需要注意的十二点流程

1.将一些需要变动的配置写在属性文件中 比如&#xff0c;没有把一些需要并发执行时使用的线程数设置成可在属性文件中配置。那么你的程序无论在DEV环境中&#xff0c;还是TEST环境中&#xff0c;都可以顺畅无阻地运行&#xff0c;但是一旦部署在PROD上&#xff0c;把它作为多线…

Unity经典游戏教程之:雪人兄弟

版权声明&#xff1a; 本文原创发布于博客园"优梦创客"的博客空间&#xff08;网址&#xff1a;http://www.cnblogs.com/raymondking123/&#xff09;以及微信公众号"优梦创客"&#xff08;微信号&#xff1a;unitymaker&#xff09;您可以自由转载&#x…

一款自用的翻译小工具,开源了

一款自用的翻译小工具&#xff0c;开源了TranslationTool作者&#xff1a;WPFDevelopersOrg - 唐宋元明清|驚鏵原文链接&#xff1a;https://github.com/Kybs0/TranslationTool此项目使用WPF MVVM开发。框架使用大于等于.NET461。Visual Studio 2019。最初是支持以下&#xff1…

【ELK集群+MQ】通用部署方案以及快速实现MQ发布订阅服务功能

前言&#xff1a;大概一年多前写过一个部署ELK系列的博客文章&#xff0c;前不久刚好在部署一个ELK的解决方案&#xff0c;我顺便就把一些基础的部分拎出来&#xff0c;再整合成一期文章。大概内容包括&#xff1a;搭建ELK集群&#xff0c;以及写一个简单的MQ服务。如果需要看一…

多语言报表的改动方法

在定义上传RTF模板的时候&#xff0c;会有一个是否可翻译的选项&#xff0c;选择之后。就能够上传xlf文件作为翻译内容。 对于已经存在的多语言类型报表&#xff0c;稍作改动之后再上传&#xff0c;可能会出现下面现象&#xff1a; 进程出现了“未完毕”的提示 想要改动非常eas…

LightOJ - 1027 A Dangerous Maze —— 期望

题目链接&#xff1a;https://vjudge.net/problem/LightOJ-1027 1027 - A Dangerous MazePDF (English)StatisticsForumTime Limit: 2 second(s)Memory Limit: 32 MBYou are in a maze; seeing n doors in front of you in beginning. You can choose any door you like. The p…

MASA MAUI Plugin (六)集成个推,实现本地消息推送[Android] 篇

背景MAUI的出现&#xff0c;赋予了广大.Net开发者开发多平台应用的能力&#xff0c;MAUI 是Xamarin.Forms演变而来&#xff0c;但是相比Xamarin性能更好&#xff0c;可扩展性更强&#xff0c;结构更简单。但是MAUI对于平台相关的实现并不完整。所以MASA团队开展了一个实验性项目…

微软加更.NET7中文手册,都有哪些新亮点?

11月8号发布了.NET7&#xff0c;从底层性能改进&#xff0c;到上层API升级&#xff0c;让.NET7综合性能再度提升&#xff01;同时发布了最新的C#11&#xff0c;也带来了很多小惊喜。如何快捷学习最新的.NET7和C#11&#xff1f;答案只有一个&#xff0c;微软官方中文文档&#x…

.NET Conf China 2022 第一批讲师阵容大揭秘!整个期待了!

目光看过来2022年12月3-4日一场社区性质的国内规模最大的线上线下.NET Conf 2022技术大会即将盛大开幕目前大会正紧锣密鼓地进行中第一批大咖讲师及主题已确定小编迫不及待想和大家分享分享嘉宾很大咖分享内容很硬核戳戳小手期待ing孔令磊维宏股份 首席架构师 十多年数控领域研…

妙用SQL Server聚合函数和子查询迭代求和

先看看下面的表和其中的数据&#xff1a;t_product该表有两个字段&#xff1a;xh和price&#xff0c; 其中xh是主索引字段&#xff0c;现在要得到如下的查询结果&#xff1a;从上面的查询结果可以看出&#xff0c;totalprice字段值的规则是从第1条记录到当前记录的price之和。如…