#!/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 ""