- 新增日志文件输出功能,支持配置日志文件路径和级别 - 添加心跳故障排查脚本 check-heartbeat.sh - 支持通过环境变量 LOG_FILE 设置日志文件路径 - 日志自动创建目录,支持相对路径和绝对路径 - 优化日志初始化逻辑,支持直接写入文件 - 改进配置加载,支持日志配置项 - 完善文档,添加故障排查章节和日志功能说明 - 更新版本号至 v1.1.0
380 lines
10 KiB
Bash
Executable File
380 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# ============================================
|
||
# LinkMaster 节点端运行脚本
|
||
# 用途:启动、停止、重启节点端服务
|
||
# ============================================
|
||
|
||
set -e
|
||
|
||
# 颜色输出
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 脚本目录
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
cd "$SCRIPT_DIR"
|
||
|
||
# 配置
|
||
BINARY_NAME="agent"
|
||
LOG_FILE="node.log"
|
||
PID_FILE="node.pid"
|
||
BACKEND_URL="${BACKEND_URL:-http://localhost:8080}"
|
||
|
||
# 获取PID
|
||
get_pid() {
|
||
if [ -f "$PID_FILE" ]; then
|
||
PID=$(cat "$PID_FILE")
|
||
if ps -p "$PID" > /dev/null 2>&1; then
|
||
echo "$PID"
|
||
else
|
||
rm -f "$PID_FILE"
|
||
echo ""
|
||
fi
|
||
else
|
||
echo ""
|
||
fi
|
||
}
|
||
|
||
# 拉取最新源码并编译
|
||
update_and_build() {
|
||
echo -e "${BLUE}拉取最新源码...${NC}"
|
||
|
||
# 检查是否在 Git 仓库中
|
||
if [ ! -d ".git" ]; then
|
||
echo -e "${YELLOW}警告: 当前目录不是 Git 仓库,跳过代码更新${NC}"
|
||
return 0
|
||
fi
|
||
|
||
# 配置 Git safe.directory,解决所有权问题
|
||
CURRENT_DIR=$(pwd)
|
||
git config --global --add safe.directory "$CURRENT_DIR" 2>/dev/null || true
|
||
|
||
# 拉取最新代码
|
||
if git pull 2>&1; then
|
||
echo -e "${GREEN}✓ 代码更新完成${NC}"
|
||
else
|
||
PULL_EXIT_CODE=$?
|
||
echo -e "${YELLOW}警告: Git 拉取失败(退出码: $PULL_EXIT_CODE),将使用当前代码继续${NC}"
|
||
echo -e "${YELLOW}可能原因: 网络问题、权限问题或本地有未提交的更改${NC}"
|
||
fi
|
||
|
||
# 检查 Go 环境
|
||
if ! command -v go > /dev/null 2>&1; then
|
||
echo -e "${RED}错误: 未找到 Go 环境,无法编译${NC}"
|
||
exit 1
|
||
fi
|
||
|
||
# 配置 Go 代理(使用国内镜像)
|
||
if [ -z "$GOPROXY" ]; then
|
||
# 测试并选择最快的 Go 代理
|
||
GO_PROXY_MIRRORS=(
|
||
"https://goproxy.cn,direct"
|
||
"https://goproxy.io,direct"
|
||
"https://mirrors.aliyun.com/go-proxy/,direct"
|
||
)
|
||
|
||
for proxy in "${GO_PROXY_MIRRORS[@]}"; do
|
||
proxy_url=$(echo "$proxy" | cut -d',' -f1)
|
||
if curl -sf --connect-timeout 2 "${proxy_url}" > /dev/null 2>&1; then
|
||
export GOPROXY="$proxy"
|
||
export GOSUMDB=sum.golang.google.cn
|
||
break
|
||
fi
|
||
done
|
||
|
||
# 如果所有镜像都不可用,使用默认配置
|
||
if [ -z "$GOPROXY" ]; then
|
||
export GOPROXY="https://proxy.golang.org,direct"
|
||
export GOSUMDB=sum.golang.google.cn
|
||
fi
|
||
fi
|
||
|
||
# 更新依赖
|
||
echo -e "${BLUE}更新 Go 依赖(使用代理: ${GOPROXY})...${NC}"
|
||
if ! go mod download 2>&1; then
|
||
echo -e "${YELLOW}警告: 依赖更新失败,尝试使用官方源...${NC}"
|
||
# 如果失败,尝试使用官方源
|
||
OLD_GOPROXY="$GOPROXY"
|
||
export GOPROXY="https://proxy.golang.org,direct"
|
||
if ! go mod download 2>&1; then
|
||
export GOPROXY="$OLD_GOPROXY"
|
||
echo -e "${YELLOW}警告: 依赖更新失败,尝试继续编译${NC}"
|
||
else
|
||
echo -e "${GREEN}✓ 依赖更新完成${NC}"
|
||
fi
|
||
else
|
||
echo -e "${GREEN}✓ 依赖更新完成${NC}"
|
||
fi
|
||
|
||
# 编译
|
||
echo -e "${BLUE}编译二进制文件...${NC}"
|
||
ARCH=$(uname -m)
|
||
case $ARCH in
|
||
x86_64)
|
||
ARCH="amd64"
|
||
;;
|
||
aarch64|arm64)
|
||
ARCH="arm64"
|
||
;;
|
||
*)
|
||
ARCH="amd64"
|
||
;;
|
||
esac
|
||
|
||
if GOOS=linux GOARCH=${ARCH} CGO_ENABLED=0 go build -buildvcs=false -ldflags="-w -s" -o "$BINARY_NAME" ./cmd/agent 2>&1; then
|
||
if [ -f "$BINARY_NAME" ] && [ -s "$BINARY_NAME" ]; then
|
||
chmod +x "$BINARY_NAME"
|
||
echo -e "${GREEN}✓ 编译成功${NC}"
|
||
else
|
||
echo -e "${RED}错误: 编译失败,未生成二进制文件${NC}"
|
||
exit 1
|
||
fi
|
||
else
|
||
echo -e "${RED}错误: 编译失败${NC}"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 检查二进制文件
|
||
check_binary() {
|
||
if [ ! -f "$BINARY_NAME" ]; then
|
||
echo -e "${RED}错误: 找不到二进制文件 $BINARY_NAME${NC}"
|
||
echo -e "${YELLOW}尝试编译...${NC}"
|
||
update_and_build
|
||
return
|
||
fi
|
||
|
||
if [ ! -x "$BINARY_NAME" ]; then
|
||
chmod +x "$BINARY_NAME"
|
||
fi
|
||
}
|
||
|
||
# 检查端口占用
|
||
check_port() {
|
||
if command -v lsof > /dev/null 2>&1; then
|
||
PORT_PID=$(lsof -ti :2200 2>/dev/null || echo "")
|
||
if [ -n "$PORT_PID" ]; then
|
||
# 检查是否是我们的进程
|
||
if [ -f "$PID_FILE" ] && [ "$PORT_PID" = "$(cat "$PID_FILE")" ]; then
|
||
return 0
|
||
fi
|
||
echo -e "${YELLOW}警告: 端口2200已被占用 (PID: $PORT_PID)${NC}"
|
||
echo -e "${YELLOW}是否要停止该进程? (y/n)${NC}"
|
||
read -r answer
|
||
if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then
|
||
kill "$PORT_PID" 2>/dev/null || true
|
||
sleep 1
|
||
else
|
||
echo -e "${RED}取消启动${NC}"
|
||
exit 1
|
||
fi
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 启动服务
|
||
start() {
|
||
PID=$(get_pid)
|
||
if [ -n "$PID" ]; then
|
||
echo -e "${YELLOW}节点端已在运行 (PID: $PID)${NC}"
|
||
return 0
|
||
fi
|
||
|
||
# 拉取最新源码并编译
|
||
update_and_build
|
||
|
||
check_port
|
||
|
||
echo -e "${BLUE}启动节点端服务...${NC}"
|
||
echo -e "${BLUE}后端地址: $BACKEND_URL${NC}"
|
||
echo -e "${BLUE}日志文件: $LOG_FILE${NC}"
|
||
|
||
# 设置环境变量
|
||
export BACKEND_URL="$BACKEND_URL"
|
||
export LOG_FILE="$LOG_FILE"
|
||
|
||
# 后台运行(日志现在由程序直接写入文件,这里保留重定向作为备份)
|
||
nohup ./"$BINARY_NAME" >> "$LOG_FILE" 2>&1 &
|
||
NEW_PID=$!
|
||
|
||
# 保存PID
|
||
echo "$NEW_PID" > "$PID_FILE"
|
||
|
||
# 等待启动
|
||
sleep 2
|
||
|
||
# 检查是否启动成功
|
||
if ps -p "$NEW_PID" > /dev/null 2>&1; then
|
||
# 再次检查健康状态
|
||
sleep 1
|
||
if curl -s http://localhost:2200/api/health > /dev/null 2>&1; then
|
||
echo -e "${GREEN}✓ 节点端已启动 (PID: $NEW_PID)${NC}"
|
||
echo -e "${BLUE}日志文件: $LOG_FILE${NC}"
|
||
echo -e "${BLUE}查看日志: tail -f $LOG_FILE${NC}"
|
||
else
|
||
echo -e "${GREEN}✓ 节点端进程已启动 (PID: $NEW_PID)${NC}"
|
||
echo -e "${YELLOW}⚠ 健康检查未通过,请稍后查看日志${NC}"
|
||
fi
|
||
else
|
||
echo -e "${RED}✗ 节点端启动失败${NC}"
|
||
echo -e "${YELLOW}请查看日志: cat $LOG_FILE${NC}"
|
||
rm -f "$PID_FILE"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 停止服务
|
||
stop() {
|
||
PID=$(get_pid)
|
||
|
||
# 如果没有PID文件,尝试通过端口查找
|
||
if [ -z "$PID" ]; then
|
||
if command -v lsof > /dev/null 2>&1; then
|
||
PORT_PID=$(lsof -ti :2200 2>/dev/null || echo "")
|
||
if [ -n "$PORT_PID" ]; then
|
||
PID="$PORT_PID"
|
||
echo -e "${YELLOW}通过端口找到进程 (PID: $PID)${NC}"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [ -z "$PID" ]; then
|
||
echo -e "${YELLOW}节点端未运行${NC}"
|
||
rm -f "$PID_FILE"
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${BLUE}停止节点端服务 (PID: $PID)...${NC}"
|
||
|
||
# 发送TERM信号
|
||
kill "$PID" 2>/dev/null || true
|
||
|
||
# 等待进程结束
|
||
for i in {1..10}; do
|
||
if ! ps -p "$PID" > /dev/null 2>&1; then
|
||
break
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
# 如果还在运行,强制杀死
|
||
if ps -p "$PID" > /dev/null 2>&1; then
|
||
echo -e "${YELLOW}强制停止节点端...${NC}"
|
||
kill -9 "$PID" 2>/dev/null || true
|
||
sleep 1
|
||
fi
|
||
|
||
rm -f "$PID_FILE"
|
||
echo -e "${GREEN}✓ 节点端已停止${NC}"
|
||
}
|
||
|
||
# 重启服务
|
||
restart() {
|
||
echo -e "${BLUE}重启节点端服务...${NC}"
|
||
stop
|
||
sleep 1
|
||
start
|
||
}
|
||
|
||
# 查看状态
|
||
status() {
|
||
PID=$(get_pid)
|
||
if [ -n "$PID" ]; then
|
||
echo -e "${GREEN}节点端运行中 (PID: $PID)${NC}"
|
||
|
||
# 检查健康状态
|
||
if command -v curl > /dev/null 2>&1; then
|
||
HEALTH=$(curl -s http://localhost:2200/api/health 2>/dev/null || echo "failed")
|
||
if [ "$HEALTH" = '{"status":"ok"}' ]; then
|
||
echo -e "${GREEN}✓ 健康检查: 正常${NC}"
|
||
else
|
||
echo -e "${YELLOW}⚠ 健康检查: 异常${NC}"
|
||
fi
|
||
fi
|
||
|
||
# 显示进程信息
|
||
ps -p "$PID" -o pid,ppid,cmd,%mem,%cpu,etime 2>/dev/null || true
|
||
else
|
||
echo -e "${RED}节点端未运行${NC}"
|
||
fi
|
||
}
|
||
|
||
# 查看日志
|
||
logs() {
|
||
if [ -f "$LOG_FILE" ]; then
|
||
tail -f "$LOG_FILE"
|
||
else
|
||
echo -e "${YELLOW}日志文件不存在: $LOG_FILE${NC}"
|
||
fi
|
||
}
|
||
|
||
# 查看完整日志
|
||
logs_all() {
|
||
if [ -f "$LOG_FILE" ]; then
|
||
cat "$LOG_FILE"
|
||
else
|
||
echo -e "${YELLOW}日志文件不存在: $LOG_FILE${NC}"
|
||
fi
|
||
}
|
||
|
||
# 显示帮助
|
||
help() {
|
||
echo "LinkMaster 节点端运行脚本"
|
||
echo ""
|
||
echo "使用方法:"
|
||
echo " $0 {start|stop|restart|status|logs|logs-all|help}"
|
||
echo ""
|
||
echo "命令说明:"
|
||
echo " start - 启动节点端服务(会自动拉取最新代码并编译)"
|
||
echo " stop - 停止节点端服务"
|
||
echo " restart - 重启节点端服务"
|
||
echo " status - 查看运行状态"
|
||
echo " logs - 实时查看日志"
|
||
echo " logs-all - 查看完整日志"
|
||
echo " help - 显示帮助信息"
|
||
echo ""
|
||
echo "环境变量:"
|
||
echo " BACKEND_URL - 后端服务地址 (默认: http://localhost:8080)"
|
||
echo ""
|
||
echo "示例:"
|
||
echo " BACKEND_URL=http://192.168.1.100:8080 $0 start"
|
||
echo " $0 status"
|
||
echo " $0 logs"
|
||
}
|
||
|
||
# 主逻辑
|
||
case "${1:-help}" in
|
||
start)
|
||
start
|
||
;;
|
||
stop)
|
||
stop
|
||
;;
|
||
restart)
|
||
restart
|
||
;;
|
||
status)
|
||
status
|
||
;;
|
||
logs)
|
||
logs
|
||
;;
|
||
logs-all)
|
||
logs_all
|
||
;;
|
||
help|--help|-h)
|
||
help
|
||
;;
|
||
*)
|
||
echo -e "${RED}未知命令: $1${NC}"
|
||
echo ""
|
||
help
|
||
exit 1
|
||
;;
|
||
esac
|
||
|