Workman(php)实时推送示例(thinkphp)与问题排查

文章介绍了Workerman这款开源PHP应用容器在物联网和实时通讯中的应用,包括物联网设备数据实时展示、报警消息推送,以及GatewayWorker框架在服务端的部署和代码实现,同时提到了HTTPS环境下WebSocket连接的问题解决方法。
摘要由CSDN通过智能技术生成

workman

workerman是一款开源高性能PHP应用容器,它大大突破了传统PHP应用范围,被广泛的用于互联网、即时通讯、APP开发、硬件通讯、智能家居、物联网等领域的开发。

业务场景

目前接触到的场景大致分为两类:

  • 物联网方面;设备传感器传送回来的报警数据是实时展示给总控制台(如下图的多个报警数据统计图部分)
  • 实时消息推送;比如有系统有新的客户注册需要实时推送给相关业务员知晓等

代码实现

本质上其实就是服务端与客户端受代码逻辑控制下的数据交互。

GatewayWorker(服务端)

GatewayWorker基于Workerman开发的一个项目框架。因为绝大多数开发者的目标是基于Workerman开发TCP长连接应用,而长连接应用服务端有很多共同之处,例如它们有相同的进程模型以及单发、群发、广播等接口需求。所以才有了GatewayWorker框架,GatewayWorker是基于Workerman开发的一个TCP长连接框架,实现了单发、群送、广播等长连接必用的接口。

服务端部署(Linux)

在GatewayWorker官网下载对应的代码示例,在TP框架代码根目录下创建了一个workman的文件夹然后后将官方示例代码放入其中具体文件目录如下(不一定非要这么放个人习惯):
此处我在TP框架代码根目录下创建了一个workman的文件夹,让后将官方示例代码放入其中

核心代码介绍

首先需要注意的是start_gateway.php文件中对客户端外放的协议与端口(宝塔与安全组记得开放),具体代码位置如下:

// 需要注意的就是这句话
$gateway = new Gateway("tcp://0.0.0.0:8282");
//其中tcp指定的是协议,此处还可以写websocket,具体看客户端与服务端采用什么协议通讯(两者要使用相同的协议)
//其中8282是外放给客户端的通讯端口

其次就是业务逻辑处理部分,对应的文件是Events.php而通常用到的方法就是onMessage用来处理收到客户端发送的数据

//参数$client_id为当前连接服务端的客户端ID
//$message为客户端发来的数据
//此处仅仅是展示,根据实际情况自行修改
public static function onMessage($client_id, $message)
{
    $data = json_decode($message, true);//对数据进行解json处理(自行判断是否需要)
    $type=$data['type']; //通常给服务端发数据都会带一个类型字段用以区分消息
    switch ($type){
        //正常服务端会有心跳包给客户端,客户端也要在收到心跳包的时候反馈服务端以维持长连接
        case 'ping':
            return ;
            break;
        //客户端链接成功后会传一个业务上的数据身份(比如业务员数据库ID)给服务端用于数据绑定    
        case 'init':
            $uid=$data['id'];
            // 将当前连接的客户端ID与数据库中的uid绑定
            Gateway::bindUid($client_id, $uid);
            $message = array('type'=>"clock");
            // 给对应消息的客户端发初始化成功的消息
            GateWay::sendToUid($uid,json_encode($message));
            break;
    }
}
服务端启动

如上面的项目框架代码图片,进入Linux服务器项目根目录中的workman(就是自定义名字放GatewayWorker示例代码的目录)下切换www用户(这个不知道不切换行不行)执行命令:

php start.php start

成功运行截图(记得设置守护进程和开机启动):
在这里插入图片描述

主动推送

当有新的用户注册时,服务端需要主动推送消息给对应的客户端需要用到gatewayclient需要composer安装一下,示例代码如下:

//给用户发数据
public static function sendToUid($message, $admin_id)
{
	try {
			//客户端连接初始化时会绑定客户端ID与数据ID,此处检测一下这个数据ID对应的客户端ID是否在线
			if (gateway::isUidOnline($admin_id)) {
				//如果在线进行数据发送
				gateway::sendToUid($admin_id, json_encode($message, JSON_UNESCAPED_UNICODE));
			};
        } catch (\Exception $e) {
			//异常处理
        }
}

测试工具与Javascript(客户端)

在线测试工具

服务端启动成功以后我们可以百度一些在线连接工具测试一下连接是否成功,例如:WebSocket 在线测试 输入服务器IP以及上面文件中定义的外放端口(注意这个是测试websoket协议的服务端要对应上),点击连接之后出现右侧连接已建立说明服务端基本没有问题
在这里插入图片描述

JS连接

通常客户端方面我采用JS进行websocket协议进行连接,下面给出代码示例:

