腾讯围棋2026
76.33MB · 2026-02-04
在服务器上跑队列消费时,如果直接前台执行 php think queue:work,一旦断开 SSH 会话进程就可能被挂断,导致队列停止消费。本文提供一套轻量的脚本方案(基于 nohup + &),实现队列 脱离终端常驻 + 可启停 + 可查状态。
把下面三段分别保存为 start-queue.sh、status-queue.sh、stop-queue.sh(建议放项目根目录或 bin/ 目录)。
start-queue.sh - 启动队列(支持多 worker)#!/usr/bin/env bash
#============= 用户配置区(改这里即可) =============
PHP_BIN=/www/server/php/81/bin/php # PHP 可执行文件路径
PROJECT=/www/wwwroot/example.com # 项目根目录
QUEUE_NAME=default # 队列名
TRIES=3 # 任务失败重试次数(根据业务调整)
WORKER_NUM=1 # worker 进程数量(多个 worker 可并行消费,建议 1-4)
LOG_DIR=$PROJECT/runtime/queue # 日志目录(会自动创建)
STRICT_MATCH=1 # 1=按工作目录严格匹配(推荐),0=只匹配 queue:work
#===================================================
cd "$PROJECT" || exit
# 自动按日期生成日志文件名(格式:queue-20260126.log)
LOG_FILE="$LOG_DIR/queue-$(date +%Y%m%d).log"
# 确保日志目录存在
if [ ! -d "$LOG_DIR" ]; then
mkdir -p "$LOG_DIR"
echo "Created log directory: $LOG_DIR"
fi
# 查找并杀掉旧进程(通过工作目录匹配,更精确)
if [ "$STRICT_MATCH" -eq 1 ]; then
# 找到所有 queue:work 进程,然后检查工作目录是否匹配
OLD_PIDS=$(ps -ef | grep '[q]ueue:work' | awk '{print $2}')
if [ -n "$OLD_PIDS" ]; then
for pid in $OLD_PIDS; do
# 获取进程的工作目录
if [ -d "/proc/$pid" ]; then
PWD_PATH=$(readlink /proc/$pid/cwd 2>/dev/null)
if [ "$PWD_PATH" = "$PROJECT" ]; then
echo "Killing old queue worker in $PROJECT (PID $pid) ..."
kill "$pid" 2>/dev/null
fi
fi
done
sleep 2
fi
else
# 简单匹配模式:杀掉所有 queue:work
OLD_PIDS=$(ps -ef | grep '[q]ueue:work' | awk '{print $2}')
if [ -n "$OLD_PIDS" ]; then
echo "Killing old queue workers (PIDs: $OLD_PIDS) ..."
echo "$OLD_PIDS" | xargs kill 2>/dev/null
sleep 2
fi
fi
# 启动指定数量的 worker 进程
echo "Starting $WORKER_NUM queue worker(s) in $PROJECT ..."
for i in $(seq 1 "$WORKER_NUM"); do
nohup "$PHP_BIN" think queue:work --queue "$QUEUE_NAME" --tries "$TRIES"
>>"$LOG_FILE" 2>&1 &
echo " - Worker #$i started, PID: $!"
done
echo "All workers started successfully!"
echo "Log file: $LOG_FILE"
status-queue.sh - 查看队列运行状态(显示队列名、重试次数等)#!/usr/bin/env bash
#============= 用户配置区(与 start 保持一致) =============
PROJECT=/www/wwwroot/example.com # 项目根目录
STRICT_MATCH=1 # 1=按工作目录严格匹配,0=只匹配 queue:work
#=========================================================
# 查找所有 queue:work 进程
ALL_PIDS=$(ps -ef | grep '[q]ueue:work' | grep -v grep)
if [ -z "$ALL_PIDS" ]; then
echo "Queue worker is NOT running"
exit 0
fi
# 根据匹配模式过滤
MATCHED_COUNT=0
echo "Queue worker status:"
echo "----------------------------------------"
while read -r line; do
PID=$(echo "$line" | awk '{print $2}')
# 严格匹配模式:检查工作目录
if [ "$STRICT_MATCH" -eq 1 ]; then
if [ -d "/proc/$PID" ]; then
PWD_PATH=$(readlink /proc/$PID/cwd 2>/dev/null)
if [ "$PWD_PATH" != "$PROJECT" ]; then
continue # 工作目录不匹配,跳过
fi
else
continue
fi
fi
# 提取队列名称
QUEUE_NAME=$(echo "$line" | grep -oP '(?<=--queue )S+' || echo "unknown")
# 提取重试次数
TRIES=$(echo "$line" | grep -oP '(?<=--tries )S+' || echo "N/A")
echo "PID: $PID"
echo " 队列名: $QUEUE_NAME"
echo " 重试次数: $TRIES"
echo " 工作目录: ${PWD_PATH:-$PROJECT}"
echo "----------------------------------------"
((MATCHED_COUNT++))
done <<< "$ALL_PIDS"
if [ $MATCHED_COUNT -gt 0 ]; then
echo "共 $MATCHED_COUNT 个 worker 进程在运行"
else
echo "Queue worker is NOT running in $PROJECT"
fi
stop-queue.sh - 停止队列#!/usr/bin/env bash
#============= 用户配置区(与 start 保持一致) =============
PROJECT=/www/wwwroot/example.com # 项目根目录
STRICT_MATCH=1 # 1=按工作目录严格匹配,0=只匹配 queue:work
#=========================================================
# 查找所有 queue:work 进程
ALL_PIDS=$(ps -ef | grep '[q]ueue:work' | awk '{print $2}')
if [ -z "$ALL_PIDS" ]; then
echo "Queue worker not found, nothing to stop"
exit 0
fi
# 根据匹配模式过滤
MATCHED_PIDS=()
if [ "$STRICT_MATCH" -eq 1 ]; then
# 严格匹配:检查工作目录
for pid in $ALL_PIDS; do
if [ -d "/proc/$pid" ]; then
PWD_PATH=$(readlink /proc/$pid/cwd 2>/dev/null)
if [ "$PWD_PATH" = "$PROJECT" ]; then
MATCHED_PIDS+=("$pid")
fi
fi
done
else
# 简单匹配:所有 queue:work
MATCHED_PIDS=($ALL_PIDS)
fi
if [ ${#MATCHED_PIDS[@]} -gt 0 ]; then
echo "Stopping queue workers in $PROJECT (PIDs: ${MATCHED_PIDS[*]}) ..."
for pid in "${MATCHED_PIDS[@]}"; do
kill "$pid" 2>/dev/null
done
echo "All queue workers stopped (${#MATCHED_PIDS[@]} process(es))"
else
echo "Queue worker not found in $PROJECT, nothing to stop"
fi
chmod +x start-queue.sh status-queue.sh stop-queue.sh
每个脚本顶部都有 #============= 用户配置区 =============,按你的实际环境修改:
start-queue.sh 配置说明(最重要)| 变量名 | 说明 | 示例 |
|---|---|---|
PHP_BIN | PHP 可执行文件路径 | /usr/bin/php 或 /www/server/php/81/bin/php |
PROJECT | 项目根目录(必须包含 think 命令) | /www/wwwroot/example.com |
QUEUE_NAME | 要消费的队列名 | default / mail / sms 等 |
TRIES | 任务失败重试次数 | 3(建议 2-5,根据业务容错度调整) |
WORKER_NUM | 同时启动几个 worker 进程 | 1(单机建议 1-4,太多会增加数据库压力) |
LOG_DIR | 日志目录(自动按日期分割) | $PROJECT/runtime/queue(日志文件自动按 queue-20260126.log 格式) |
STRICT_MATCH | 是否按工作目录严格匹配(通过 /proc/$pid/cwd 检查) | 1(推荐,只操作本项目进程)或 0(操作所有 queue:work) |
status-queue.sh 和 stop-queue.sh 配置只需保证 PROJECT 和 STRICT_MATCH 与 start-queue.sh 一致即可。
# 启动队列
./start-queue.sh
# 查看状态(会显示队列名、重试次数等详细信息)
./status-queue.sh
# 停止队列
./stop-queue.sh
# 查看今天的日志(实时追踪)
tail -f /www/wwwroot/example.com/runtime/queue/queue-$(date +%Y%m%d).log
# 查看所有日志文件
ls -lh /www/wwwroot/example.com/runtime/queue/
status-queue.sh 输出示例:
Queue worker status:
----------------------------------------
PID: 12345
队列名: default
重试次数: 3
工作目录: /www/wwwroot/example.com
----------------------------------------
PID: 12346
队列名: mail
重试次数: 5
工作目录: /www/wwwroot/example.com
----------------------------------------
共 2 个 worker 进程在运行
LOG_DIR:日志自动按日期分割脚本会自动按日期生成日志文件,格式为 queue-YYYYMMDD.log,所有日志统一放在 LOG_DIR 目录下:
LOG_DIR=$PROJECT/runtime/queue # 配置日志目录
实际效果:
runtime/queue/
├── queue-20260124.log (前天的日志)
├── queue-20260125.log (昨天的日志)
└── queue-20260126.log (今天的日志,正在写入)
优势:
查看今天的日志:
tail -f /www/wwwroot/example.com/runtime/queue/queue-$(date +%Y%m%d).log
TRIES:任务失败重试次数--tries 3 表示任务执行失败后会再重试 2 次(总共尝试 3 次)。根据你的业务场景调整:
2-31(失败立即进失败队列,人工介入)5 或更高WORKER_NUM:多 worker 并行消费设置为 2 或更多时,会同时启动多个进程并行消费队列(提高吞吐量):
WORKER_NUM=4 # 同时启动 4 个 worker
适用场景:
注意事项:
WORKER_NUM=1STRICT_MATCH:防误杀开关(推荐开启)当你服务器上有多个 ThinkPHP 项目都在跑队列时:
STRICT_MATCH=1(推荐):只操作"工作目录是当前项目"的进程(安全)STRICT_MATCH=0:操作所有 queue:work 进程(可能误杀其它项目)原理:
queue:work 进程的工作目录(通过 readlink /proc/$pid/cwd)$PROJECT 的进程才会被操作ps -ef 看到的命令行里不一定包含完整项目路径)为什么不用命令行匹配?
因为启动时已经 cd $PROJECT,执行的是相对路径 think,所以 ps -ef 输出里看不到完整项目路径。使用工作目录匹配更可靠。
脚本启动时用的命令是:
nohup php think queue:work --queue default --tries 3 >> queue.log 2>&1 &
nohup:让进程忽略挂断信号(SIGHUP),SSH 断开也不会被杀>> queue.log 2>&1:把标准输出/错误都追加到日志文件&:进程在后台运行,终端立即返回脚本已经内置了按日期分割日志的功能(每天自动生成新文件,格式:queue-20260126.log),避免单个日志文件过大。
查看日志文件列表:
ls -lh /www/wwwroot/example.com/runtime/queue/
# 输出示例:
# queue-20260124.log (3.2M)
# queue-20260125.log (5.1M)
# queue-20260126.log (1.8M)
自动清理 7 天前的旧日志(可加到 crontab):
# 每天凌晨 2 点清理 7 天前的日志
0 2 * * * find /www/wwwroot/example.com/runtime/queue/ -name "queue-*.log" -mtime +7 -delete
手动清理示例:
# 只保留最近 7 天
find /www/wwwroot/example.com/runtime/queue/ -name "queue-*.log" -mtime +7 -delete
# 压缩 3 天前的日志(节省空间)
find /www/wwwroot/example.com/runtime/queue/ -name "queue-*.log" -mtime +3 ! -name "*.gz" -exec gzip {} ;
检查:
PHP_BIN 路径是否正确(执行 which php 或 /www/server/php/81/bin/php -v 确认)PROJECT 路径是否正确(能否 cd $PROJECT && ls think)tail -50 /www/wwwroot/example.com/runtime/queue/queue-$(date +%Y%m%d).log 看报错不会。think-queue 底层(基于 Redis/数据库)有锁机制,多个 worker 会自动分配任务,不会重复消费。
可以写个定时任务(cron)每 5 分钟执行 status-queue.sh,如果未运行就自动执行 start-queue.sh 或发告警。
查看昨天的日志:
tail -100 /www/wwwroot/example.com/runtime/queue/queue-$(date -d "yesterday" +%Y%m%d).log
搜索最近 3 天内的所有错误:
grep -i "error|exception|fail" /www/wwwroot/example.com/runtime/queue/queue-*.log | tail -50
查看某个日期的日志:
cat /www/wwwroot/example.com/runtime/queue/queue-20260125.log
统计今天处理了多少任务(假设日志里有 "Processing job" 字样):
grep -c "Processing" /www/wwwroot/example.com/runtime/queue/queue-$(date +%Y%m%d).log