deploy.sh 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #!/bin/bash
  2. # ============================================================
  3. # 充电项目一键部署脚本 (2核8G 服务器)
  4. # 用法: ./deploy.sh [admin|miniapp|all] [--skip-frontend] [--no-stop]
  5. # --no-stop 构建前不停止服务(构建期间服务继续运行,但启动新进程前需手动停旧进程)
  6. # --skip-frontend 跳过前端构建(仅后端代码变更时使用)
  7. # 调试: bash -x ./deploy.sh
  8. # ============================================================
  9. APP_DIR="/data/app"
  10. CODE_DIR="/data/code/charge-java"
  11. BACKUP_DIR="/data/backup"
  12. LOG_DIR="/data/logs"
  13. HISTORY_FILE="/data/history"
  14. CERT_DIR="/data/pem"
  15. RED='\033[0;31m'
  16. GREEN='\033[0;32m'
  17. YELLOW='\033[1;33m'
  18. NC='\033[0m'
  19. log() { echo -e "[$(date '+%H:%M:%S')] $1"; }
  20. info() { log "${GREEN}INFO${NC} $1"; }
  21. warn() { log "${YELLOW}WARN${NC} $1"; }
  22. error() { log "${RED}ERROR${NC} $1"; }
  23. mkdir -p "$LOG_DIR" "$BACKUP_DIR"
  24. # ============================================================
  25. # 资源限制配置 (2核8G 服务器)
  26. # ============================================================
  27. # ---- 构建阶段 ----
  28. NODE_MAX_MEM="${NODE_MAX_MEM:-2048}" # Node 最大 old-space MB
  29. MAVEN_XMX="${MAVEN_XMX:-1024}" # Maven JVM 最大堆 MB
  30. MAVEN_XMS="${MAVEN_XMS:-256}" # Maven JVM 初始堆 MB
  31. MAVEN_THREADS="${MAVEN_THREADS:-2}" # Maven 编译线程数
  32. # ---- 运行时 JVM (admin 模块: 管理后台 + 定时任务 + POI Excel) ----
  33. ADMIN_XMX="${ADMIN_XMX:-1024}" # 最大堆 MB
  34. ADMIN_XMS="${ADMIN_XMS:-256}" # 初始堆 MB
  35. ADMIN_META="${ADMIN_META:-256}" # 元空间上限 MB
  36. ADMIN_CODE_CACHE="${ADMIN_CODE_CACHE:-128}" # JIT 代码缓存 MB
  37. ADMIN_XSS="${ADMIN_XSS:-256}" # 线程栈 KB
  38. # ---- 运行时 JVM (miniapp 模块: 小程序API) ----
  39. MINIAPP_XMX="${MINIAPP_XMX:-768}"
  40. MINIAPP_XMS="${MINIAPP_XMS:-256}"
  41. MINIAPP_META="${MINIAPP_META:-256}"
  42. MINIAPP_CODE_CACHE="${MINIAPP_CODE_CACHE:-128}"
  43. MINIAPP_XSS="${MINIAPP_XSS:-256}"
  44. # 低优先级包装器(避免构建抢占服务 CPU)
  45. low_prio() {
  46. nice -n 19 ionice -c 2 -n 7 "$@"
  47. }
  48. # ---- 参数解析 ----
  49. TARGET="${1:-all}"
  50. SKIP_FRONTEND=false
  51. NO_STOP=false
  52. for arg in "$@"; do
  53. [ "$arg" = "--skip-frontend" ] && SKIP_FRONTEND=true
  54. [ "$arg" = "--no-stop" ] && NO_STOP=true
  55. done
  56. info "开始部署 充电项目 (目标=$TARGET, 零停机=$NO_STOP)"
  57. info "Node:${NODE_MAX_MEM}M | Maven:${MAVEN_XMX}M | Admin-JVM:${ADMIN_XMX}M | Miniapp-JVM:${MINIAPP_XMX}M"
  58. # ---- 拉取代码 ----
  59. info "拉取最新代码..."
  60. cd "$CODE_DIR" || { error "无法进入 $CODE_DIR"; exit 1; }
  61. low_prio git pull origin master || { error "git pull 失败"; exit 1; }
  62. # ============================================================
  63. # 阶段 1: 停止服务 (释放内存给构建)
  64. # ============================================================
  65. stop_service() {
  66. local module=$1
  67. local pid=$(ps -ef | grep java | grep "$module" | grep -v grep | awk '{print $2}')
  68. if [ -z "$pid" ]; then
  69. info "$module 未在运行"
  70. return 0
  71. fi
  72. for p in $pid; do
  73. info "终止 $module (pid=$p)..."
  74. kill "$p" 2>/dev/null
  75. for i in $(seq 1 10); do
  76. if ! kill -0 "$p" 2>/dev/null; then
  77. info "进程 $p 已退出"
  78. break
  79. fi
  80. sleep 1
  81. done
  82. if kill -0 "$p" 2>/dev/null; then
  83. warn "强制终止 $p"
  84. kill -9 "$p" 2>/dev/null
  85. sleep 1
  86. fi
  87. done
  88. }
  89. if [ "$NO_STOP" = false ]; then
  90. case "$TARGET" in
  91. admin) stop_service "admin-0.0.1-SNAPSHOT" ;;
  92. miniapp) stop_service "miniapp-0.0.1-SNAPSHOT" ;;
  93. all) stop_service "admin-0.0.1-SNAPSHOT"
  94. stop_service "miniapp-0.0.1-SNAPSHOT" ;;
  95. esac
  96. sleep 3
  97. fi
  98. # ============================================================
  99. # 阶段 2: 构建
  100. # ============================================================
  101. if [ "$SKIP_FRONTEND" = false ] && [ "$TARGET" != "miniapp" ]; then
  102. # 管理后台前端
  103. info "构建前端 admin-web ..."
  104. (
  105. cd "$CODE_DIR/admin-web" || exit 1
  106. export NODE_OPTIONS="--max-old-space-size=$NODE_MAX_MEM"
  107. low_prio npm run build
  108. ) || { error "admin-web 构建失败"; exit 1; }
  109. info "admin-web 构建完成"
  110. # 小程序前端 (H5 端)
  111. info "构建前端 charge-front ..."
  112. (
  113. cd "$CODE_DIR/charge-front" || exit 1
  114. export NODE_OPTIONS="--max-old-space-size=$NODE_MAX_MEM"
  115. low_prio npm run build
  116. ) || { error "charge-front 构建失败"; exit 1; }
  117. info "charge-front 构建完成"
  118. fi
  119. export MAVEN_OPTS="-Xmx${MAVEN_XMX}m -Xms${MAVEN_XMS}m -XX:+UseG1GC"
  120. if [ "$TARGET" = "miniapp" ]; then
  121. info "Maven 打包 (仅 miniapp)..."
  122. (
  123. cd "$CODE_DIR"
  124. low_prio mvn clean package -DskipTests -pl miniapp -am \
  125. -T "${MAVEN_THREADS}" -Dmaven.test.skip=true
  126. ) || { error "Maven 打包失败"; exit 1; }
  127. elif [ "$TARGET" = "admin" ]; then
  128. info "Maven 打包 (仅 admin)..."
  129. (
  130. cd "$CODE_DIR"
  131. low_prio mvn clean package -DskipTests -pl admin -am \
  132. -T "${MAVEN_THREADS}" -Dmaven.test.skip=true
  133. ) || { error "Maven 打包失败"; exit 1; }
  134. else
  135. info "Maven 打包 (全量, 线程=${MAVEN_THREADS})..."
  136. (
  137. cd "$CODE_DIR"
  138. low_prio mvn clean package -DskipTests \
  139. -T "${MAVEN_THREADS}" \
  140. -Dmaven.test.skip=true \
  141. -Dmaven.compiler.fork=false
  142. ) || { error "Maven 打包失败"; exit 1; }
  143. fi
  144. sleep 2
  145. # ============================================================
  146. # 阶段 3: 备份 & 部署 & 启动
  147. # ============================================================
  148. backup_and_copy() {
  149. local module=$1
  150. local jar_src="$CODE_DIR/$module/target/${module}-0.0.1-SNAPSHOT.jar"
  151. local jar_dst="$APP_DIR/${module}-0.0.1-SNAPSHOT.jar"
  152. if [ ! -f "$jar_src" ]; then
  153. error "未找到 jar 文件: $jar_src"
  154. exit 1
  155. fi
  156. if [ -f "$jar_dst" ]; then
  157. cp "$jar_dst" "$BACKUP_DIR/${module}-0.0.1-SNAPSHOT.jar.$(date +%Y%m%d_%H%M%S)"
  158. info "已备份 $module (保留最近 5 个)"
  159. ls -t "$BACKUP_DIR/${module}"-*.jar 2>/dev/null | tail -n +6 | xargs -r rm -f
  160. fi
  161. cp "$jar_src" "$jar_dst"
  162. info "$module jar 已复制到 $APP_DIR"
  163. }
  164. # 构建模块专用的 JVM 参数
  165. build_jvm_opts() {
  166. local module=$1
  167. if [ "$module" = "admin" ]; then
  168. echo "-Xmx${ADMIN_XMX}m -Xms${ADMIN_XMS}m -Xss${ADMIN_XSS}k \
  169. -XX:MaxMetaspaceSize=${ADMIN_META}m -XX:ReservedCodeCacheSize=${ADMIN_CODE_CACHE}m \
  170. -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication \
  171. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR}/ \
  172. -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
  173. else
  174. echo "-Xmx${MINIAPP_XMX}m -Xms${MINIAPP_XMS}m -Xss${MINIAPP_XSS}k \
  175. -XX:MaxMetaspaceSize=${MINIAPP_META}m -XX:ReservedCodeCacheSize=${MINIAPP_CODE_CACHE}m \
  176. -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication \
  177. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOG_DIR}/ \
  178. -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
  179. fi
  180. }
  181. start_service() {
  182. local module=$1
  183. local jar_file="${module}-0.0.1-SNAPSHOT.jar"
  184. local start_log="$LOG_DIR/${module}.log"
  185. local jvm_opts=$(build_jvm_opts "$module")
  186. info "启动 $module (JVM: $(echo $jvm_opts | tr -s ' '))..."
  187. # admin 模块需要指定微信支付证书路径
  188. local cert_arg=""
  189. if [ "$module" = "admin" ]; then
  190. cert_arg="-Dwechat.payment.certPath=${CERT_DIR}/apiclient_cert.pem -Dwechat.payment.keyPath=${CERT_DIR}/apiclient_key.pem"
  191. fi
  192. cd "$APP_DIR"
  193. nohup java $jvm_opts -jar -Dspring.profiles.active=prod $cert_arg "$jar_file" > "$start_log" 2>&1 &
  194. sleep 5
  195. local new_pid=$(ps -ef | grep java | grep "$module" | grep -v grep | awk '{print $2}')
  196. if [ -n "$new_pid" ]; then
  197. info "$module 启动成功 (pid=$new_pid)"
  198. echo "$(date) $module restarted" >> "$HISTORY_FILE"
  199. else
  200. error "$module 可能启动失败,查看日志:"
  201. echo "--- $start_log ---"
  202. tail -30 "$start_log"
  203. echo "--- end ---"
  204. exit 1
  205. fi
  206. }
  207. case "$TARGET" in
  208. admin)
  209. backup_and_copy "admin"
  210. start_service "admin"
  211. ;;
  212. miniapp)
  213. backup_and_copy "miniapp"
  214. start_service "miniapp"
  215. ;;
  216. all)
  217. backup_and_copy "admin"
  218. backup_and_copy "miniapp"
  219. start_service "miniapp"
  220. start_service "admin"
  221. ;;
  222. *)
  223. error "未知模块: $TARGET (可选: admin | miniapp | all)"
  224. exit 1
  225. ;;
  226. esac
  227. echo "========================================"
  228. info "充电项目 部署完成"
  229. echo "========================================"