range

If there were you, the world would be just right

针对问题:Hyperf 生产环境, 不上Swarm, 不上 k8s, 如何更新?

<?php

/**
 * 双机热备更新:
 * 1、检查当前项目运行端口 9501
 * 2、更改项目运行端口为 9502,启动项目(此时运行的是两个服务9501+9502)
 * 3、更改nginx配置为 9502,并重新 reload
 * 4、停止9501端口服务
 */

#--------------------- 主要修改内容 start ------------------
// 目录
define("WEB_PATH", "/data/web");
// Todo 项目
define("PROJECT_NAME", "cp-game-api");
// Todo nginx配置文件
define("NGINX_CONF", "/usr/local/nginx/conf/vhost/cp-game-api.conf");
// TODO 原先端口
define("PORT", 9501);
// Todo 交换端口
define("NEW_PORT", 9502);
#--------------------- 主要修改内容 end ------------------

// 完整项目地址
define("PROJECT_PATH", WEB_PATH . "/" . PROJECT_NAME);
// RUNTIME 
define("RUNTIME_PATH", PROJECT_PATH . "/runtime");
// 项目构建日志
define("LOG_FILE", RUNTIME_PATH . "/release.log");
// PID
define("PID_FILE", RUNTIME_PATH . "/hyperf.pid");

class Release
{
    public $pid = 0;

    public function __construct()
    {
        // 旧服务pid
        if (file_exists(PID_FILE)) {
            $this->pid = file_get_contents(PID_FILE);
        }
    }

    // 判断端口
    public function getPort()
    {
        $lsofExec   = "lsof -i:" . PORT;
        $lsofResult = exec($lsofExec);

        $newPort = PORT;
        $oldPort = NEW_PORT;
        if ($lsofResult !== '') {
            $newPort = NEW_PORT;
            $oldPort = PORT;
        }

        return [$oldPort, $newPort];
    }

    // 读取nginx配置,判断当前正在使用的是哪个端口
    public function getNginxServerPort()
    {
        $fileContent = file_get_contents(NGINX_CONF);
        $pattern     = '/127\.0\.0\.1:(\d+)/';
        preg_match($pattern, $fileContent, $matches);
        return $matches[1] ?? 0;
    }

    // 执行正文
    public function run()
    {
        $this->log(PHP_EOL . date("Y-m-d H:i:s") . ' 版本发布 ===> START <=== ');

        // 判断当前是哪个端口正在服务
        [$oldPort, $newPort] = $this->getPort();

        // 将另一个闲置的端口,替换到新版本中
        $projectPath = WEB_PATH . "/" . PROJECT_NAME;
        $this->log(' 新服务端口为:' . $newPort);
        $devConfig = file_get_contents($projectPath . '/.env');
        $devConfig = str_replace($oldPort, $newPort, $devConfig);
        file_put_contents($projectPath . '/.env', $devConfig);

        // 启动新服务(这一刻新旧服务是同时存在的)
        $startExec = "cd {$projectPath}; php bin/hyperf.php start";
        $this->log(" 新服务启动:" . $startExec);
        exec($startExec);

        // 替换 Nginx 配置
        $oldNginxPort = $this->getNginxServerPort();
        if ($oldNginxPort && $oldNginxPort != $newPort) {
            $this->log(' 替换nginx配置端口为:' . NGINX_CONF . ' ==> ' . $newPort);
            ### 这里需要换成自己服务器环境 nginx 配置文件所在的目录
            $ngConfig = file_get_contents(NGINX_CONF);
            $ngConfig = str_replace($oldNginxPort, $newPort, $ngConfig);
            file_put_contents(NGINX_CONF, $ngConfig);

            // 重启 Nginx 服务
            $reloadNgExec = "nginx -s reload";
            $this->log(" 重载nginx: " . $reloadNgExec);
            exec($reloadNgExec);
        }

        if ($this->pid) {
            $stopExec = "kill {$this->pid}";
        } else {
            $stopExec = "netstat -tunlp | awk '{if ($4 == " . '"0.0.0.0:' . $oldPort . '"' . ') print $7}\' | awk -F"/" \'{ print $1 }\' | xargs kill -15';
        }
        $this->log(" 停掉旧服务: " . $stopExec);
        exec($stopExec);

        $this->log(date("Y-m-d H:i:s") . ' 版本发布 ===> END <=== ' . PHP_EOL);
    }

    // 日志记录
    public function log($msg, $filename = '')
    {
        $filename = $filename ?: LOG_FILE;

        echo $msg . PHP_EOL;

        return file_put_contents($filename, $msg . PHP_EOL, FILE_APPEND);
    }
}

(new Release())->run();

执行结果

2023-07-05 11:39:27 版本发布 ===> START <=== 
 新服务端口为:9502
 新服务启动:cd /data/web/cp-game-api; php bin/hyperf.php start
 替换nginx配置端口为:/usr/local/nginx/conf/vhost/cp-game-api.conf ==> 9502
 重载nginx: nginx -s reload
 停掉旧服务: netstat -tunlp | awk '{if ($4 == "0.0.0.0:9501") print $7}' | awk -F"/" '{ print $1 }' | xargs kill -15
2023-07-05 11:39:27 版本发布 ===> END <=== 

再次执行结果

2023-07-05 11:41:36 版本发布 ===> START <=== 
 新服务端口为:9501
 新服务启动:cd /data/web/cp-game-api; php bin/hyperf.php start
 替换nginx配置端口为:/usr/local/nginx/conf/vhost/cp-game-api.conf ==> 9501
 重载nginx: nginx -s reload
 停掉旧服务: netstat -tunlp | awk '{if ($4 == "0.0.0.0:9502") print $7}' | awk -F"/" '{ print $1 }' | xargs kill -15
2023-07-05 11:41:37 版本发布 ===> END <===