//建立WebSocket通讯,其中"ReconnectingWebSocket.js" 是一个 JavaScript 库,用于处理 WebSocket 连接断开后自动重新连接的功能百度自行下载提前引入即可
var socket = new ReconnectingWebSocket('ws://127.0.0.1/8282'); //此处改成自己的服务器IP与端口号

//连接成功时触发
socket.onopen = function () {
	console.log("服务连接成功!");
	//初始化用户信息 adminInfo看自己业务需求自己定义
	socket.send(adminInfo);
};

//监听收到的消息
socket.onmessage = function (res) {
	var data = eval("(" + res.data + ")");
	switch (data['type']) {
		//服务端心跳
		case 'ping':
			socket.send('{"type":"ping"}');
			break;
		//初始化成功
		case "clock":  //建立链接成功,定时刷新
			break;
		//服务端通知有新的数据
		case "message_update": //有新的消息
			console.log('有新的消息');
			layer.msg('您有新消息注意查收!', {'offset': '75%'});
			// ... 写新消息下的业务逻辑
			break;
	}
}

问题排查

  1. 当网站配置了https后,服务端使用'ws://127.0.0.1/8282'连接就会失败。即使改成'wss://127.0.0.1/8282' 或者wss://域名/8282还失败的可以采用nignx代理;

进入宝塔在网站配置页面,在配置文件中写入下面内容,具体位置可以看图片:

location /wss {
	proxy_pass http://127.0.0.1:9999;
	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "Upgrade";
	proxy_set_header X-Real-IP $remote_addr;
	proxy_read_timeout 3600s;
}

其中9999要改成实际服务端外放的端口号

在这里插入图片描述
客户端连接是写法如下:

//域名记得替换
var socket = new ReconnectingWebSocket('wss://www.baidu.com/wss');
  1. 后续如果遇到其他问题再进行补充;写的有点乱主要目的是方便自己查找(不喜勿喷)
  • 18
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
ThinkPHP6.0 + Workerman长连接中使用异步任务,可以参照以下的步骤: 1. 安装Workerman扩展和Workerman服务端库。 可以使用Composer安装Workerman扩展和Workerman服务端库,可以通过以下命令进行安装: ```bash composer require workerman/workerman composer require workerman/channel ``` 2. 创建一个Workerman服务端脚本,并在其中定义异步任务处理方法。 ```php // 引入Workerman和Channel库 use Workerman\Worker; use Workerman\Channel\Client; // 创建一个Workerman服务端 $worker = new Worker('websocket://0.0.0.0:2346'); // 创建一个Channel服务端 $channel = new Channel\Server('127.0.0.1', 2206); // 在Worker进程中创建异步任务处理方法 $worker->onWorkerStart = function($worker) use ($channel) { // 创建一个异步任务 $task = new \Workerman\AsyncTask(); // 设置异步任务的处理方法 $task->onMessage = function($task, $data) use ($channel) { // 处理业务逻辑 $result = doSomething($data); // 将处理结果发送到Channel中 $channel->push($data['channel'], $result); }; // 将异步任务注册到Worker进程中 $worker->addTask($task); }; ``` 3. 在控制器中将需要处理的数据发送到异步任务中。 ```php use think\facade\Db; use Workerman\Channel\Client; class Index { public function index() { // 创建一个Channel客户端 $channel_client = new Client('127.0.0.1', 2206); // 发送数据到异步任务中处理 $data = [ 'channel' => 'my_channel', // 定义Channel名称 'data' => [ 'user_id' => 1, 'content' => 'hello world', ], ]; $task_id = $channel_client->asend($data); // 获取异步任务的处理结果 $result = $channel_client->pop($task_id); return json($result); } } ``` 4. 在Workerman服务端中监听Channel,获取异步任务的处理结果。 ```php // 在Worker进程中监听Channel,获取异步任务的处理结果 $worker->onWorkerStart = function($worker) use ($channel) { // 创建一个异步任务 $task = new \Workerman\AsyncTask(); // 设置异步任务的处理方法 $task->onMessage = function($task, $data) use ($channel) { // 处理业务逻辑 $result = doSomething($data); // 将处理结果发送到Channel中 $channel->push($data['channel'], $result); }; // 将异步任务注册到Worker进程中 $worker->addTask($task); // 监听Channel,获取异步任务的处理结果 $channel->on('my_channel', function($data) use ($worker) { // 处理异步任务的处理结果 $worker->connections[$data['fd']]->send(json_encode($data['result'])); }); }; ``` 通过以上的步骤,我们就可以在ThinkPHP6.0 + Workerman长连接中使用异步任务来处理业务逻辑了。需要注意的是,异步任务的处理方法会在Worker进程中的异步任务进程中执行,所以需要将需要的资源例如MySQL连接等在onWorkerStart回调函数中创建。同时,需要使用Channel来进行异步任务的处理结果获取,避免阻塞主进程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值