From c4eb862a8f4bf564ee5810e9894700c776484a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=85=88=E6=9E=97?= Date: Sat, 14 Dec 2024 14:28:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=20Chokidar=20Watch=20?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=E6=9D=A5=E6=94=AF=E6=8C=81=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=90=8E=E8=87=AA=E5=8A=A8Reload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/file-watcher.cjs | 12 ++++++++ config/laravels.php | 40 ++++++++++++++++++++++++ src/LaravelS.php | 12 +++++++- src/Swoole/ChokidarWatchTrait.php | 51 +++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 bin/file-watcher.cjs create mode 100644 src/Swoole/ChokidarWatchTrait.php diff --git a/bin/file-watcher.cjs b/bin/file-watcher.cjs new file mode 100644 index 00000000..7d2cba20 --- /dev/null +++ b/bin/file-watcher.cjs @@ -0,0 +1,12 @@ +const chokidar = require('chokidar'); + +const paths = JSON.parse(process.argv[2]); +const options = JSON.parse(process.argv[3]); + +const watcher = chokidar.watch(paths, options); + +watcher + .on('add', () => console.log('file added ...')) + .on('change', () => console.log('file changed ...')) + .on('unlink', () => console.log('file deleted ...')) + .on('unlinkDir', () => console.log('directory deleted ...')); diff --git a/config/laravels.php b/config/laravels.php index 54b62cfa..47fbfb07 100644 --- a/config/laravels.php +++ b/config/laravels.php @@ -120,6 +120,46 @@ 'log' => true, ], + 'chokidar_reload' => [ + // Whether enable the chokidar Reload to reload all worker processes when your code is modified. + // chokidar Reload,用于当修改代码后实时Reload所有worker进程,依赖库node js 库 chokidar, + // 默认false,建议仅开发环境开启,修改监听数上限。 + 'enable' => env('LARAVELS_CHOKIDAR_RELOAD', false), + + // The file path that chokidar watches + // chokidar 监控的文件路径,默认有base_path()。 + 'watch_base_path' => base_path(), + 'watch_path_files' => [ + 'app', + 'bootstrap', + 'config/**/*.php', + 'database/**/*.php', + 'public/**/*.php', + 'resources/**/*.php', + 'routes', + 'composer.lock', + '.env', + ], + + // chokidar 监控的参数。 + 'watch_options' => [ + //是否使用轮询方式监听文件变化。默认为 false,如果文件系统不支持事件监听,设置为 true 会强制使用轮询 + 'usePolling' => true, + //在使用轮询时,设置轮询的时间间隔(单位:毫秒)。默认值为 100 + 'interval' => 200, + //是否忽略首次读取文件时的事件(例如,文件刚开始时的“添加”或“变化”事件)。默认为 false,即不会忽略首次事件。 + 'ignoreInitial' => true, + ], + + // The excluded/ignored directories that Inotify watches + // Inotify 监控时需要排除(或忽略)的目录,默认[],示例:[base_path('vendor')]。 + 'excluded_dirs' => [base_path('vendor')], + + // Whether output the reload log + // 是否输出Reload的日志,默认true。 + 'log' => true, + ], + /* |-------------------------------------------------------------------------- | Swoole Event Handlers diff --git a/src/LaravelS.php b/src/LaravelS.php index fb1c86c9..a030d67b 100644 --- a/src/LaravelS.php +++ b/src/LaravelS.php @@ -12,6 +12,7 @@ use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface; use Hhxsv5\LaravelS\Swoole\Events\WorkerStopInterface; use Hhxsv5\LaravelS\Swoole\InotifyTrait; +use Hhxsv5\LaravelS\Swoole\ChokidarWatchTrait; use Hhxsv5\LaravelS\Swoole\Process\CustomProcessTrait; use Hhxsv5\LaravelS\Swoole\Process\ProcessTitleTrait; use Hhxsv5\LaravelS\Swoole\Request; @@ -37,7 +38,7 @@ class LaravelS extends Server /** * Fix conflicts of traits */ - use InotifyTrait, LaravelTrait, LogTrait, ProcessTitleTrait, TimerTrait, CustomProcessTrait; + use InotifyTrait, ChokidarWatchTrait, LaravelTrait, LogTrait, ProcessTitleTrait, TimerTrait, CustomProcessTrait; /**@var array */ protected $laravelConf; @@ -61,6 +62,15 @@ public function __construct(array $svrConf, array $laravelConf) $inotifyConf['process_prefix'] = $svrConf['process_prefix']; $this->swoole->inotifyProcess = $this->addInotifyProcess($this->swoole, $inotifyConf, $this->laravelConf); + $chokidarConf = isset($this->conf['chokidar_reload']) ? $this->conf['chokidar_reload'] : []; + if (!empty($chokidarConf['enable'])) { + if (!isset($chokidarConf['watch_path'])) { + $chokidarConf['watch_path'] = $this->laravelConf['root_path']; + } + $chokidarConf['process_prefix'] = $svrConf['process_prefix']; + $this->swoole->chokidarProcess = $this->addChokidarWatchProcess($this->swoole, $chokidarConf, $this->laravelConf); + } + $processes = isset($this->conf['processes']) ? $this->conf['processes'] : []; $this->swoole->customProcesses = $this->addCustomProcesses($this->swoole, $svrConf['process_prefix'], $processes, $this->laravelConf); diff --git a/src/Swoole/ChokidarWatchTrait.php b/src/Swoole/ChokidarWatchTrait.php new file mode 100644 index 00000000..4cdf9b36 --- /dev/null +++ b/src/Swoole/ChokidarWatchTrait.php @@ -0,0 +1,51 @@ +warning('No file to watch by chokidar'); + return false; + } + + $callback = function (Process $worker) use ($config, $laravelConf, $watchPathFiles) { + $log = !empty($config['log']); + $watch_base_path = $config['watch_base_path']; + $this->setProcessTitle(sprintf('%s laravels: chokidar process', $config['process_prefix'])); + $nodeExecutable = (new ExecutableFinder)->find('node'); + $nodeScriptPath = realpath(__DIR__.'/../../bin/file-watcher.cjs'); + $this->info(sprintf('$nodeScriptPath: %s', $nodeScriptPath)); + + $watchOptions = isset($config['watch_options']) ? (array)$config['watch_options'] : ['ignoreInitial' => true]; + $worker->exec($nodeExecutable, [$nodeScriptPath, + json_encode(collect($watchPathFiles)->map(fn ($path) => $watch_base_path.'/'.$path)), + json_encode($watchOptions), + ]); + + // 获取 Node.js 脚本的输出 + tap($worker->read(), function ($output) use ($log, $laravelConf) { + Portal::runLaravelSCommand($laravelConf['root_path'], 'reload'); + if ($log) { + $this->info(sprintf('reloaded by chokidar, reason: %s', $output)); + } + }); + }; + + $process = new Process($callback, false, 0); + $swoole->addProcess($process); + return $process; + } +}