Linux 下监控文件自动实现swoole framework热更新

来源:互联网 发布:php安装教程 linux 编辑:程序博客网 时间:2024/06/05 12:46

swoole是以cli运行的,然后长驻内存的。整个生命周期只有在启动的时间可以一次执行RINT过程, 之后所有的请求都在第三步以内完成。(这也是swoole更快的原因之一),这样的话,相关的php脚本如果被执行了一次,就永久性的长驻内存了,更新代码就没有效果了。如果想让代码生效就要重启swoole服务,这种做法是比较粗暴和繁琐。如何才能实现自动检测代码文件?代码自动生效?

在网上查到说使用runkit扩展,经过我测试发现不生效(可能我配置的不对)。
后来找了一个可行的方法,进过整理修改,测试成功。具体步骤如下:

安装inotify扩展

pecl install inotify

在php.ini 最下添加

extension=inotify.so

用于监控文件

创建PHP文件

在 swoole/ToolKit 目录下创建
AutoReload.php

<?phpnamespace Swoole\ToolKit;class NotFound extends \Exception{}class AutoReload{    /**     * @var resource     */    protected $inotify;    protected $pid;    protected $reloadFileTypes = array('.php' => true);    protected $watchFiles      = array();    protected $afterNSeconds   = 1;    /**     * 正在reload     */    protected $reloading = false;    protected $events;    /**     * 根目录     * @var array     */    protected $rootDirs = array();    public function putLog($log)    {        $_log = "[" . date('Y-m-d H:i:s') . "]\t" . $log . "\n";        echo $_log;    }    /**     * @param $serverPid     * @throws NotFound     */    public function __construct($serverPid)    {        $this->pid = $serverPid;        if (posix_kill($serverPid, 0) === false) {            throw new NotFound("Process#$serverPid not found.");        }        $this->inotify = inotify_init();        $this->events  = IN_MODIFY | IN_DELETE | IN_CREATE | IN_MOVE;        swoole_event_add($this->inotify, function ($ifd) {            $events = inotify_read($this->inotify);            if (!$events) {                return;            }            // var_dump($events);            foreach ($events as $ev) {                if ($ev['mask'] == IN_IGNORED) {                    continue;                } else if ($ev['mask'] == IN_CREATE or $ev['mask'] == IN_DELETE or $ev['mask'] == IN_MODIFY or $ev['mask'] == IN_MOVED_TO or $ev['mask'] == IN_MOVED_FROM) {                    $fileType = strrchr($ev['name'], '.');                    //非重启类型                    if (!isset($this->reloadFileTypes[$fileType])) {                        continue;                    }                }                //正在reload,不再接受任何事件,冻结1秒                if (!$this->reloading) {                    $this->putLog("after 1 seconds reload the server");                    //有事件发生了,进行重启                    swoole_timer_after($this->afterNSeconds * 1000, array($this, 'reload'));                    $this->reloading = true;                }            }        });    }    public function reload()    {        $this->putLog("reloading");        //向主进程发送信号        posix_kill($this->pid, SIGUSR1);        //清理所有监听        $this->clearWatch();        //重新监听        foreach ($this->rootDirs as $root) {            $this->watch($root);        }        //继续进行reload        $this->reloading = false;    }    /**     * 添加文件类型     * @param $type     */    public function addFileType($type)    {        $type                               = trim($type, '.');        $this->reloadFileTypes['.' . $type] = true;    }    /**     * 添加事件     * @param $inotifyEvent     */    public function addEvent($inotifyEvent)    {        $this->events |= $inotifyEvent;    }    /**     * 清理所有inotify监听     */    public function clearWatch()    {        foreach ($this->watchFiles as $wd) {            inotify_rm_watch($this->inotify, $wd);        }        $this->watchFiles = array();    }    /**     * @param $dir     * @param bool $root     * @return bool     * @throws NotFound     */    public function watch($dir, $root = true)    {        //目录不存在        if (!is_dir($dir)) {            throw new NotFound("[$dir] is not a directory.");        }        //避免重复监听        if (isset($this->watchFiles[$dir])) {            return false;        }        //根目录        if ($root) {            $this->rootDirs[] = $dir;        }        $wd                     = inotify_add_watch($this->inotify, $dir, $this->events);        $this->watchFiles[$dir] = $wd;        $files = scandir($dir);        foreach ($files as $f) {            if ($f == '.' or $f == '..') {                continue;            }            $path = $dir . '/' . $f;            //递归目录            if (is_dir($path)) {                $this->watch($path, false);            }            //检测文件类型            $fileType = strrchr($f, '.');            if (isset($this->reloadFileTypes[$fileType])) {                $wd                      = inotify_add_watch($this->inotify, $path, $this->events);                $this->watchFiles[$path] = $wd;            }        }        return true;    }    public function run()    {        swoole_event_wait();    }}

在 swoole 文件夹下创建
daemon.php

<?phprequire __DIR__ . '/ToolKit/AutoReload.php';//存放SWOOLE服务的PID$file = __DIR__ . '/../../examples/app_server.pid';if (file_exists($file)) {    $pid = file_get_contents($file);    $kit = new Swoole\ToolKit\AutoReload((int)$pid);    $kit->watch(__DIR__ . '/../../');//监控的目录    $kit->run();}

创建启动脚本

vim swoole.sh

#!/bin/bashapppidpath="/var/www/examples/app_server.pid"pid="/tmp/swoole.pid" #进程锁定if [ -e $pid ]then        exit 0fiecho $$ > ${pid}       if [ -f ${apppidpath} ]        then            nohup php /var/www/libs/Swoole/daemon.php  </dev/null &>/dev/null &                   echo "deamon is ok!"        else            echo "swoole is stopped!"        fi#删除锁定文件rm $pid -rf

chmod +x swoole.sh

启动监控程序

./swoole.sh

至此,Linux监控文件自动实现swoole framework热更新部署完成,大家可以修改代码看看是否生效。
如果在使用过程中出现什么问题,欢迎批评指正。如有好的建议,也希望能给予回复,再次感谢。

原创粉丝点击