网络上有太多现成的laravel+swoole配置,搭建聊天系统的例子文章了,可是没有说后端主动推送的
我遇到了这样的需求:
系统已经是laravel+swoole搭建的聊天了,但是需要后端遇到订单状态改变的时候,主动推送给后台系统。我也不想另辟蹊径,感觉可以继续开发swoole的功能来满足这样的需求。
想了个后来觉得挺业余的方案:
创建一个系统用户,然后后端用这个用户来发送消息给管理后台的客服。
在实现这个方案的时候,发现主要是要解决怎么把消息插到websocket服务里面。一直围着“open,message,close”这三个监听状态在纠结。后来在翻swoole wiki的时候发现了事件onRequest
Swoole\WebSocket\Server 继承自 Swoole\Http\Server,所以 Http\Server 提供的所有 API 和配置项都可以使用。请参考 Swoole\Http\Server 章节。
- 设置了 onRequest 回调,WebSocket\Server 也可以同时作为 HTTP 服务器
- 未设置 onRequest 回调,WebSocket\Server 收到 HTTP 请求后会返回 HTTP 400 错误页面
- 如果想通过接收 HTTP 触发所有 WebSocket 的推送,需要注意作用域的问题,面向过程请使用 global 对 Swoole\WebSocket\Server 进行引用,面向对象可以把 Swoole\WebSocket\Server 设置成一个成员属性
来自swoole的demo
https://wiki.swoole.com/zh-cn/#/websocket_server?id=onrequest
class WebSocketServer
{
public $server;
public function __construct()
{
$this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$this->server->on('open', function (Swoole\WebSocket\Server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
});
$this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
$this->server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
$this->server->on('request', function ($request, $response) {
// 接收http请求从get获取message参数的值,给用户推送
// $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送
foreach ($this->server->connections as $fd) {
// 需要先判断是否是正确的websocket连接,否则有可能会push失败
if ($this->server->isEstablished($fd)) {
$this->server->push($fd, $request->get['message']);
}
}
});
$this->server->start();
}
}
new WebSocketServer();
结合我的项目,部分代码是这样的
修改ImCommand类
在ImCommand类的pushHomeLogic方法中正确调用handle中的swoole_server实例,您需要确保该服务器实例在类级别可访问,并且在调用pushHomeLogic方法时能够传递正确的swoole_server实例。以下是修改后的代码示例:
将swoole_server实例设为类成员变量,以便在类的其他方法中访问它。
修改pushHomeLogic方法签名,使其接受swoole_server实例作为参数。
调用pushHomeLogic时传递$server实例。
修改后的代码如下:
<?php namespace App\Console\Commands;
// ... 其他使用声明 ...
class ImCommand extends Command
{
use SocketPush;
protected $serverInstance; // 添加一个类成员变量来存储server实例
// ... 其他方法 ...
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// 创建server并赋值给类成员变量
$this->serverInstance = new Server("0.0.0.0", 9502);
// ... 其他handle中的逻辑 ...
// 监听WebSocket主动推送消息事件
$this->serverInstance->on('request', function ($request, $response) {
echo "request-".$request->fd."\n";
var_dump($request->post);
// 调用pushHomeLogic时传递$serv实例
call_user_func([$this, 'pushHomeLogic'], $request, $this->serverInstance);
});
// ... 其他handle中的逻辑 ...
}
// 修改pushHomeLogic方法以接受$serv参数
public function pushHomeLogic($request, $serv)
{
$data = $request->post;
$serv->push($data['fd'], $this->success(ImService::$scene[ImService::PUSH_MESSAGE], $data['data']));
}
// ... 其他方法 ...
}
这样,当在request事件处理器中调用pushHomeLogic方法时,它会使用在handle方法中创建并保存的swoole_server实例进行消息推送。
创建消息服务ImMessageService
/**
* CURL请求
* @param $data
*/
public static function curl($data)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "http://127.0.0.1:9502");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_exec($curl);
curl_close($curl);
}
/**
* 主动触发
*/
public static function push($param)
{
$to_user_id = 999; // 后台管理客服id
$cacheKey = "socket_user:id_".$to_user_id;
$to_user_info = Redis::get($cacheKey);
if (empty($to_user_info)) return;
$to_user_info = json_decode($to_user_info,true);
$param['fd'] = $to_user_info['fd'];
self::curl($param); // 主动推送消息
}
在控制器中直接调用
// 系统推送订单消息
$param['msg'] = '用户新订单,请注意查看!';
ImMessageService::push($param);
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:JefskyWong ——程序猿甜品店
链接:https://www.jefsky.com/blog/340
来源:https://www.jefsky.com/