Files
starRiverProperty/maven-cw-elevator-application/scripts/run_v1v2_parity_automated.sh
T
反编译工作区 ee9c0aa26d fix: relocate cwos-portal decompiled output to correct path; remove nested directory
Former-commit-id: 7a35ba61e92ede5d81f132d969713dabe7a27194
2026-04-29 12:11:22 +08:00

226 lines
9.6 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 一键:构建 V2 → sync-jars → 后台启动 V1/V2(默认同 deploy 配置、异端口)→ 健康等待 → 跑完整 API 套件。
#
# 【能力边界|审阅说明】
# - 会做:按 api_catalog.json 对 V1/V2 **顺序**发 HTTP(冒烟 + 双端对拍),比对返回(compare_modecode_only / deep / status_only),
# 产出 report/smoke-*.md、parity-*.md、SUITE-*.mdstrict 模式下不一致则 pytest 失败、脚本非 0。
# - 不会做:**并发压测**(无固定 RPS、并发连接数、持续时间、p95 延迟统计);若需压测请另用 hey/wrk/k6 等指向同一 ELEVATOR_BASE_OLD/NEW。
#
# 用法(在 maven-cw-elevator-application 目录):
# ./scripts/run_v1v2_parity_automated.sh
#
# 环境变量(节选):
# ELEVATOR_SKIP_BUILD=1 跳过 mvn package(已有 target JAR
# ELEVATOR_SKIP_SYNC=1 跳过 deploy/sync-jars.shdeploy 下已有 JAR
# ELEVATOR_SKIP_START=1 不启动进程,仅跑套件(需已监听 ELEVATOR_BASE_OLD/NEW
# ELEVATOR_SUITE_STRICT=1 默认值;smoke/parity 失败则脚本非 0(传给 run_full_elevator_api_suite.sh
# ELEVATOR_SUITE_STRICT=0 smoke/parity 与旧版一致用 || true,便于只看报告
# ELEVATOR_PARITY_BOUNDARY=0 对拍仅基线体,不合并 boundary_patches(默认本脚本为 1
# ELEVATOR_PORT_V1=18080 ELEVATOR_PORT_V2=18081
# ELEVATOR_STARTUP_TIMEOUT=240 单端就绪最长等待秒数
# ELEVATOR_AUTO_KILL=1 默认退出时结束本脚本启动的 java 子进程
# ELEVATOR_JAVA_OPTS 传给两边的 JVM(如 -Xmx512m
# ELEVATOR_USE_ENV_JAVA=1 使用当前 JAVA_HOME(见 deploy/common-java.sh
# ELEVATOR_PARITY_OFFLINE=1 默认:禁用 Consul(笔记本无 8500 / 避免误连 JAR 内嵌机房地址)。
# ELEVATOR_PARITY_OFFLINE=0 使用 deploy/*/bootstrap.properties 中的 Consul(默认 host 192.168.3.12:8500,与该机 Docker consul 一致)。
#
set -euo pipefail
REPO="$(cd "$(dirname "$0")/.." && pwd)"
DEPLOY="${REPO}/deploy"
PARITY_OFFLINE_PROPS="${DEPLOY}/parity-offline-overrides.properties"
TOOL="${REPO}/tools/elevator_api_parity"
SCRIPTS="${REPO}/scripts"
# shellcheck source=../deploy/common-java.sh
source "${DEPLOY}/common-java.sh"
MERGE="${DEPLOY}/merge-redis-json.sh"
PORT_V1="${ELEVATOR_PORT_V1:-18080}"
PORT_V2="${ELEVATOR_PORT_V2:-18081}"
BASE_V1="${ELEVATOR_BASE_OLD:-http://127.0.0.1:${PORT_V1}}"
BASE_V2="${ELEVATOR_BASE_NEW:-http://127.0.0.1:${PORT_V2}}"
START_TIMEOUT="${ELEVATOR_STARTUP_TIMEOUT:-240}"
AUTO_KILL="${ELEVATOR_AUTO_KILL:-1}"
export ELEVATOR_BASE_OLD="$BASE_V1"
export ELEVATOR_BASE_NEW="$BASE_V2"
export ELEVATOR_SUITE_STRICT="${ELEVATOR_SUITE_STRICT:-1}"
# 对拍时展开 api_catalog 中 boundary_patches(入参边界);设为 0 仅跑基线 default 体以缩短耗时
export ELEVATOR_PARITY_BOUNDARY="${ELEVATOR_PARITY_BOUNDARY:-1}"
PID_DIR=""
STARTED_V1=""
STARTED_V2=""
cleanup() {
if [[ "${AUTO_KILL}" != "1" ]] || [[ -z "${PID_DIR}" ]] || [[ ! -d "${PID_DIR}" ]]; then
return 0
fi
for key in v1 v2; do
f="${PID_DIR}/${key}.pid"
if [[ -f "$f" ]]; then
pid="$(cat "$f" 2>/dev/null || true)"
if [[ -n "${pid}" ]] && kill -0 "$pid" 2>/dev/null; then
echo "==> 停止 ${key} (pid ${pid})"
kill "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
fi
fi
done
rm -rf "${PID_DIR}"
}
trap cleanup EXIT INT TERM
_port_in_use() {
local port="$1"
if command -v nc >/dev/null 2>&1; then
nc -z 127.0.0.1 "$port" 2>/dev/null
return $?
fi
timeout 0.3 bash -c "echo >/dev/tcp/127.0.0.1/${port}" 2>/dev/null
}
_wait_healthy() {
local base="$1"
local label="$2"
local pid_file="$3"
local log_file="$4"
local end=$(( $(date +%s) + START_TIMEOUT ))
echo "==> 等待就绪 ${label} (${base}),最长 ${START_TIMEOUT}s"
while (( $(date +%s) < end )); do
if [[ -f "${pid_file}" ]]; then
local pid=""
pid="$(cat "${pid_file}" 2>/dev/null || true)"
if [[ -n "${pid}" ]] && ! kill -0 "${pid}" 2>/dev/null; then
echo "ERROR: ${label} 进程已退出(pid=${pid}),无需继续等待健康检查。" >&2
echo " 请检查日志: ${log_file}" >&2
return 1
fi
fi
for path in /actuator/health /health; do
code="$(curl -s -o /tmp/elev_h$$ -w '%{http_code}' "${base}${path}" 2>/dev/null || echo 0)"
if [[ "${code}" == "200" ]] && grep -qiE 'up|"status"[[:space:]]*:[[:space:]]*"OK"' /tmp/elev_h$$ 2>/dev/null; then
rm -f /tmp/elev_h$$
echo " OK ${label} ${path}"
return 0
fi
done
sleep 2
done
rm -f /tmp/elev_h$$
echo "ERROR: ${label}${START_TIMEOUT}s 内未通过健康检查: ${base}" >&2
return 1
}
_pick_java_home
if [[ ! -x "${JAVA_HOME}/bin/java" ]]; then
echo "ERROR: 未找到 JDK。请安装 openjdk-8-jdk 或设置 JAVA_HOME / ELEVATOR_USE_ENV_JAVA=1" >&2
exit 1
fi
JAVA="${JAVA_HOME}/bin/java"
OPEN_FLAGS=()
while IFS= read -r line; do
[[ -n "${line}" ]] && OPEN_FLAGS+=("${line}")
done < <(_jdk8_open_flags "$JAVA")
if [[ ! -x "${MERGE}" ]]; then
chmod +x "${MERGE}" 2>/dev/null || true
fi
if [[ "${ELEVATOR_SKIP_BUILD:-0}" != "1" ]]; then
OLD_TARGET_JAR="${REPO}/cw-elevator-application-starter/target/cw-elevator-application-2.0.0.jar"
OLD_DEPLOY_JAR="${DEPLOY}/v2-maven/cw-elevator-application-2.0.0.jar"
echo "==> 删除旧 V2 JAR(确保全量重编译产物)"
rm -f "${OLD_TARGET_JAR}" "${OLD_DEPLOY_JAR}"
echo "==> mvn package (skip tests)"
(cd "${REPO}" && mvn -q -DskipTests clean package)
else
echo "==> 跳过构建 (ELEVATOR_SKIP_BUILD=1)"
fi
if [[ "${ELEVATOR_SKIP_SYNC:-0}" != "1" ]]; then
echo "==> sync-jarsV1 来自仓库根 cw-elevator-application-V1.0.0.20211103/"
# 默认用当前 Maven data 模块覆盖 V1 fat-jar 内嵌 data JAR,避免历史包与源码分片实现分叉导致 record_page 等对拍假差异;纯正历史 V1 请 ELEVATOR_SKIP_SYNC=1 或 PARITY_PATCH_V1_DATA=0
export PARITY_PATCH_V1_DATA="${PARITY_PATCH_V1_DATA:-1}"
bash "${DEPLOY}/sync-jars.sh"
else
echo "==> 跳过 sync-jars (ELEVATOR_SKIP_SYNC=1)"
fi
V1_DIR="${DEPLOY}/v1-legacy"
V2_DIR="${DEPLOY}/v2-maven"
JAR_V1="${V1_DIR}/cw-elevator-application-V1.0.0.20211103.jar"
JAR_V2="${V2_DIR}/cw-elevator-application-2.0.0.jar"
if [[ ! -f "${JAR_V1}" ]] || [[ ! -f "${JAR_V2}" ]]; then
echo "ERROR: 缺少 JAR。请执行 ${DEPLOY}/sync-jars.sh 或先 mvn package。" >&2
echo " 期望: ${JAR_V1}" >&2
echo " ${JAR_V2}" >&2
exit 1
fi
LOG_DIR="${REPO}/.elevator-parity-logs"
mkdir -p "${LOG_DIR}"
if [[ "${ELEVATOR_SKIP_START:-0}" != "1" ]]; then
if _port_in_use "${PORT_V1}" && [[ "${ELEVATOR_ALLOW_BUSY_PORTS:-0}" != "1" ]]; then
echo "ERROR: 端口 ${PORT_V1} 已被占用。可先停占用进程,或设 ELEVATOR_SKIP_START=1 使用已启动实例。" >&2
exit 1
fi
if _port_in_use "${PORT_V2}" && [[ "${ELEVATOR_ALLOW_BUSY_PORTS:-0}" != "1" ]]; then
echo "ERROR: 端口 ${PORT_V2} 已被占用。" >&2
exit 1
fi
PID_DIR="$(mktemp -d "${REPO}/.elevator-parity-pids.XXXXXX")"
# Consul:若脚本未加载 bootstrap、且无本地 ConsulJAR 内嵌地址会导致 agentServiceRegister 失败→整进程退出(日志见 TransportException 8500)。
OFF_JVM=()
LOC_CHAIN_V1="file:${V1_DIR}/bootstrap.properties,file:${V1_DIR}/application.properties,file:${V1_DIR}/application-access-control.properties,file:${V1_DIR}/redis-override.properties"
LOC_CHAIN_V2="file:${V2_DIR}/bootstrap.properties,file:${V2_DIR}/application.properties,file:${V2_DIR}/application-access-control.properties,file:${V2_DIR}/redis-override.properties"
if [[ "${ELEVATOR_PARITY_OFFLINE:-1}" == "1" ]] && [[ -f "${PARITY_OFFLINE_PROPS}" ]]; then
LOC_CHAIN_V1+=",file:${PARITY_OFFLINE_PROPS}"
LOC_CHAIN_V2+=",file:${PARITY_OFFLINE_PROPS}"
# 覆盖 Spring Cloud 先于 spring.config 解析的 classpath bootstrap,避免仍指向机房 Consul
OFF_JVM=("-Dspring.cloud.consul.enabled=false")
echo "==> Consul: 离线模式(ELEVATOR_PARITY_OFFLINE=1,禁用注册)。若在本机 192.168.3.12 已有 Docker Consul,可改用 ELEVATOR_PARITY_OFFLINE=0"
else
echo "==> Consul: 联机模式(ELEVATOR_PARITY_OFFLINE=0,按 bootstrap 连接 Consuldeploy 默认 192.168.3.12:8500"
fi
echo "==> 启动 V1 :${PORT_V1}"
export SPRING_APPLICATION_JSON="$("${MERGE}" "${V1_DIR}/redis-override.properties")"
# shellcheck disable=SC2086
nohup "${JAVA}" "${OPEN_FLAGS[@]}" "${OFF_JVM[@]}" ${ELEVATOR_JAVA_OPTS:-} -jar "${JAR_V1}" \
--spring.config.location="${LOC_CHAIN_V1}" \
--server.port="${PORT_V1}" >>"${LOG_DIR}/v1-legacy.log" 2>&1 &
echo $! > "${PID_DIR}/v1.pid"
STARTED_V1=1
echo "==> 启动 V2 :${PORT_V2}"
export SPRING_APPLICATION_JSON="$("${MERGE}" "${V2_DIR}/redis-override.properties")"
# shellcheck disable=SC2086
nohup "${JAVA}" "${OPEN_FLAGS[@]}" "${OFF_JVM[@]}" ${ELEVATOR_JAVA_OPTS:-} -jar "${JAR_V2}" \
--spring.config.location="${LOC_CHAIN_V2}" \
--server.port="${PORT_V2}" >>"${LOG_DIR}/v2-maven.log" 2>&1 &
echo $! > "${PID_DIR}/v2.pid"
STARTED_V2=1
unset SPRING_APPLICATION_JSON
echo " V1 pid=$(cat "${PID_DIR}/v1.pid") log=${LOG_DIR}/v1-legacy.log"
echo " V2 pid=$(cat "${PID_DIR}/v2.pid") log=${LOG_DIR}/v2-maven.log"
_wait_healthy "${BASE_V1}" "V1" "${PID_DIR}/v1.pid" "${LOG_DIR}/v1-legacy.log" || exit 1
_wait_healthy "${BASE_V2}" "V2" "${PID_DIR}/v2.pid" "${LOG_DIR}/v2-maven.log" || exit 1
else
echo "==> 跳过启动 (ELEVATOR_SKIP_START=1),假定 ${BASE_V1} / ${BASE_V2} 已可用"
fi
echo "==> 完整 API 套件(ELEVATOR_SUITE_STRICT=${ELEVATOR_SUITE_STRICT}"
bash "${SCRIPTS}/run_full_elevator_api_suite.sh"
SUITE_EXIT=$?
exit "${SUITE_EXIT}"