Laravel 使用 Swoole 代码热更新方案
业务背景
做的是一款游戏匹配的App,PHP使用swoole创建websocket提供游戏的匹配服务
匹配流程如下
- 对匹配者的鉴权(握手事件处理)
- 匹配的业务逻辑(比如男只能匹配到女,这块也是需要热更新,open事件处理)
- 匹配成功返回数据,关闭连接
- 再往后就是nodejs去提供服务
想要达到的目的
在不重启服务的情况下,改变了
匹配的业务逻辑
代码的情况下自动热更新代码
关于热更新swoole官方文档
其实核心就是说你要热更新的代码必须在
onWorkerStart
事件中引入
安装swoole和inotify
自己绘制的"设计图"
如果你使用artisan启动swoole服务的话,可能会热更新失败,因为在
onWorkerStart
,之前已经载入太多类
index
设置常量同时实例化MatchServer来启动服务
require 'MatchServer.php';
if (php_sapi_name() != 'cli') die('请用cli模式启动');
define('ROOT_PATH',dirname(dirname(dirname(__DIR__))).'/');
define('PORT',20005);
$server = new MatchServer();
MatchServer
class MatchServer{
private $server;
protected $application;
function __construct ()
{
// 创建swoole_table,用于进程间数据共享
$table = new swoole_table(1024);
$table->column('fd', swoole_table::TYPE_INT);
$table->column('uid', swoole_table::TYPE_INT);
$table->column('gameType', swoole_table::TYPE_STRING, 256);
$table->column('data', swoole_table::TYPE_STRING, 256);
$table->create();
$this->server = new swoole_websocket_server("0.0.0.0", PORT);
$this->server->table = $table;
// 注册回调事件
$this->server->on('handShake', array($this, 'onHandShake'));
$this->server->on('workerStart', array($this, 'onWorkerStart'));
$this->server->on('open', array($this, 'onOpen'));
$this->server->on('message', array($this, 'onMessage'));
$this->server->on('close', array($this, 'onClose'));
$this->server->start();
}
/**
* 处理握手
*
* @param swoole_http_request $request
* @param swoole_http_response $response
*
* @return bool
*/
public function onHandShake (\swoole_http_request $request, \swoole_http_response $response)
{
if(参数校验不通过)
{
$response->end();
return false;
}
//swoole握手环节,因为我的匹配是在open事件处理,当自己处理握手之后,不会自动调用open事件,需自己调用
// 握手环节代码..太多..考虑到篇幅问题,不贴了..大家可以去swoole手册搜索
$this->onOpen($this->server, $request);
return true;
}
/**
* 载入框架入口文件,并设置inotify热更新目录
*
* @param $server
* @param $worker_id
*/
public function onWorkerStart ($server, $worker_id)
{
// 载入框架入口文件
require ROOT_PATH.'public/index.php';
// 实例化业务逻辑类
$this->application = new MatchApplication();
if ($worker_id == 0) {
// 设置热更新目录
$dir = app_path('Game/Match');
$list[] = $dir;
foreach (array_diff(scandir($dir), array('.', '..')) as $item) {
$list[] = $dir.'/'.$item;
}
$notify = inotify_init();
foreach ($list as $item) {
inotify_add_watch($notify, $item, IN_CREATE | IN_DELETE | IN_MODIFY);
}
swoole_event_add($notify, function () use ($notify,$server) {
$events = inotify_read($notify);
if (!empty($events)) {
// 执行swolle reload
$server->reload();
}
});
}
}
/**
* 处理匹配
*
* @param $server
* @param $request
*/
public function onOpen ($server, $request)
{
// 调用业务逻辑类的onOpen
$this->application->onOpen($server,$request);
}
public function onMessage ($server, $frame){}
/**
* 关闭连接同时删除swoole_table数据
*
* @param $server
* @param $fd
*/
public function onClose ($server, $fd)
{
// 由于我进程间的数据共享用的swoole_table,所以连接关闭,需要删除数据
if ($server->table->exist($fd)) {
$server->table->del($fd);
}
}
}
MatchApplication
/**
* 处理匹配业务逻辑
*
* @param $server
* @param $request
*/
public function onOpen ($server, $request)
{
$fd = $request->fd;
// 处理业务逻辑......
$server->push($fd,$data);
$server->close($fd);
}
启动服务
$ php Index.php
确认onWorkerStart
之前没有载入你要热更新的代码
public function onWorkerStart ($server, $worker_id)
{
print_r(get_included_files());
return;
}
关于守护我用的是supervisor
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 7年前 自动加精
学习学习
@linzi007 共勉共勉 :smile:
先赞后看
引入index.php会输出laravel首页,楼主是怎么解决的?
@过江的桥
因为我这边也是构建api..并未用到web路由,所以,我也就返回了一个字符串
不过这个问题确实存在~就目前来讲,我应该没有好的解决办法.. :sob: 很抱歉
@罗阳 判断了下是否cli运行解决了:simple_smile:
这样不会输出首页,去掉了response
请教下,我的业务逻辑是写在onmessage闭包里边,这种情况怎么热更新。
docker的alpine系统,热加载没有反应,好像inotify在alpine不兼容
学习了 inotify_init,inotify_add_watch,inotify_read。之前从来没用过