针对场景4进行修改
This commit is contained in:
477
start_all_vllm.sh
Executable file
477
start_all_vllm.sh
Executable file
@@ -0,0 +1,477 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==============================================================================
|
||||
# 无人机自然语言控制项目 - vLLM 一键启动脚本(新增,不修改 start_all.sh)
|
||||
# ==============================================================================
|
||||
# 功能:启动所有必需的服务(vLLM推理模型、llama-server embedding模型、FastAPI后端)
|
||||
# 用法:./start_all_vllm.sh [选项]
|
||||
# ==============================================================================
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 默认配置(可通过环境变量覆盖)
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LLAMA_SERVER_DIR="${LLAMA_SERVER_DIR:-~/llama.cpp/build/bin}"
|
||||
INFERENCE_MODEL_DIR="${INFERENCE_MODEL_DIR:-/home/a/models/AWQ/Qwen3-4B-Instruct-2507}"
|
||||
VLLM_CONDA_ENV="${VLLM_CONDA_ENV:-VLLM}"
|
||||
VLLM_SPECULATIVE_CONFIG="${VLLM_SPECULATIVE_CONFIG:-}"
|
||||
VLLM_GPU_MEMORY_UTILIZATION="${VLLM_GPU_MEMORY_UTILIZATION:-0.6}"
|
||||
VLLM_SERVED_MODEL_NAME="${VLLM_SERVED_MODEL_NAME:-local-model}"
|
||||
EMBEDDING_MODEL="${EMBEDDING_MODEL:-~/models/gguf/Qwen3/Qwen3-Embedding-4B/Qwen3-Embedding-4B-Q5_K_M.gguf}"
|
||||
VENV_PATH="${VENV_PATH:-${PROJECT_ROOT}/backend_service/venv}"
|
||||
LOG_DIR="${LOG_DIR:-${PROJECT_ROOT}/logs}"
|
||||
PID_FILE="${LOG_DIR}/services_vllm.pid"
|
||||
|
||||
# 端口配置
|
||||
INFERENCE_PORT="${INFERENCE_PORT:-8081}"
|
||||
EMBEDDING_PORT="${EMBEDDING_PORT:-8090}"
|
||||
API_PORT="${API_PORT:-8000}"
|
||||
|
||||
# 创建日志目录
|
||||
mkdir -p "${LOG_DIR}"
|
||||
|
||||
# ==============================================================================
|
||||
# 辅助函数
|
||||
# ==============================================================================
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查命令是否存在
|
||||
check_command() {
|
||||
if ! command -v "$1" &> /dev/null; then
|
||||
print_error "$1 命令未找到,请先安装"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# 检查端口是否被占用
|
||||
check_port() {
|
||||
local port=$1
|
||||
if lsof -Pi :${port} -sTCP:LISTEN -t >/dev/null 2>&1 ; then
|
||||
return 0 # 端口被占用
|
||||
else
|
||||
return 1 # 端口空闲
|
||||
fi
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_service() {
|
||||
local url=$1
|
||||
local service_name=$2
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
|
||||
print_info "等待 ${service_name} 启动..."
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if curl -s "${url}" > /dev/null 2>&1; then
|
||||
print_success "${service_name} 已就绪"
|
||||
return 0
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
print_error "${service_name} 启动超时"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 停止所有服务
|
||||
stop_services() {
|
||||
print_info "正在停止所有服务..."
|
||||
|
||||
if [ -f "${PID_FILE}" ]; then
|
||||
while read -r pid; do
|
||||
if ps -p "$pid" > /dev/null 2>&1; then
|
||||
print_info "停止进程 PID: $pid"
|
||||
kill "$pid" 2>/dev/null || true
|
||||
fi
|
||||
done < "${PID_FILE}"
|
||||
rm -f "${PID_FILE}"
|
||||
fi
|
||||
|
||||
# 尝试通过端口停止服务
|
||||
for port in ${INFERENCE_PORT} ${EMBEDDING_PORT} ${API_PORT}; do
|
||||
if check_port ${port}; then
|
||||
local pid
|
||||
pid=$(lsof -ti:${port})
|
||||
if [ -n "$pid" ]; then
|
||||
print_info "停止占用端口 ${port} 的进程 (PID: $pid)"
|
||||
kill $pid 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
print_success "所有服务已停止"
|
||||
}
|
||||
|
||||
# 清理函数(脚本退出时调用)
|
||||
cleanup() {
|
||||
if [ "$?" -ne 0 ]; then
|
||||
print_error "启动过程中发生错误,正在清理..."
|
||||
fi
|
||||
# 注意:这里不自动停止服务,让用户手动控制
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# ==============================================================================
|
||||
# 主函数
|
||||
# ==============================================================================
|
||||
|
||||
start_services() {
|
||||
print_info "=========================================="
|
||||
print_info " 无人机自然语言控制项目 - vLLM 服务启动"
|
||||
print_info "=========================================="
|
||||
echo ""
|
||||
|
||||
# 检查必要的命令
|
||||
print_info "检查必要的命令..."
|
||||
check_command "python3" || exit 1
|
||||
check_command "curl" || exit 1
|
||||
check_command "lsof" || print_warning "lsof 未安装,将无法检查端口占用"
|
||||
check_command "conda" || exit 1
|
||||
echo ""
|
||||
|
||||
# 检查端口占用
|
||||
print_info "检查端口占用..."
|
||||
if check_port ${INFERENCE_PORT}; then
|
||||
print_warning "端口 ${INFERENCE_PORT} 已被占用,推理模型可能已在运行"
|
||||
fi
|
||||
if check_port ${EMBEDDING_PORT}; then
|
||||
print_warning "端口 ${EMBEDDING_PORT} 已被占用,Embedding模型可能已在运行"
|
||||
fi
|
||||
if check_port ${API_PORT}; then
|
||||
print_error "端口 ${API_PORT} 已被占用,请先停止占用该端口的服务"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查llama-server(用于 embedding,展开路径中的 ~)
|
||||
local llama_server_dir_expanded
|
||||
llama_server_dir_expanded=$(eval echo "${LLAMA_SERVER_DIR}")
|
||||
local llama_server="${llama_server_dir_expanded}/llama-server"
|
||||
if [ ! -f "${llama_server}" ]; then
|
||||
print_error "llama-server 未找到: ${llama_server}"
|
||||
print_info "请设置 LLAMA_SERVER_DIR 环境变量指向正确的路径"
|
||||
print_info "当前路径: ${LLAMA_SERVER_DIR}"
|
||||
print_info "展开后路径: ${llama_server_dir_expanded}"
|
||||
exit 1
|
||||
fi
|
||||
print_success "找到 llama-server: ${llama_server}"
|
||||
echo ""
|
||||
|
||||
# 检查模型路径
|
||||
local inference_model_dir_expanded
|
||||
inference_model_dir_expanded=$(eval echo "${INFERENCE_MODEL_DIR}")
|
||||
local embedding_model_expanded
|
||||
embedding_model_expanded=$(eval echo "${EMBEDDING_MODEL}")
|
||||
|
||||
if [ ! -e "${inference_model_dir_expanded}" ]; then
|
||||
print_error "推理模型目录未找到: ${inference_model_dir_expanded}"
|
||||
print_info "请设置 INFERENCE_MODEL_DIR 环境变量指向正确的模型目录"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "${inference_model_dir_expanded}" ]; then
|
||||
print_warning "推理模型路径不是目录: ${inference_model_dir_expanded}"
|
||||
print_warning "vLLM 通常需要传入模型目录(包含 config/tokenizer/权重等)"
|
||||
fi
|
||||
print_success "找到推理模型目录: ${inference_model_dir_expanded}"
|
||||
|
||||
if [ ! -f "${embedding_model_expanded}" ]; then
|
||||
print_error "Embedding模型文件未找到: ${embedding_model_expanded}"
|
||||
print_info "请设置 EMBEDDING_MODEL 环境变量指向正确的模型路径"
|
||||
exit 1
|
||||
fi
|
||||
print_success "找到Embedding模型: ${embedding_model_expanded}"
|
||||
echo ""
|
||||
|
||||
# 检查ROS2环境
|
||||
local ros2_setup="${PROJECT_ROOT}/install/setup.bash"
|
||||
if [ ! -f "${ros2_setup}" ]; then
|
||||
print_warning "ROS2 setup文件未找到: ${ros2_setup}"
|
||||
print_warning "如果项目已与ROS2解耦,可以忽略此警告"
|
||||
else
|
||||
print_success "找到ROS2 setup文件: ${ros2_setup}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查venv虚拟环境
|
||||
local venv_path_expanded
|
||||
venv_path_expanded=$(eval echo "${VENV_PATH}")
|
||||
print_info "检查venv虚拟环境: ${venv_path_expanded}"
|
||||
if [ ! -d "${venv_path_expanded}" ]; then
|
||||
print_error "venv虚拟环境目录不存在: ${venv_path_expanded}"
|
||||
print_info "请先创建venv环境: python3 -m venv ${venv_path_expanded}"
|
||||
print_info "然后安装依赖: ${venv_path_expanded}/bin/pip install -r backend_service/requirements.txt"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "${venv_path_expanded}/bin/activate" ]; then
|
||||
print_error "venv激活脚本不存在: ${venv_path_expanded}/bin/activate"
|
||||
print_error "这看起来不是一个有效的venv环境"
|
||||
exit 1
|
||||
fi
|
||||
print_success "venv虚拟环境存在: ${venv_path_expanded}"
|
||||
echo ""
|
||||
|
||||
# 初始化PID文件
|
||||
> "${PID_FILE}"
|
||||
|
||||
# ==========================================================================
|
||||
# 启动推理模型服务(vLLM)
|
||||
# ==========================================================================
|
||||
print_info "启动推理模型服务(vLLM) (端口 ${INFERENCE_PORT})..."
|
||||
bash -lc "
|
||||
set -e
|
||||
CONDA_BASE=\$(conda info --base)
|
||||
if [ -z \"\${CONDA_BASE}\" ] || [ ! -f \"\${CONDA_BASE}/etc/profile.d/conda.sh\" ]; then
|
||||
echo \"[ERROR] conda.sh 未找到:\${CONDA_BASE}/etc/profile.d/conda.sh\" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
source \"\${CONDA_BASE}/etc/profile.d/conda.sh\"
|
||||
conda activate \"${VLLM_CONDA_ENV}\"
|
||||
export VLLM_USE_V1=1
|
||||
echo \"[DEBUG] conda_env=${VLLM_CONDA_ENV}\"
|
||||
echo \"[DEBUG] CONDA_PREFIX=\${CONDA_PREFIX}\"
|
||||
echo \"[DEBUG] which python: \$(which python)\"
|
||||
echo \"[DEBUG] which vllm: \$(which vllm)\"
|
||||
vllm --version || true
|
||||
python -c \"import sys; print('[DEBUG] python:', sys.version)\" || true
|
||||
python -c \"import torch; print('[DEBUG] torch:', torch.__version__, 'cuda:', torch.version.cuda, 'available:', torch.cuda.is_available())\" || true
|
||||
|
||||
SPEC_ARGS=()
|
||||
if [ -n \"${VLLM_SPECULATIVE_CONFIG}\" ]; then
|
||||
SPEC_ARGS+=(--speculative-config \"${VLLM_SPECULATIVE_CONFIG}\")
|
||||
fi
|
||||
exec vllm serve \"${inference_model_dir_expanded}\" \
|
||||
--host=0.0.0.0 \
|
||||
--port=${INFERENCE_PORT} \
|
||||
--dtype=auto \
|
||||
--max-num-seqs=1 \
|
||||
--max-model-len=16384 \
|
||||
\"\${SPEC_ARGS[@]}\" \
|
||||
--served-model-name \"${VLLM_SERVED_MODEL_NAME}\" \
|
||||
--trust-remote-code \
|
||||
--gpu-memory-utilization=${VLLM_GPU_MEMORY_UTILIZATION} \
|
||||
--uvicorn-log-level=debug \
|
||||
--max-parallel-loading-workers 4
|
||||
" > "${LOG_DIR}/vllm_inference_model.log" 2>&1 &
|
||||
local inference_pid=$!
|
||||
echo $inference_pid >> "${PID_FILE}"
|
||||
print_success "推理模型服务(vLLM)已启动 (PID: $inference_pid)"
|
||||
print_info "日志文件: ${LOG_DIR}/vllm_inference_model.log"
|
||||
echo ""
|
||||
|
||||
# ==========================================================================
|
||||
# 启动Embedding模型服务(llama-server)
|
||||
# ==========================================================================
|
||||
print_info "启动Embedding模型服务 (端口 ${EMBEDDING_PORT})..."
|
||||
cd "${llama_server_dir_expanded}"
|
||||
nohup ./llama-server \
|
||||
-m "${embedding_model_expanded}" \
|
||||
--gpu-layers 36 \
|
||||
--port ${EMBEDDING_PORT} \
|
||||
--embeddings \
|
||||
--pooling last \
|
||||
--host 0.0.0.0 \
|
||||
> "${LOG_DIR}/embedding_model.log" 2>&1 &
|
||||
local embedding_pid=$!
|
||||
echo $embedding_pid >> "${PID_FILE}"
|
||||
print_success "Embedding模型服务已启动 (PID: $embedding_pid)"
|
||||
print_info "日志文件: ${LOG_DIR}/embedding_model.log"
|
||||
echo ""
|
||||
|
||||
# ==========================================================================
|
||||
# 等待模型服务就绪
|
||||
# ==========================================================================
|
||||
print_info "等待模型服务就绪..."
|
||||
sleep 3 # 给服务一些启动时间
|
||||
|
||||
# 等待推理模型服务(vLLM)
|
||||
if ! wait_for_service "http://localhost:${INFERENCE_PORT}/health" "推理模型服务(vLLM)"; then
|
||||
if ! wait_for_service "http://localhost:${INFERENCE_PORT}/v1/models" "推理模型服务(vLLM)"; then
|
||||
print_warning "推理模型服务(vLLM)可能未完全就绪,但将继续启动"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 等待Embedding模型服务
|
||||
if ! wait_for_service "http://localhost:${EMBEDDING_PORT}/health" "Embedding模型服务"; then
|
||||
if ! wait_for_service "http://localhost:${EMBEDDING_PORT}/v1/models" "Embedding模型服务"; then
|
||||
print_warning "Embedding模型服务可能未完全就绪,但将继续启动"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ==========================================================================
|
||||
# 启动FastAPI后端服务
|
||||
# ==========================================================================
|
||||
print_info "启动FastAPI后端服务 (端口 ${API_PORT})..."
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
# 尝试找到 dot 命令 (Graphviz) 路径,以便后端生成图片
|
||||
DOT_PATH=$(which dot || true)
|
||||
DOT_DIR=""
|
||||
if [ -n "$DOT_PATH" ]; then
|
||||
DOT_DIR=$(dirname "$DOT_PATH")
|
||||
print_info "找到 Graphviz dot 命令: $DOT_PATH"
|
||||
else
|
||||
print_warning "未找到 dot 命令,可视化图片生成可能会失败"
|
||||
# 尝试默认的 miniconda 路径作为备选
|
||||
if [ -f "/home/a/miniconda3/bin/dot" ]; then
|
||||
DOT_DIR="/home/a/miniconda3/bin"
|
||||
print_info "使用备选路径: $DOT_DIR"
|
||||
fi
|
||||
fi
|
||||
|
||||
bash -c "
|
||||
# 激活ROS2环境(如果存在)
|
||||
if [ -f '${ros2_setup}' ]; then
|
||||
source '${ros2_setup}'
|
||||
fi
|
||||
|
||||
# 确保 Graphviz 在 PATH 中
|
||||
if [ -n '${DOT_DIR}' ]; then
|
||||
export PATH=\"${DOT_DIR}:\$PATH\"
|
||||
fi
|
||||
|
||||
# 让后端默认使用 vLLM 暴露的模型名,避免请求 local-model 导致 404
|
||||
OPENAI_MODEL=\"\${OPENAI_MODEL:-${VLLM_SERVED_MODEL_NAME}}\" \
|
||||
CLASSIFIER_MODEL=\"\${CLASSIFIER_MODEL:-${VLLM_SERVED_MODEL_NAME}}\" \
|
||||
SIMPLE_MODEL=\"\${SIMPLE_MODEL:-${VLLM_SERVED_MODEL_NAME}}\" \
|
||||
COMPLEX_MODEL=\"\${COMPLEX_MODEL:-${VLLM_SERVED_MODEL_NAME}}\" \
|
||||
cd '${PROJECT_ROOT}/backend_service' && \
|
||||
'${venv_path_expanded}/bin/python' -m uvicorn src.main:app --host 0.0.0.0 --port ${API_PORT}
|
||||
" > "${LOG_DIR}/fastapi.log" 2>&1 &
|
||||
local api_pid=$!
|
||||
echo $api_pid >> "${PID_FILE}"
|
||||
print_success "FastAPI服务已启动 (PID: $api_pid)"
|
||||
print_info "日志文件: ${LOG_DIR}/fastapi.log"
|
||||
echo ""
|
||||
|
||||
# 等待FastAPI服务就绪
|
||||
sleep 3
|
||||
if wait_for_service "http://localhost:${API_PORT}/docs" "FastAPI服务"; then
|
||||
print_success "所有服务已成功启动!"
|
||||
else
|
||||
print_warning "FastAPI服务可能未完全就绪,请检查日志: ${LOG_DIR}/fastapi.log"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 显示服务访问信息
|
||||
print_info "=========================================="
|
||||
print_info " 服务启动完成!"
|
||||
print_info "=========================================="
|
||||
print_info "推理模型API(vLLM): http://localhost:${INFERENCE_PORT}/v1"
|
||||
print_info "Embedding模型API: http://localhost:${EMBEDDING_PORT}/v1"
|
||||
print_info "FastAPI后端: http://localhost:${API_PORT}"
|
||||
print_info "API文档: http://localhost:${API_PORT}/docs"
|
||||
print_info ""
|
||||
print_info "日志文件位置:"
|
||||
print_info " - 推理模型(vLLM): ${LOG_DIR}/vllm_inference_model.log"
|
||||
print_info " - Embedding模型: ${LOG_DIR}/embedding_model.log"
|
||||
print_info " - FastAPI服务: ${LOG_DIR}/fastapi.log"
|
||||
print_info ""
|
||||
print_info "按 Ctrl+C 停止所有服务"
|
||||
print_info "=========================================="
|
||||
echo ""
|
||||
|
||||
# 设置信号处理,确保Ctrl+C时能清理
|
||||
trap 'print_info "\n正在停止服务..."; stop_services; exit 0' INT TERM
|
||||
|
||||
# 保持脚本运行
|
||||
print_info "所有服务正在运行中,查看日志请使用:"
|
||||
print_info " tail -f ${LOG_DIR}/*.log"
|
||||
echo ""
|
||||
|
||||
wait
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# 脚本入口
|
||||
# ==============================================================================
|
||||
|
||||
case "${1:-start}" in
|
||||
start)
|
||||
start_services
|
||||
;;
|
||||
stop)
|
||||
stop_services
|
||||
;;
|
||||
restart)
|
||||
stop_services
|
||||
sleep 2
|
||||
start_services
|
||||
;;
|
||||
status)
|
||||
print_info "检查服务状态..."
|
||||
if [ -f "${PID_FILE}" ]; then
|
||||
print_info "已记录的服务进程:"
|
||||
while read -r pid; do
|
||||
if ps -p "$pid" > /dev/null 2>&1; then
|
||||
print_success "PID $pid: 运行中"
|
||||
else
|
||||
print_warning "PID $pid: 已停止"
|
||||
fi
|
||||
done < "${PID_FILE}"
|
||||
else
|
||||
print_info "未找到PID文件,服务可能未启动"
|
||||
fi
|
||||
echo ""
|
||||
print_info "端口占用情况:"
|
||||
for port in ${INFERENCE_PORT} ${EMBEDDING_PORT} ${API_PORT}; do
|
||||
if check_port ${port}; then
|
||||
local pid
|
||||
pid=$(lsof -ti:${port})
|
||||
print_success "端口 ${port}: 被占用 (PID: $pid)"
|
||||
else
|
||||
print_warning "端口 ${port}: 空闲"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "用法: $0 {start|stop|restart|status}"
|
||||
echo ""
|
||||
echo "命令说明:"
|
||||
echo " start - 启动所有服务(默认)"
|
||||
echo " stop - 停止所有服务"
|
||||
echo " restart - 重启所有服务"
|
||||
echo " status - 查看服务状态"
|
||||
echo ""
|
||||
echo "环境变量配置:"
|
||||
echo " LLAMA_SERVER_DIR - llama-server所在目录 (默认: ~/llama.cpp/build/bin)"
|
||||
echo " INFERENCE_MODEL_DIR - 推理模型目录(vLLM) (默认: /home/a/models/safetensors/Qwen3-4B)"
|
||||
echo " VLLM_CONDA_ENV - conda环境名 (默认: vllm;如果你的是 VLLM,请设置为 VLLM)"
|
||||
echo " VLLM_SPECULATIVE_CONFIG - vLLM speculative JSON(可选;例如: {\"model\":\"/home/a/qwen3-4b-eagle3\",\"num_speculative_tokens\":5,\"method\":\"eagle3\"})"
|
||||
echo " VLLM_GPU_MEMORY_UTILIZATION - vLLM 显存占用比例 (默认: 0.85)"
|
||||
echo " VLLM_SERVED_MODEL_NAME - vLLM 对外模型名 (默认: local-model;后端也会用它作为默认模型名)"
|
||||
echo " EMBEDDING_MODEL - Embedding模型路径 (默认: ~/models/gguf/Qwen3/Qwen3-Embedding-4B/Qwen3-Embedding-4B-Q5_K_M.gguf)"
|
||||
echo " VENV_PATH - venv虚拟环境路径 (默认: \${PROJECT_ROOT}/backend_service/venv)"
|
||||
echo " LOG_DIR - 日志目录 (默认: \${PROJECT_ROOT}/logs)"
|
||||
echo " INFERENCE_PORT - 推理端口 (默认: 8081)"
|
||||
echo " EMBEDDING_PORT - embedding端口 (默认: 8090)"
|
||||
echo " API_PORT - 后端端口 (默认: 8000)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user