| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- #!/bin/bash
- # ============================================================
- # 充电项目一键部署脚本 (2核8G 服务器)
- # 用法: ./deploy.sh [admin|miniapp|all] [--skip-frontend] [--no-stop]
- # --no-stop 构建前不停止服务(构建期间服务继续运行,但启动新进程前需手动停旧进程)
- # --skip-frontend 跳过前端构建(仅后端代码变更时使用)
- # 调试: bash -x ./deploy.sh
- # ============================================================
- APP_DIR="/data/app"
- CODE_DIR="/data/code/charge-java"
- BACKUP_DIR="/data/backup"
- LOG_DIR="/data/logs"
- HISTORY_FILE="/data/history"
- CERT_DIR="/data/pem"
- # 构建工具路径(对齐 Jenkins 环境)
- NPM="${NPM:-/usr/local/bin/npm}"
- MVN="${MVN:-mvn}"
- # 将 npm 全局 bin 加入 PATH(确保 uni 等全局包可被找到)
- NPM_PREFIX=$($NPM config get prefix 2>/dev/null || echo "/usr/local")
- export PATH="$NPM_PREFIX/bin:$PATH"
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- NC='\033[0m'
- log() { echo -e "[$(date '+%H:%M:%S')] $1"; }
- info() { log "${GREEN}INFO${NC} $1"; }
- warn() { log "${YELLOW}WARN${NC} $1"; }
- error() { log "${RED}ERROR${NC} $1"; }
- mkdir -p "$LOG_DIR" "$BACKUP_DIR"
- # ============================================================
- # 资源限制配置 (2核8G 服务器)
- # ============================================================
- # ---- 构建阶段 ----
- NODE_MAX_MEM="${NODE_MAX_MEM:-2048}" # Node 最大 old-space MB
- MAVEN_XMX="${MAVEN_XMX:-1024}" # Maven JVM 最大堆 MB
- MAVEN_XMS="${MAVEN_XMS:-256}" # Maven JVM 初始堆 MB
- MAVEN_THREADS="${MAVEN_THREADS:-2}" # Maven 编译线程数
- # ---- 运行时 JVM (admin 模块: 管理后台 + 定时任务 + POI Excel) ----
- ADMIN_XMX="${ADMIN_XMX:-1024}" # 最大堆 MB
- ADMIN_XMS="${ADMIN_XMS:-256}" # 初始堆 MB
- ADMIN_META="${ADMIN_META:-256}" # 元空间上限 MB
- ADMIN_CODE_CACHE="${ADMIN_CODE_CACHE:-128}" # JIT 代码缓存 MB
- ADMIN_XSS="${ADMIN_XSS:-256}" # 线程栈 KB
- # ---- 运行时 JVM (miniapp 模块: 小程序API) ----
- MINIAPP_XMX="${MINIAPP_XMX:-768}"
- MINIAPP_XMS="${MINIAPP_XMS:-256}"
- MINIAPP_META="${MINIAPP_META:-256}"
- MINIAPP_CODE_CACHE="${MINIAPP_CODE_CACHE:-128}"
- MINIAPP_XSS="${MINIAPP_XSS:-256}"
- # 低优先级包装器(避免构建抢占服务 CPU)
- low_prio() {
- nice -n 19 ionice -c 2 -n 7 "$@"
- }
- # ---- 参数解析 ----
- TARGET="${1:-all}"
- SKIP_FRONTEND=false
- NO_STOP=false
- for arg in "$@"; do
- [ "$arg" = "--skip-frontend" ] && SKIP_FRONTEND=true
- [ "$arg" = "--no-stop" ] && NO_STOP=true
- done
- info "开始部署 充电项目 (目标=$TARGET, 零停机=$NO_STOP)"
- info "Maven:$MVN | NPM:$NPM | Node:${NODE_MAX_MEM}M | Admin-JVM:${ADMIN_XMX}M | Miniapp-JVM:${MINIAPP_XMX}M"
- # ---- 拉取代码 ----
- info "拉取最新代码..."
- cd "$CODE_DIR" || { error "无法进入 $CODE_DIR"; exit 1; }
- low_prio git pull origin master || { error "git pull 失败"; exit 1; }
- # ============================================================
- # 阶段 1: 停止服务 (释放内存给构建)
- # ============================================================
- stop_service() {
- local module=$1
- local pid=$(ps -ef | grep java | grep "$module" | grep -v grep | awk '{print $2}')
- if [ -z "$pid" ]; then
- info "$module 未在运行"
- return 0
- fi
- for p in $pid; do
- info "终止 $module (pid=$p)..."
- kill "$p" 2>/dev/null
- for i in $(seq 1 10); do
- if ! kill -0 "$p" 2>/dev/null; then
- info "进程 $p 已退出"
- break
- fi
- sleep 1
- done
- if kill -0 "$p" 2>/dev/null; then
- warn "强制终止 $p"
- kill -9 "$p" 2>/dev/null
- sleep 1
- fi
- done
- }
- if [ "$NO_STOP" = false ]; then
- case "$TARGET" in
- admin) stop_service "admin-0.0.1-SNAPSHOT" ;;
- miniapp) stop_service "miniapp-0.0.1-SNAPSHOT" ;;
- all) stop_service "admin-0.0.1-SNAPSHOT"
- stop_service "miniapp-0.0.1-SNAPSHOT" ;;
- esac
- sleep 3
- fi
- # ============================================================
- # 阶段 2: 安装公共模块到本地仓库 (对齐 Jenkins mvn install 流程)
- # ============================================================
- info "安装公共模块到本地仓库..."
- (
- cd "$CODE_DIR"
- low_prio $MVN install -DskipTests -Dmaven.test.skip=true
- ) || { error "Maven install 失败"; exit 1; }
- # ============================================================
- # 阶段 3: 前端构建
- # ============================================================
- if [ "$SKIP_FRONTEND" = false ] && [ "$TARGET" != "miniapp" ]; then
- # ---- 管理后台前端 (admin-web) ----
- info "构建前端 admin-web ..."
- # 清理旧前端产物,避免残留文件打入 jar
- rm -rf "$CODE_DIR/admin/src/main/resources/static/"
- mkdir -p "$CODE_DIR/admin/src/main/resources/static/"
- (
- cd "$CODE_DIR/admin-web" || exit 1
- export NODE_OPTIONS="--max-old-space-size=$NODE_MAX_MEM"
- $NPM install
- low_prio $NPM run build
- ) || { error "admin-web 构建失败"; exit 1; }
- info "admin-web 构建完成"
- # ---- 小程序前端 (charge-front, uni-app) ----
- info "构建前端 charge-front ..."
- (
- # uni-app CLI 依赖,首次部署需全局安装
- if ! command -v uni >/dev/null 2>&1; then
- info "安装 @dcloudio/uvm ..."
- $NPM install -g @dcloudio/uvm
- fi
- cd "$CODE_DIR/charge-front" || exit 1
- export NODE_OPTIONS="--max-old-space-size=$NODE_MAX_MEM"
- $NPM install
- low_prio $NPM run build
- ) || { error "charge-front 构建失败"; exit 1; }
- info "charge-front 构建完成"
- fi
- # ============================================================
- # 阶段 4: Maven 打包
- # ============================================================
- export MAVEN_OPTS="-Xmx${MAVEN_XMX}m -Xms${MAVEN_XMS}m -XX:+UseG1GC"
- if [ "$TARGET" = "miniapp" ]; then
- info "Maven 打包 (仅 miniapp)..."
- (
- cd "$CODE_DIR/miniapp"
- low_prio $MVN clean package -DskipTests -Dmaven.test.skip=true
- ) || { error "Maven 打包失败"; exit 1; }
- elif [ "$TARGET" = "admin" ]; then
- info "Maven 打包 (仅 admin)..."
- (
- cd "$CODE_DIR/admin"
- low_prio $MVN clean package -DskipTests -Dmaven.test.skip=true
- ) || { error "Maven 打包失败"; exit 1; }
- else
- info "Maven 打包 (全量, 线程=${MAVEN_THREADS})..."
- (
- cd "$CODE_DIR"
- low_prio $MVN clean package -DskipTests \
- -T "${MAVEN_THREADS}" \
- -Dmaven.test.skip=true \
- -Dmaven.compiler.fork=false
- ) || { error "Maven 打包失败"; exit 1; }
- fi
- sleep 2
- # ============================================================
- # 阶段 5: 备份 & 部署 & 启动
- # ============================================================
- backup_and_copy() {
- local module=$1
- local jar_src="$CODE_DIR/$module/target/${module}-0.0.1-SNAPSHOT.jar"
- local jar_dst="$APP_DIR/${module}-0.0.1-SNAPSHOT.jar"
- if [ ! -f "$jar_src" ]; then
- error "未找到 jar 文件: $jar_src"
- exit 1
- fi
- if [ -f "$jar_dst" ]; then
- cp "$jar_dst" "$BACKUP_DIR/${module}-0.0.1-SNAPSHOT.jar.$(date +%Y%m%d_%H%M%S)"
- info "已备份 $module (保留最近 5 个)"
- ls -t "$BACKUP_DIR/${module}"-*.jar 2>/dev/null | tail -n +6 | xargs -r rm -f
- fi
- cp "$jar_src" "$jar_dst"
- info "$module jar 已复制到 $APP_DIR"
- }
- # 构建模块专用的 JVM 参数
- build_jvm_opts() {
- local module=$1
- if [ "$module" = "admin" ]; then
- echo "-Xmx${ADMIN_XMX}m -Xms${ADMIN_XMS}m -Xss${ADMIN_XSS}k \
- -XX:MaxMetaspaceSize=${ADMIN_META}m -XX:ReservedCodeCacheSize=${ADMIN_CODE_CACHE}m \
- -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication \
- -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR}/ \
- -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
- else
- echo "-Xmx${MINIAPP_XMX}m -Xms${MINIAPP_XMS}m -Xss${MINIAPP_XSS}k \
- -XX:MaxMetaspaceSize=${MINIAPP_META}m -XX:ReservedCodeCacheSize=${MINIAPP_CODE_CACHE}m \
- -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication \
- -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR}/ \
- -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
- fi
- }
- start_service() {
- local module=$1
- local jar_file="${module}-0.0.1-SNAPSHOT.jar"
- local start_log="$LOG_DIR/${module}.log"
- local jvm_opts=$(build_jvm_opts "$module")
- info "启动 $module (JVM: $(echo $jvm_opts | tr -s ' '))..."
- # admin 模块需要指定微信支付证书路径
- local cert_arg=""
- if [ "$module" = "admin" ]; then
- cert_arg="-Dwechat.payment.certPath=${CERT_DIR}/apiclient_cert.pem -Dwechat.payment.keyPath=${CERT_DIR}/apiclient_key.pem"
- fi
- cd "$APP_DIR"
- nohup java $jvm_opts -jar -Dspring.profiles.active=prod $cert_arg "$jar_file" > "$start_log" 2>&1 &
- sleep 5
- local new_pid=$(ps -ef | grep java | grep "$module" | grep -v grep | awk '{print $2}')
- if [ -n "$new_pid" ]; then
- info "$module 启动成功 (pid=$new_pid)"
- echo "$(date) $module restarted" >> "$HISTORY_FILE"
- else
- error "$module 可能启动失败,查看日志:"
- echo "--- $start_log ---"
- tail -30 "$start_log"
- echo "--- end ---"
- exit 1
- fi
- }
- case "$TARGET" in
- admin)
- backup_and_copy "admin"
- start_service "admin"
- ;;
- miniapp)
- backup_and_copy "miniapp"
- start_service "miniapp"
- ;;
- all)
- backup_and_copy "admin"
- backup_and_copy "miniapp"
- start_service "miniapp"
- start_service "admin"
- ;;
- *)
- error "未知模块: $TARGET (可选: admin | miniapp | all)"
- exit 1
- ;;
- esac
- echo "========================================"
- info "充电项目 部署完成"
- echo "========================================"
|