serv00部署gpt-load && Passenger进程保活

0.前提,部署到make run那一步,还未进行pm2保活

1.开始,参考下面讲的passenger保活进程,

2.app.js代码(旧):

总结
const express = require("express");
const path = require("path");
const exec = require("child_process").exec;
const app = express();
const port = port;//前提里面设置的端口

const user = ""; // Serv00 用户名
const pName = "gpt-load-server"; // 我保活了这个

// 完整路径到二进制程序(注意:不需要 go run,直接运行编译后的文件)
//const Process = `/home/${user}/gpt-load/${pName}`;//这里我手动kill了gpt-load-server之后没有触发app.js里面重启gpt-load-server的逻辑
const Process = "gpt-load/gpt-load-server";  // 匹配路径中包含这一段

// 日志文件路径
const LogFile = `/home/${user}/gpt-load/gpt-load.log`;

function keepWebAlive() {
  const currentDate = new Date();
  const formattedDate = currentDate.toLocaleDateString();
  const formattedTime = currentDate.toLocaleTimeString();

  // 精确查找该进程是否在运行
  exec(`pgrep -f "${Process}"`, (err, stdout) => {
    if (stdout.trim()) {
      console.log(`${formattedDate}, ${formattedTime}: ${pName} is Running (PID: ${stdout.trim()})`);
    } else {
      console.log(`${formattedDate}, ${formattedTime}: ${pName} is NOT Running, attempting to restart...`);
      // 使用 nohup 启动,输出日志
      exec(`cd /home/${user}/gpt-load && nohup ${Process} > ${LogFile} 2>&1 &`, (err) => {
        if (err) {
          console.error(`${formattedDate}, ${formattedTime}: Restart failed: ${err}`);
        } else {
          console.log(`${formattedDate}, ${formattedTime}: Restarted ${pName} successfully!`);
        }
      });
    }
  });
}

// 初始执行一次
keepWebAlive();

// 每 10 秒检查一次
setInterval(keepWebAlive, 10 * 1000);

app.use(express.static(path.join(__dirname, 'static')));

app.listen(port, () => {
  console.log(`Web server is listening on port ${port}!`);
});

app.js

const express = require("express");
const path = require("path");
const { exec } = require("child_process");
const fs = require("fs");
const app = express();

// ========== 配置部分 ==========
const PORT = ; // 替换为你的实际端口
const USER = ""; // Serv00用户名
const PROCESS_NAME = "gpt-load-server"; 
const PROCESS_PATH = `/home/${USER}/gpt-load/${PROCESS_NAME}`; // 绝对路径
const LOG_PATH = `/home/${USER}/gpt-load/gpt-load.log`;

// ========== 进程锁防止重复启动 ==========
let isRestarting = false;
let restartAttempts = 0;
const MAX_RETRIES = 3;

// ========== 优化后的进程守护函数 ==========
async function keepProcessAlive() {
  if (isRestarting) return;
  
  const timestamp = new Date().toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  }).replace(/\//g, '-');

  try {
    // 更可靠的进程检测方式
    const isRunning = await new Promise((resolve) => {
      exec(`ps aux | grep "[${PROCESS_NAME[0]}]${PROCESS_NAME.slice(1)}"`, (err, stdout) => {
        resolve(!!stdout.trim());
      });
    });

    if (isRunning) {
      console.log(`[${timestamp}] ${PROCESS_NAME} is running`);
      restartAttempts = 0; // 重置重试计数器
      return;
    }

    // 进程崩溃处理
    if (restartAttempts >= MAX_RETRIES) {
      console.error(`[${timestamp}] Maximum restart attempts reached. Stopping...`);
      return;
    }

    isRestarting = true;
    restartAttempts++;

    console.log(`[${timestamp}] Attempting to restart ${PROCESS_NAME} (attempt ${restartAttempts}/${MAX_RETRIES})`);

    // 确保日志目录存在
    if (!fs.existsSync(path.dirname(LOG_PATH))) {
      fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
    }

    // 使用绝对路径启动进程
    exec(`cd /home/${USER}/gpt-load && nohup ${PROCESS_PATH} >> ${LOG_PATH} 2>&1 &`, 
      (err) => {
        isRestarting = false;
        if (err) {
          console.error(`[${timestamp}] Restart failed: ${err.message}`);
        } else {
          console.log(`[${timestamp}] ${PROCESS_NAME} restarted successfully`);
        }
      });

  } catch (error) {
    console.error(`[${timestamp}] Monitoring error: ${error.message}`);
    isRestarting = false;
  }
}

