158 lines
6.0 KiB
Bash
158 lines
6.0 KiB
Bash
|
|
#!/usr/bin/env bash
|
|||
|
|
# 须用 bash 执行;若误用 sh/dash 会自动改用 bash 再跑一遍
|
|||
|
|
[ -n "${BASH_VERSION:-}" ] || exec /usr/bin/env bash "$0" ${1+"$@"}
|
|||
|
|
# 重启 langchain-chat(startup.py --all-api,与 start_all.sh 一致)
|
|||
|
|
# 日志:gangyan/logs/langchain-chat.log
|
|||
|
|
set -u
|
|||
|
|
source "$(cd "$(dirname "$0")" && pwd)/common-restart.sh"
|
|||
|
|
LOG_FILE="$LOG_DIR/langchain-chat.log"
|
|||
|
|
TRIM_LOG="$LOG_DIR/log-trim.log"
|
|||
|
|
|
|||
|
|
CONDA_SH="/opt/software/miniconda3/etc/profile.d/conda.sh"
|
|||
|
|
LC_ROOT="$GANGYAN_ROOT/langchain-chat"
|
|||
|
|
|
|||
|
|
# 与 configs/server_config.py 一致,用于旧进程未退出时按端口释放
|
|||
|
|
LANGCHAIN_LISTEN_PORTS=(7861 20000 20101 20002)
|
|||
|
|
|
|||
|
|
_kill_by_listen_ports() {
|
|||
|
|
local port pid
|
|||
|
|
for port in "${LANGCHAIN_LISTEN_PORTS[@]}"; do
|
|||
|
|
for pid in $(lsof -ti :"$port" -sTCP:LISTEN 2>/dev/null || true); do
|
|||
|
|
kill -TERM "$pid" 2>/dev/null && log_tee "按端口 ${port} TERM PID=${pid}"
|
|||
|
|
done
|
|||
|
|
done
|
|||
|
|
sleep 2
|
|||
|
|
for port in "${LANGCHAIN_LISTEN_PORTS[@]}"; do
|
|||
|
|
for pid in $(lsof -ti :"$port" -sTCP:LISTEN 2>/dev/null || true); do
|
|||
|
|
kill -KILL "$pid" 2>/dev/null && log_tee "按端口 ${port} KILL PID=${pid}"
|
|||
|
|
done
|
|||
|
|
done
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 用 ss 判断端口是否在监听。仅用 lsof 会漏掉 root 监听的端口(非 root 看不到 PID),
|
|||
|
|
# 会误判为已释放并再次启动,导致 [Errno 98] address already in use。
|
|||
|
|
_langchain_ports_still_listen() {
|
|||
|
|
local port
|
|||
|
|
for port in "${LANGCHAIN_LISTEN_PORTS[@]}"; do
|
|||
|
|
if ss -tln 2>/dev/null | grep -qE ":${port}[[:space:]]"; then
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
return 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_sudo_fuser_kill_ports() {
|
|||
|
|
command -v sudo >/dev/null 2>&1 || return 1
|
|||
|
|
if ! sudo -n true 2>/dev/null; then
|
|||
|
|
log_tee "提示: 相关端口仍被占用(可能是 root 启动的进程)。当前用户无法用 lsof 看到其 PID。"
|
|||
|
|
log_tee "请在本机执行其一: sudo bash $0 或 sudo fuser -k 7861/tcp 20000/tcp 20101/tcp 20002/tcp"
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
local port
|
|||
|
|
for port in "${LANGCHAIN_LISTEN_PORTS[@]}"; do
|
|||
|
|
if ss -tln 2>/dev/null | grep -qE ":${port}[[:space:]]"; then
|
|||
|
|
sudo -n fuser -k "${port}/tcp" 2>/dev/null && log_tee "sudo fuser -k ${port}/tcp"
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
sleep 2
|
|||
|
|
return 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_stop_langchain() {
|
|||
|
|
log_tee "======== 停止 langchain-chat ========"
|
|||
|
|
|
|||
|
|
# 多种命令行形式(-a / --all-api、conda 路径、直接 python)
|
|||
|
|
for pat in \
|
|||
|
|
"/opt/software/miniconda3/envs/langchain-chat/bin/python"'.''*'"startup.py" \
|
|||
|
|
"conda run -n langchain-chat"'.''*'"startup.py" \
|
|||
|
|
"python"'.''*'"startup.py --all-api" \
|
|||
|
|
"python"'.''*'"startup.py -a"
|
|||
|
|
do
|
|||
|
|
if pkill -f "$pat" 2>/dev/null; then
|
|||
|
|
log_tee "pkill 已匹配并发送信号: $pat"
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
# cwd 在 langchain-chat 且命令行含 startup.py(兜底)
|
|||
|
|
while read -r pid; do
|
|||
|
|
[ -z "${pid:-}" ] && continue
|
|||
|
|
[ ! -d "/proc/$pid" ] && continue
|
|||
|
|
local cwd cmdl
|
|||
|
|
cwd=$(readlink -f "/proc/$pid/cwd" 2>/dev/null || true)
|
|||
|
|
cmdl=$(tr '\0' ' ' <"/proc/$pid/cmdline" 2>/dev/null || true)
|
|||
|
|
if [[ "$cmdl" == *startup.py* ]] && [[ "$cwd" == "$LC_ROOT" ]]; then
|
|||
|
|
kill -TERM "$pid" 2>/dev/null && log_tee "按 cwd+argv TERM PID=$pid"
|
|||
|
|
fi
|
|||
|
|
done < <(pgrep -f 'startup\.py' 2>/dev/null || true)
|
|||
|
|
|
|||
|
|
sleep 2
|
|||
|
|
|
|||
|
|
# pkill 未匹配到旧进程时,端口仍可能被旧实例占用(含 root 启动、非 root 无法用 lsof 杀)
|
|||
|
|
if _langchain_ports_still_listen; then
|
|||
|
|
log_tee "langchain 相关端口仍监听 (7861/20000/20101/20002),按 lsof 可见 PID 清理…"
|
|||
|
|
_kill_by_listen_ports
|
|||
|
|
else
|
|||
|
|
log_tee "相关端口已释放,跳过端口强杀"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if _langchain_ports_still_listen; then
|
|||
|
|
log_tee "端口仍被占用,尝试免密 sudo fuser 清理…"
|
|||
|
|
_sudo_fuser_kill_ports || true
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
local _w=0
|
|||
|
|
while _langchain_ports_still_listen && [ "$_w" -lt 30 ]; do
|
|||
|
|
sleep 1
|
|||
|
|
_w=$((_w + 1))
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
if _langchain_ports_still_listen; then
|
|||
|
|
log_tee "错误: 7861/20000/20101/20002 仍有端口在监听,无法安全启动第二个实例。"
|
|||
|
|
log_tee "请先释放端口后再执行本脚本(见上方 sudo 提示)。"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
sleep 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_stop_langchain
|
|||
|
|
|
|||
|
|
# 停止旧的日志裁剪守护(避免重复多开)
|
|||
|
|
pkill -f "log-trim-daemon\.sh --file ${LOG_FILE}" 2>/dev/null || true
|
|||
|
|
|
|||
|
|
log_tee "======== 启动 langchain-chat (--all-api) ========"
|
|||
|
|
if [ ! -f "$CONDA_SH" ]; then
|
|||
|
|
log_tee "错误: 未找到 $CONDA_SH"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
# shellcheck source=/dev/null
|
|||
|
|
source "$CONDA_SH"
|
|||
|
|
conda activate langchain-chat
|
|||
|
|
cd "$LC_ROOT"
|
|||
|
|
export PYTHONPATH="$LC_ROOT"
|
|||
|
|
# PDF 预览与 kb_config.PDF_CONVERT_KB_ROOT 一致(地址归一化在 Python 内完成)
|
|||
|
|
export PDF_CONVERT_KB_ROOT="${PDF_CONVERT_KB_ROOT:-$LC_ROOT/knowledge_base}"
|
|||
|
|
# 明确使用可请求的地址(:- 无法覆盖已设置的 0.0.0.0,此处强制纠正)
|
|||
|
|
if [[ "${PDF_CONVERT_API_URL:-}" == *0.0.0.0* ]] || [[ -z "${PDF_CONVERT_API_URL:-}" ]]; then
|
|||
|
|
export PDF_CONVERT_API_URL="http://127.0.0.1:6006/convert/"
|
|||
|
|
fi
|
|||
|
|
# 避免旧 .pyc 仍含硬编码的 0.0.0.0:6006
|
|||
|
|
rm -f "$LC_ROOT/configs/__pycache__/kb_config."*.pyc 2>/dev/null || true
|
|||
|
|
rm -f "$LC_ROOT/server/knowledge_base/__pycache__/file_converter."*.pyc 2>/dev/null || true
|
|||
|
|
rm -f "$LC_ROOT/server/knowledge_base/__pycache__/cleanpdf."*.pyc 2>/dev/null || true
|
|||
|
|
rm -f "$LC_ROOT/server/knowledge_base/__pycache__/pdf_convert_url."*.pyc 2>/dev/null || true
|
|||
|
|
|
|||
|
|
# 控制日志文件总大小(保留最后 5MB,避免 langchain-chat.log 无限增长)
|
|||
|
|
nohup bash "$GANGYAN_ROOT/scripts/log-trim-daemon.sh" --file "$LOG_FILE" --max-mb 5 --interval-sec 3 >> "$TRIM_LOG" 2>&1 &
|
|||
|
|
log_tee "已启动日志裁剪守护,日志: $TRIM_LOG"
|
|||
|
|
|
|||
|
|
nohup python startup.py --all-api >> "$LOG_FILE" 2>&1 &
|
|||
|
|
log_tee "已后台启动,PID=$! ,API 约 7861 ,日志: $LOG_FILE"
|
|||
|
|
|
|||
|
|
# 本地 PDF 预览依赖的转换微服务(:6006);未监听时再启动
|
|||
|
|
if ! ss -tln 2>/dev/null | grep -qE ':6006[[:space:]]'; then
|
|||
|
|
bash "$GANGYAN_ROOT/scripts/pdf-convert-service.sh" >> "$LOG_DIR/pdf-convert-service.log" 2>&1 || log_tee "提示: pdf-convert-service 启动失败,见 $LOG_DIR/pdf-convert-service.log"
|
|||
|
|
else
|
|||
|
|
log_tee "pdf-convert-service 端口 6006 已在监听,跳过"
|
|||
|
|
fi
|