| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- <?php
- declare(strict_types=1);
- namespace App\Command;
- use Swoole\Process;
- use Symfony\Component\Console\Command\Command;
- use Hyperf\Contract\ConfigInterface;
- use Hyperf\Contract\StdoutLoggerInterface;
- use Hyperf\Server\ServerFactory;
- use InvalidArgumentException;
- use Psr\Container\ContainerInterface;
- use Psr\EventDispatcher\EventDispatcherInterface;
- use Swoole\Runtime;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Input\InputOption;
- use Symfony\Component\Console\Output\OutputInterface;
- use Symfony\Component\Console\Style\SymfonyStyle;
- use Hyperf\Command\Annotation\Command as HyperfCommand;
- //php bin/hyperf.php start -d //启动服务并进入后台模式
- //php bin/hyperf.php start -c //启动服务并清除 runtime/container 目录
- //php bin/hyperf.php start -w //启动服务并监控 app、config目录以及 .env 变化自动重启
- //php bin/hyperf.php start -w -p /bin/php //启动 watch 服务,参数 p 指定 php 安装目录
- //php bin/hyperf.php start -w -t 10 //启动 watch 服务,参数 t 指定 watch 时间间隔,单位秒
- //php bin/hyperf.php stop //停止服务
- //php bin/hyperf.php restart //重启服务
- //php bin/hyperf.php restart -c //重启服务并清除 runtime/container 目录
- /**
- * @HyperfCommand()
- */
- class StartCommand extends Command
- {
- /**
- * @var ContainerInterface
- */
- private $container;
- /**
- * @var SymfonyStyle
- */
- private $io;
- /**
- * @var int
- */
- private $interval;
- /**
- * @var bool
- */
- private $clear;
- /**
- * @var bool
- */
- private $daemonize;
- /**
- * @var string
- */
- private $php;
- public function __construct(ContainerInterface $container)
- {
- $this->container = $container;
- parent::__construct('start');
- }
- protected function configure()
- {
- $this
- ->setDescription('Start hyperf servers.')
- ->addOption('daemonize', 'd', InputOption::VALUE_OPTIONAL, 'swoole server daemonize', false)
- ->addOption('clear', 'c', InputOption::VALUE_OPTIONAL, 'clear runtime container', false)
- ->addOption('watch', 'w', InputOption::VALUE_OPTIONAL, 'watch swoole server', false)
- ->addOption('interval', 't', InputOption::VALUE_OPTIONAL, 'interval time ( 1-15 seconds)', 3)
- ->addOption('php', 'p', InputOption::VALUE_OPTIONAL, 'which php');
- }
- protected function execute(InputInterface $input, OutputInterface $output)
- {
- $this->io = new SymfonyStyle($input, $output);
- $this->checkEnvironment($output);
- $this->stopServer();
- $this->clear = ($input->getOption('clear') !== false);
- $this->daemonize = ($input->getOption('daemonize') !== false);
- if ($input->getOption('watch') !== false) {
- $this->interval = (int)$input->getOption('interval');
- if ($this->interval < 0 || $this->interval > 15) {
- $this->interval = 3;
- }
- if (!$this->php = $input->getOption('php')) {
- if (!$this->php = exec('which php')) {
- $this->php = 'php';
- }
- }
- $this->watchServer();
- } else {
- if ($this->clear) {
- $this->clearRuntimeContainer();
- }
- $this->startServer();
- }
- return 0;
- }
- private function checkEnvironment(OutputInterface $output)
- {
- /**
- * swoole.use_shortname = true => string(1) "1" => enabled
- * swoole.use_shortname = "true" => string(1) "1" => enabled
- * swoole.use_shortname = on => string(1) "1" => enabled
- * swoole.use_shortname = On => string(1) "1" => enabled
- * swoole.use_shortname = "On" => string(2) "On" => enabled
- * swoole.use_shortname = "on" => string(2) "on" => enabled
- * swoole.use_shortname = 1 => string(1) "1" => enabled
- * swoole.use_shortname = "1" => string(1) "1" => enabled
- * swoole.use_shortname = 2 => string(1) "1" => enabled
- * swoole.use_shortname = false => string(0) "" => disabled
- * swoole.use_shortname = "false" => string(5) "false" => disabled
- * swoole.use_shortname = off => string(0) "" => disabled
- * swoole.use_shortname = Off => string(0) "" => disabled
- * swoole.use_shortname = "off" => string(3) "off" => disabled
- * swoole.use_shortname = "Off" => string(3) "Off" => disabled
- * swoole.use_shortname = 0 => string(1) "0" => disabled
- * swoole.use_shortname = "0" => string(1) "0" => disabled
- * swoole.use_shortname = 00 => string(2) "00" => disabled
- * swoole.use_shortname = "00" => string(2) "00" => disabled
- * swoole.use_shortname = "" => string(0) "" => disabled
- * swoole.use_shortname = " " => string(1) " " => disabled.
- */
- $useShortname = ini_get_all('swoole')['swoole.use_shortname']['local_value'];
- $useShortname = strtolower(trim(str_replace('0', '', $useShortname)));
- if (!in_array($useShortname, ['', 'off', 'false'], true)) {
- $output->writeln('<error>ERROR</error> Swoole short name have to disable before start server, please set swoole.use_shortname = off into your php.ini.');
- exit(0);
- }
- }
- private function clearRuntimeContainer()
- {
- exec('rm -rf ' . BASE_PATH . '/runtime/container');
- }
- private function startServer()
- {
- $serverFactory = $this->container->get(ServerFactory::class)
- ->setEventDispatcher($this->container->get(EventDispatcherInterface::class))
- ->setLogger($this->container->get(StdoutLoggerInterface::class));
- $serverConfig = $this->container->get(ConfigInterface::class)->get('server', []);
- if (!$serverConfig) {
- throw new InvalidArgumentException('At least one server should be defined.');
- }
- if ($this->daemonize) {
- $serverConfig['settings']['daemonize'] = 1;
- $this->io->success('swoole server start success.');
- }
- Runtime::enableCoroutine(true, swoole_hook_flags());
- $serverFactory->configure($serverConfig);
- $serverFactory->start();
- }
- private function stopServer()
- {
- $pidFile = BASE_PATH . '/runtime/hyperf.pid';
- $pid = file_exists($pidFile) ? intval(file_get_contents($pidFile)) : false;
- if ($pid && Process::kill($pid, SIG_DFL)) {
- if (!Process::kill($pid, SIGTERM)) {
- $this->io->error('old swoole server stop error.');
- die();
- }
- while (Process::kill($pid, SIG_DFL)) {
- sleep(1);
- }
- }
- }
- private function watchServer()
- {
- $this->io->note('start new swoole server ...');
- $pid = $this->startProcess();
- while ($pid > 0) {
- $this->watch();
- $this->io->note('restart swoole server ...');
- $this->stopProcess($pid);
- $pid = $this->startProcess();
- sleep(1);
- }
- }
- private function startProcess()
- {
- $this->clearRuntimeContainer();
- $process = new Process(function (Process $process) {
- $args = [BASE_PATH . '/bin/hyperf.php', 'start'];
- if ($this->daemonize) {
- $args[] = '-d';
- }
- $process->exec($this->php, $args);
- });
- return $process->start();
- }
- private function stopProcess(int $pid): bool
- {
- $this->io->text('stop old swoole server. pid:' . $pid);
- $timeout = 15;
- $startTime = time();
- while (true) {
- if ($ret = Process::wait(false) && $ret['pid'] == $pid) {
- return true;
- }
- if (!Process::kill($pid, SIG_DFL)) {
- return true;
- }
- if ((time() - $startTime) >= $timeout) {
- $this->io->error('stop old swoole server timeout.');
- return false;
- }
- Process::kill($pid, SIGTERM);
- sleep(1);
- }
- return false;
- }
- private function monitorDirs(bool $recursive = false)
- {
- $dirs[] = BASE_PATH . '/app';
- $dirs[] = BASE_PATH . '/config';
- if ($recursive) {
- foreach ($dirs as $dir) {
- $dirIterator = new \RecursiveDirectoryIterator($dir);
- $iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::SELF_FIRST);
- /** @var \SplFileInfo $file */
- foreach ($iterator as $file) {
- if ($file->isDir() && $file->getFilename() != '.' && $file->getFilename() != '..') {
- $dirs[] = $file->getPathname();
- }
- }
- }
- }
- return $dirs;
- }
- private function monitorFiles()
- {
- $files[] = BASE_PATH . '/.env';
- return $files;
- }
- private function watch()
- {
- if (extension_loaded('inotify')) {
- return $this->inotifyWatch();
- } else {
- return $this->fileWatch();
- }
- }
- private function inotifyWatch()
- {
- $fd = inotify_init();
- stream_set_blocking($fd, 0);
- $dirs = $this->monitorDirs(true);
- foreach ($dirs as $dir) {
- inotify_add_watch($dir, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE);
- }
- $files = $this->monitorFiles();
- foreach ($files as $file) {
- inotify_add_watch($file, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE);
- }
- while (true) {
- sleep($this->interval);
- if (inotify_read($fd)) {
- break;
- }
- }
- fclose($fd);
- }
- private function fileWatch()
- {
- $dirs = $this->monitorDirs();
- $files = $this->monitorFiles();
- $inodeListOld = [];
- $inodeListNew = [];
- $isFrist = true;
- while (true) {
- foreach ($dirs as $dir) {
- $dirIterator = new \RecursiveDirectoryIterator($dir);
- $iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::LEAVES_ONLY);
- /** @var \SplFileInfo $file */
- foreach ($iterator as $file) {
- if ($file->isFile() && in_array(strtolower($file->getExtension()), ['env', 'php'])) {
- $inode = $file->getInode();
- $sign = $file->getFilename() . $file->getMTime();
- if ($isFrist) {
- $inodeListOld[$inode] = $sign;
- } else {
- // add new file || file changed
- if (!isset($inodeListOld[$inode]) || $inodeListOld[$inode] != $sign) {
- return true;
- } else {
- $inodeListNew[$inode] = $sign;
- }
- }
- }
- }
- }
- foreach ($files as $key => $file) {
- if (file_exists($file)) {
- $file = new \SplFileInfo($file);
- $inode = $file->getInode();
- $sign = $file->getFilename() . $file->getMTime();
- if ($isFrist) {
- $inodeListOld[$inode] = $sign;
- } else {
- // add new file || file changed
- if (!isset($inodeListOld[$inode]) || $inodeListOld[$inode] != $sign) {
- return true;
- } else {
- $inodeListNew[$inode] = $sign;
- }
- }
- }
- }
- if ($isFrist) {
- $isFrist = false;
- } else {
- // file remove
- if (!empty(array_diff($inodeListOld, $inodeListNew))) {
- return true;
- }
- }
- sleep($this->interval);
- }
- }
- }
|