357 lines
12 KiB
Bash
357 lines
12 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# 百家号爬虫系统 - 快速启动脚本
|
|||
|
|
# 功能:杀死旧进程 -> 激活虚拟环境 -> 启动服务 -> 检查状态
|
|||
|
|
# 支持:nohup / gunicorn 两种启动方式
|
|||
|
|
# 新增:TaskWorker 监控和健康检查
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
# 颜色定义
|
|||
|
|
RED='\033[0;31m'
|
|||
|
|
GREEN='\033[0;32m'
|
|||
|
|
YELLOW='\033[1;33m'
|
|||
|
|
BLUE='\033[0;34m'
|
|||
|
|
NC='\033[0m'
|
|||
|
|
|
|||
|
|
# 项目配置
|
|||
|
|
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|||
|
|
VENV_DIR="${PROJECT_DIR}/venv"
|
|||
|
|
APP_PORT=8030
|
|||
|
|
PID_FILE="${PROJECT_DIR}/app.pid"
|
|||
|
|
GUNICORN_PID_FILE="${PROJECT_DIR}/gunicorn.pid"
|
|||
|
|
MONITOR_PID_FILE="${PROJECT_DIR}/monitor.pid"
|
|||
|
|
|
|||
|
|
# 启动模式(默认使用 gunicorn)
|
|||
|
|
START_MODE="${1:-gunicorn}" # gunicorn 或 nohup
|
|||
|
|
ENABLE_MONITOR="${2:-yes}" # 是否启动监控(yes/no)
|
|||
|
|
|
|||
|
|
echo -e "${BLUE}=========================================${NC}"
|
|||
|
|
echo -e "${BLUE} 百家号爬虫系统 - 启动服务${NC}"
|
|||
|
|
echo -e "${BLUE} 启动模式: ${START_MODE}${NC}"
|
|||
|
|
echo -e "${BLUE} 自动监控: ${ENABLE_MONITOR}${NC}"
|
|||
|
|
echo -e "${BLUE}=========================================${NC}"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# 1. 杀死旧进程
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
echo -e "${YELLOW}[1/3]${NC} 检查并停止旧服务..."
|
|||
|
|
|
|||
|
|
# 通过 PID 文件停止
|
|||
|
|
if [[ -f "${PID_FILE}" ]]; then
|
|||
|
|
OLD_PID=$(cat ${PID_FILE})
|
|||
|
|
if ps -p ${OLD_PID} > /dev/null 2>&1; then
|
|||
|
|
echo " 发现旧进程 (PID: ${OLD_PID}),正在停止..."
|
|||
|
|
kill ${OLD_PID} 2>/dev/null
|
|||
|
|
sleep 2
|
|||
|
|
|
|||
|
|
# 如果还在运行,强制终止
|
|||
|
|
if ps -p ${OLD_PID} > /dev/null 2>&1; then
|
|||
|
|
echo " 强制终止进程..."
|
|||
|
|
kill -9 ${OLD_PID} 2>/dev/null
|
|||
|
|
fi
|
|||
|
|
echo -e " ${GREEN}✓${NC} 旧进程已停止"
|
|||
|
|
fi
|
|||
|
|
rm -f ${PID_FILE}
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 停止 Gunicorn
|
|||
|
|
if [[ -f "${GUNICORN_PID_FILE}" ]]; then
|
|||
|
|
GUNICORN_PID=$(cat ${GUNICORN_PID_FILE})
|
|||
|
|
if ps -p ${GUNICORN_PID} > /dev/null 2>&1; then
|
|||
|
|
echo " 发现 Gunicorn 进程 (PID: ${GUNICORN_PID}),正在停止..."
|
|||
|
|
kill ${GUNICORN_PID} 2>/dev/null
|
|||
|
|
sleep 2
|
|||
|
|
|
|||
|
|
if ps -p ${GUNICORN_PID} > /dev/null 2>&1; then
|
|||
|
|
echo " 强制终止 Gunicorn..."
|
|||
|
|
kill -9 ${GUNICORN_PID} 2>/dev/null
|
|||
|
|
fi
|
|||
|
|
echo -e " ${GREEN}✓${NC} Gunicorn 进程已停止"
|
|||
|
|
fi
|
|||
|
|
rm -f ${GUNICORN_PID_FILE}
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 杀死所有 app.py 相关进程(包括任务线程)
|
|||
|
|
APP_PIDS=$(ps aux | grep "[p]ython.*app.py" | awk '{print $2}')
|
|||
|
|
if [[ -n "${APP_PIDS}" ]]; then
|
|||
|
|
echo " 发现运行中的 Python 进程,正在清理..."
|
|||
|
|
for pid in ${APP_PIDS}; do
|
|||
|
|
echo " 停止进程 ${pid}..."
|
|||
|
|
kill ${pid} 2>/dev/null
|
|||
|
|
done
|
|||
|
|
sleep 2
|
|||
|
|
|
|||
|
|
# 强制清理残留进程
|
|||
|
|
APP_PIDS=$(ps aux | grep "[p]ython.*app.py" | awk '{print $2}')
|
|||
|
|
if [[ -n "${APP_PIDS}" ]]; then
|
|||
|
|
echo " 强制清理残留进程..."
|
|||
|
|
for pid in ${APP_PIDS}; do
|
|||
|
|
kill -9 ${pid} 2>/dev/null
|
|||
|
|
done
|
|||
|
|
fi
|
|||
|
|
echo -e " ${GREEN}✓${NC} 所有旧进程已清理"
|
|||
|
|
else
|
|||
|
|
echo -e " ${GREEN}✓${NC} 未发现运行中的进程"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 清理端口占用
|
|||
|
|
PORT_PID=$(lsof -ti:${APP_PORT} 2>/dev/null)
|
|||
|
|
if [[ -n "${PORT_PID}" ]]; then
|
|||
|
|
echo " 端口 ${APP_PORT} 被占用 (PID: ${PORT_PID}),正在释放..."
|
|||
|
|
kill -9 ${PORT_PID} 2>/dev/null
|
|||
|
|
echo -e " ${GREEN}✓${NC} 端口已释放"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 停止监控进程
|
|||
|
|
if [[ -f "${MONITOR_PID_FILE}" ]]; then
|
|||
|
|
MONITOR_PID=$(cat ${MONITOR_PID_FILE})
|
|||
|
|
if ps -p ${MONITOR_PID} > /dev/null 2>&1; then
|
|||
|
|
echo " 发现监控进程 (PID: ${MONITOR_PID}),正在停止..."
|
|||
|
|
kill ${MONITOR_PID} 2>/dev/null
|
|||
|
|
sleep 1
|
|||
|
|
echo -e " ${GREEN}✓${NC} 监控进程已停止"
|
|||
|
|
fi
|
|||
|
|
rm -f ${MONITOR_PID_FILE}
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 清理 TaskWorker 锁文件
|
|||
|
|
if [[ -f "data/taskworker.lock" ]]; then
|
|||
|
|
echo " 清理 TaskWorker 锁文件..."
|
|||
|
|
rm -f data/taskworker.lock
|
|||
|
|
echo -e " ${GREEN}✓${NC} 锁文件已清理"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# 2. 激活虚拟环境
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
echo -e "${YELLOW}[2/3]${NC} 激活虚拟环境..."
|
|||
|
|
|
|||
|
|
if [[ ! -d "${VENV_DIR}" ]]; then
|
|||
|
|
echo -e " ${RED}✗${NC} 虚拟环境不存在: ${VENV_DIR}"
|
|||
|
|
echo " 请先创建虚拟环境:"
|
|||
|
|
echo " python3 -m venv .venv"
|
|||
|
|
echo " source .venv/bin/activate"
|
|||
|
|
echo " pip install -r requirements.txt"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
source ${VENV_DIR}/bin/activate
|
|||
|
|
|
|||
|
|
if [[ "$VIRTUAL_ENV" != "" ]]; then
|
|||
|
|
echo -e " ${GREEN}✓${NC} 虚拟环境已激活: ${VIRTUAL_ENV}"
|
|||
|
|
else
|
|||
|
|
echo -e " ${RED}✗${NC} 虚拟环境激活失败"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# 3. 启动服务
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
echo -e "${YELLOW}[3/5]${NC} 启动服务..."
|
|||
|
|
|
|||
|
|
cd ${PROJECT_DIR}
|
|||
|
|
|
|||
|
|
if [[ "${START_MODE}" == "gunicorn" ]]; then
|
|||
|
|
# 使用 Gunicorn 启动
|
|||
|
|
echo " 使用 Gunicorn 启动服务..."
|
|||
|
|
|
|||
|
|
# 检查 gunicorn 是否安装
|
|||
|
|
if ! command -v gunicorn &> /dev/null; then
|
|||
|
|
echo -e " ${RED}✗${NC} Gunicorn 未安装,请先安装:"
|
|||
|
|
echo " pip install gunicorn"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 后台启动 Gunicorn
|
|||
|
|
gunicorn -c gunicorn_config.py app:app
|
|||
|
|
|
|||
|
|
# 等待服务启动(daemon模式需要更长时间)
|
|||
|
|
echo " 等待服务启动..."
|
|||
|
|
sleep 5
|
|||
|
|
|
|||
|
|
# 检查服务是否成功启动
|
|||
|
|
if [[ -f "${GUNICORN_PID_FILE}" ]]; then
|
|||
|
|
GUNICORN_PID=$(cat ${GUNICORN_PID_FILE})
|
|||
|
|
if ps -p ${GUNICORN_PID} > /dev/null 2>&1; then
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo -e "${GREEN} Gunicorn 服务启动成功!${NC}"
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo ""
|
|||
|
|
echo -e " PID: ${GUNICORN_PID}"
|
|||
|
|
echo -e " 端口: ${APP_PORT}"
|
|||
|
|
echo -e " 访问地址: http://0.0.0.0:${APP_PORT}"
|
|||
|
|
echo -e " 访问日志: logs/gunicorn_access.log"
|
|||
|
|
echo -e " 错误日志: logs/gunicorn_error.log"
|
|||
|
|
echo ""
|
|||
|
|
echo -e "查看日志: ${BLUE}tail -f logs/gunicorn_error.log${NC}"
|
|||
|
|
echo -e "停止服务: ${BLUE}./stop.sh${NC}"
|
|||
|
|
echo -e "重启服务: ${BLUE}./restart.sh${NC}"
|
|||
|
|
echo -e "检查状态: ${BLUE}python check_taskworker.py${NC}"
|
|||
|
|
echo ""
|
|||
|
|
else
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${RED}=========================================${NC}"
|
|||
|
|
echo -e "${RED} Gunicorn 启动失败!${NC}"
|
|||
|
|
echo -e "${RED}=========================================${NC}"
|
|||
|
|
echo ""
|
|||
|
|
echo "请检查日志文件:"
|
|||
|
|
echo " tail -n 50 logs/gunicorn_error.log"
|
|||
|
|
echo ""
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
else
|
|||
|
|
# 尝试通过端口检查服务是否启动
|
|||
|
|
echo " PID文件未生成,检查端口占用..."
|
|||
|
|
sleep 2
|
|||
|
|
|
|||
|
|
if lsof -i:${APP_PORT} > /dev/null 2>&1; then
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo -e "${GREEN} Gunicorn 服务已启动!${NC}"
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo ""
|
|||
|
|
echo -e " 端口: ${APP_PORT}"
|
|||
|
|
echo -e " 访问地址: http://0.0.0.0:${APP_PORT}"
|
|||
|
|
echo -e " 访问日志: logs/gunicorn_access.log"
|
|||
|
|
echo -e " 错误日志: logs/gunicorn_error.log"
|
|||
|
|
echo ""
|
|||
|
|
echo -e "查看日志: ${BLUE}tail -f logs/gunicorn_error.log${NC}"
|
|||
|
|
echo -e "停止服务: ${BLUE}./stop.sh${NC}"
|
|||
|
|
echo -e "检查状态: ${BLUE}python check_taskworker.py${NC}"
|
|||
|
|
echo ""
|
|||
|
|
else
|
|||
|
|
echo -e "${RED}✗${NC} 服务启动失败,请检查错误日志:"
|
|||
|
|
echo " tail -n 50 logs/gunicorn_error.log"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
else
|
|||
|
|
# 使用 nohup 启动
|
|||
|
|
echo " 使用 nohup 启动服务..."
|
|||
|
|
|
|||
|
|
# 后台启动
|
|||
|
|
nohup python app.py > logs/app.log 2>&1 &
|
|||
|
|
NEW_PID=$!
|
|||
|
|
|
|||
|
|
# 保存 PID
|
|||
|
|
echo ${NEW_PID} > ${PID_FILE}
|
|||
|
|
|
|||
|
|
# 等待服务启动
|
|||
|
|
echo " 等待服务启动..."
|
|||
|
|
sleep 3
|
|||
|
|
|
|||
|
|
# 检查服务是否成功启动
|
|||
|
|
if ps -p ${NEW_PID} > /dev/null 2>&1; then
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo -e "${GREEN} 服务启动成功!${NC}"
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo ""
|
|||
|
|
echo -e " PID: ${NEW_PID}"
|
|||
|
|
echo -e " 端口: ${APP_PORT}"
|
|||
|
|
echo -e " 访问地址: http://127.0.0.1:${APP_PORT}"
|
|||
|
|
echo -e " 日志文件: logs/app.log"
|
|||
|
|
echo ""
|
|||
|
|
echo -e "查看日志: ${BLUE}tail -f logs/app.log${NC}"
|
|||
|
|
echo -e "停止服务: ${BLUE}kill ${NEW_PID}${NC}"
|
|||
|
|
echo -e "检查状态: ${BLUE}python check_taskworker.py${NC}"
|
|||
|
|
echo ""
|
|||
|
|
else
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${RED}=========================================${NC}"
|
|||
|
|
echo -e "${RED} 服务启动失败!${NC}"
|
|||
|
|
echo -e "${RED}=========================================${NC}"
|
|||
|
|
echo ""
|
|||
|
|
echo "请检查日志文件:"
|
|||
|
|
echo " tail -n 50 logs/app.log"
|
|||
|
|
echo ""
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# 4. 检查 TaskWorker 状态
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
echo -e "${YELLOW}[4/5]${NC} 检查 TaskWorker 状态..."
|
|||
|
|
|
|||
|
|
# 等待服务完全启动
|
|||
|
|
sleep 3
|
|||
|
|
|
|||
|
|
# 检查健康状态
|
|||
|
|
if command -v curl &> /dev/null; then
|
|||
|
|
HEALTH_CHECK=$(curl -s http://localhost:${APP_PORT}/health/taskworker 2>/dev/null)
|
|||
|
|
|
|||
|
|
if [[ -n "${HEALTH_CHECK}" ]]; then
|
|||
|
|
STATUS=$(echo ${HEALTH_CHECK} | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
|
|||
|
|
|
|||
|
|
if [[ "${STATUS}" == "healthy" ]]; then
|
|||
|
|
echo -e " ${GREEN}✓${NC} TaskWorker 状态: ${GREEN}healthy${NC}"
|
|||
|
|
|
|||
|
|
# 提取详细信息
|
|||
|
|
ALIVE_THREADS=$(echo ${HEALTH_CHECK} | grep -o '"alive_threads":[0-9]*' | cut -d':' -f2)
|
|||
|
|
PENDING=$(echo ${HEALTH_CHECK} | grep -o '"pending":[0-9]*' | cut -d':' -f2)
|
|||
|
|
|
|||
|
|
echo " 活跃线程: ${ALIVE_THREADS}"
|
|||
|
|
echo " 待处理任务: ${PENDING}"
|
|||
|
|
else
|
|||
|
|
echo -e " ${YELLOW}⚠${NC} TaskWorker 状态: ${YELLOW}${STATUS}${NC}"
|
|||
|
|
echo " 建议运行: python check_taskworker.py --fix"
|
|||
|
|
fi
|
|||
|
|
else
|
|||
|
|
echo -e " ${YELLOW}⚠${NC} 无法连接到服务,稍后重试"
|
|||
|
|
fi
|
|||
|
|
else
|
|||
|
|
echo " 跳过健康检查(curl 未安装)"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# 5. 启动监控进程(可选)
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
if [[ "${ENABLE_MONITOR}" == "yes" ]]; then
|
|||
|
|
echo -e "${YELLOW}[5/5]${NC} 启动 TaskWorker 自动监控..."
|
|||
|
|
|
|||
|
|
if [[ -f "taskworker_monitor.py" ]]; then
|
|||
|
|
# 后台启动监控
|
|||
|
|
nohup python taskworker_monitor.py > logs/monitor.out 2>&1 &
|
|||
|
|
MONITOR_PID=$!
|
|||
|
|
echo ${MONITOR_PID} > ${MONITOR_PID_FILE}
|
|||
|
|
|
|||
|
|
sleep 1
|
|||
|
|
|
|||
|
|
if ps -p ${MONITOR_PID} > /dev/null 2>&1; then
|
|||
|
|
echo -e " ${GREEN}✓${NC} 监控进程已启动 (PID: ${MONITOR_PID})"
|
|||
|
|
echo " 监控日志: logs/taskworker_monitor.log"
|
|||
|
|
echo " 输出日志: logs/monitor.out"
|
|||
|
|
else
|
|||
|
|
echo -e " ${YELLOW}⚠${NC} 监控进程启动失败"
|
|||
|
|
fi
|
|||
|
|
else
|
|||
|
|
echo -e " ${YELLOW}⚠${NC} 监控脚本不存在: taskworker_monitor.py"
|
|||
|
|
fi
|
|||
|
|
else
|
|||
|
|
echo -e "${YELLOW}[5/5]${NC} 跳过自动监控(使用 './start.sh gunicorn yes' 启用)"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo -e "${GREEN} 服务启动完成!${NC}"
|
|||
|
|
echo -e "${GREEN}=========================================${NC}"
|
|||
|
|
echo ""
|