Automatic hot update of swoole framework for monitoring files under Linux

Keywords: PHP inotify vim Linux

Swoole runs in cli and then resides in memory. The RINT process can only be executed once during the entire lifecycle at startup time, after which all requests are completed within the third step. (This is one of the reasons why swoole is faster.) In this way, if the relevant php script is executed once, it will permanently reside in memory and update the code will not work. It's rough and tedious to restart the swoole service if you want the code to take effect. How can we automatically detect code files? Does the code take effect automatically?

I checked on the Internet that the runkit extension was used, and I found that it didn't work after testing (maybe I configured it incorrectly).
Later, a feasible method was found, which was modified and tested successfully. The specific steps are as follows:

Install inotify extensions

pecl install inotify

Add at the bottom of php.ini

extension=inotify.so

Used for monitoring files

Create PHP files

Create in the swoole/ToolKit directory
AutoReload.php

<?php
namespace 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;

    /**
     * Is reload ing
     */
    protected $reloading = false;

    protected $events;

    /**
     * root directory
     * @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'], '.');
                    //Non-restart type
                    if (!isset($this->reloadFileTypes[$fileType])) {
                        continue;
                    }
                }
                //reload, no more events, freeze for 1 second
                if (!$this->reloading) {
                    $this->putLog("after 1 seconds reload the server");
                    //Something happened and we restarted it.
                    swoole_timer_after($this->afterNSeconds * 1000, array($this, 'reload'));
                    $this->reloading = true;
                }
            }
        });
    }

    public function reload()
    {
        $this->putLog("reloading");
        //Send a signal to the main process
        posix_kill($this->pid, SIGUSR1);
        //Clean up all monitors
        $this->clearWatch();
        //Re-monitoring
        foreach ($this->rootDirs as $root) {
            $this->watch($root);
        }
        //Continue reload ing
        $this->reloading = false;
    }

    /**
     * add file type
     * @param $type
     */
    public function addFileType($type)
    {
        $type                               = trim($type, '.');
        $this->reloadFileTypes['.' . $type] = true;
    }

    /**
     * Adding Events
     * @param $inotifyEvent
     */
    public function addEvent($inotifyEvent)
    {
        $this->events |= $inotifyEvent;
    }

    /**
     * Clean up all inotify listeners
     */
    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)
    {
        //directory does not exist
        if (!is_dir($dir)) {
            throw new NotFound("[$dir] is not a directory.");
        }
        //Avoid repetitive monitoring
        if (isset($this->watchFiles[$dir])) {
            return false;
        }
        //root directory
        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;
            //Recursive catalogue
            if (is_dir($path)) {
                $this->watch($path, false);
            }
            //Detecting file type
            $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();
    }
}

Create under the swoole folder
daemon.php

<?php
require __DIR__ . '/ToolKit/AutoReload.php';
//PID for storing SWOOLE services
$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__ . '/../../');//Monitoring Directory
    $kit->run();
}

Create startup scripts

vim swoole.sh

#!/bin/bash

apppidpath="/var/www/examples/app_server.pid"
pid="/tmp/swoole.pid" #Process Locking
if [ -e $pid ]
then
        exit 0
fi
echo $$ > ${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

#Delete Locked Files
rm $pid -rf

chmod +x swoole.sh

Start monitoring program

./swoole.sh

At this point, the Linux monitoring file automatically realizes the deployment of the swoole framework hot update. You can modify the code to see if it works.
If there are any problems in the use process, you are welcome to criticize and correct them. If you have any good suggestions, I hope to give you a reply. Thank you again.

Posted by Azala on Wed, 19 Jun 2019 14:36:33 -0700