// ========== 服务器配置 ==========
app.use(express.static(path.join(__dirname, 'static')));

// 健康检查接口
app.get('/health', (req, res) => {
  res.status(200).json({ 
    status: 'healthy',
    process: PROCESS_NAME,
    lastRestartAttempt: restartAttempts
  });
});

// ========== 启动服务 ==========
app.listen(PORT, () => {
  console.log(`[${new Date().toLocaleString()}] Web server listening on port ${PORT}`);
  // 初始检查 + 每30秒检测一次(降低频率)
  keepProcessAlive();
  setInterval(keepProcessAlive, 30 * 1000);
});

3.启动程序(要以绝对路径启动,不然…app.js没办法重启gpt-load-server)

启动gpt-load-server

cd /home/username/gpt-load
nohup ./gpt-load-server > gpt-load.log 2>&1 &

启动app.js

cd /home/username/domains/username.serv00.net/public_nodejs
nohup node app.js > app.log 2>&1 &

4.验证

手动杀掉gpt-load-server

pkill -f gpt-load-server

在public_nodejs目录

[id@s9]:<~/domains/id.serv00.net/public_nodejs>$ pkill -f gpt-load-server
[id@s9]:<~/domains/id.serv00.net/public_nodejs>$ tail -f app.log

查看app.log

tail -f app.log

显示下面则成功:

5.serv00母鸡重启的话,需要重启app.js

写一个start.sh脚本

#!/bin/bash
cd /home/username/gpt-load
nohup ./gpt-load-server > gpt-load.log 2>&1 &

cd /home/username/domains/username.serv00.net/public_nodejs
nohup node app.js > app.log 2>&1 &

赋权并运行

chmod +x start.sh
./start.sh

之后登陆则运行

./start.sh

即可


PS:最简单的还是pm2保活,官方也没看到说禁止,说的是最好用passenger,访问我用的域名+端口,可以自己试试passenger反代去掉端口

17 个赞

现在不用保活了吧

最上面那个serv00部署gpt-load需要保活进程才行,不然就是停在make run之后的那个打印成功日志的状态,退出后程序就结束了

2 个赞

太强了,1b!

1 个赞

再战serv00

1 个赞

大佬666啊,赶紧收藏一下社区文档。 :+1:

1 个赞

直接pm2最方便,没禁止,最上面引用的那个佬自己就用了几个月还没事的,定时任务最近几个月官方也没有在清除了image

1 个赞

研究折腾这些平台就是还要看官方的政策,辛苦佬了啊。

1 个赞

站在前人和AI的肩膀上整了下 :tieba_022:其实是担心pm2弄死我唯一剩下的serv00 :tieba_087:

1 个赞

根据我的了解,serv00好像封二进制文件的概率比较大,所以最后我选择了render

1 个赞

那我还是等死吧image

没事。反正serv00也不好用,提前备份好数据就行

我都是有限本地docker弄了再线上搞的,数据不怕,先看看能活多久 :thinking:

1 个赞

serv00不清理定时任务了,我的项目几个月了也没问题

1 个赞

我还是一直pm2 上次重启几个月之前了

1 个赞

能说下怎么反代去掉端口么,用不了https很不方便 :joy:

1 个赞

可以问一下AI,也是用serv00那个passenger,我还没试过,昨天部署的时候看它给我写了一堆,有点麻烦

serv00账号被ban了,只能看佬们玩啦

去社区申请解封试试,我看下面一大堆申请

搞不懂,为什么非要设置个端口,和newapi一样直接域名访问不香么 :joy: