1182 行 Shell | 16 个微服务模块 | 4 套验证脚本 | Docker + 本地双模部署 | AI Skill 自动化演示

你有没有遇到过这样的场景:克隆了一个微服务项目,花了半天配环境、启服务、手动 curl 验证,结果还有几个模块跑不起来?

或者更糟——你精心写了一个 demo 项目想给团队演示,结果现场翻车,端口冲突、中间件没启动、依赖顺序搞错,尴尬收场。

微服务示例项目的核心痛点不是代码写不好,而是"跑起来"太难。

本文以一个覆盖 16 个模块的 Spring Cloud Alibaba 全栈示例项目为例,深入拆解其脚本体系的设计思路——从一键启停、环境自愈、分层验证到 AI 自动化演示,看它是如何用不到 3000 行 Shell 脚本,搞定一个复杂微服务项目的全生命周期管理。


🎯 为什么脚本设计如此重要?

痛点一:微服务项目的"启动成本"被严重低估

一个典型的 Spring Cloud 项目包含:

  • 注册中心(Nacos)
  • 配置中心(Nacos Config)
  • 网关(Gateway)
  • 多个 Provider / Consumer
  • 消息中间件(RocketMQ / Kafka)
  • 分布式事务(Seata)
  • 数据库(MySQL / PostgreSQL)
  • AI 模块(需要 API Key + 向量数据库)

手动启动这些服务?你需要记住每个模块的端口、启动顺序、依赖关系、中间件地址。任何一个环节出错,整个链路就跑不通。

痛点二:验证流程重复且易出错

启服务只是第一步,验证才是关键。你需要:

  • 检查服务注册是否成功
  • 验证多协议调用链路(HTTP / Dubbo / gRPC)
  • 确认 Trace ID 跨服务传播
  • 测试分布式事务的回滚和提交
  • 验证消息收发的完整性

这些验证步骤如果靠手动 curl,不仅繁琐,而且容易遗漏。

痛点三:演示场景的"仪式感"太重

给团队演示微服务项目时,你不可能现场敲 20 个 curl 命令。你需要一个一键到底的体验:从环境检查到服务启动,从基础验证到深度演示,全程自动化。


🚀 项目脚本体系全景

这个项目的脚本设计分为 5 层,每层解决一个核心问题:

┌─────────────────────────────────────────────┐
│           AI Skill 自动化演示层              │  ← SKILL.md (495 行)
│     "告诉 AI 演示项目" → 全流程自动执行       │
├─────────────────────────────────────────────┤
│           场景化验证脚本层                    │  ← verify-*.sh (4 套)
│     Trace / Seata / Stream / Kafka           │
├─────────────────────────────────────────────┤
│           一键启停主控层                      │  ← start-all.sh (1182 行)
│     start / stop / install / verify / status  │
├─────────────────────────────────────────────┤
│           Docker 容器化部署层                 │  ← docker-compose.yml + docker-build.sh
│     profile 分组 / 宿主机中间件桥接           │
├─────────────────────────────────────────────┤
│           基础设施管理层                      │  ← kafka.sh / RocketMQ / Nacos 自动安装
│     中间件检测 / 自动下载 / 集群编排           │
└─────────────────────────────────────────────┘

接下来逐层拆解。


🏗️ 第一层:基础设施管理——中间件"零配置"

设计原则:检测优先,自动兜底,禁止阻塞

微服务依赖大量中间件,但用户的环境千差万别。脚本的第一个设计决策就是:永远不要因为中间件没就绪而卡住整个流程。

start-all.sh 中的 Nacos 检测逻辑为例:

check_nacos() {
  printf '[Nacos] 检查注册中心 (%s:%s) ...' "$NACOS_HOST" "$NACOS_PORT"
  if curl -s -o /dev/null -w '' "http://$NACOS_HOST:$NACOS_PORT/nacos/actuator/health" 2>/dev/null; then
    echo " 就绪"
    return 0
  fi
  echo " 未运行"
  # 自动搜索本地安装目录
  local nacos_dir
  nacos_dir=$(find "$HOME/nacos" "$HOME"/nacos-* "$HOME/ai-infra/nacos" \
    -maxdepth 4 -name 'startup.sh' -path '*/bin/*' 2>/dev/null | head -1)
  if [ -z "$nacos_dir" ]; then
    echo "[Nacos] ✗ 未找到安装目录"
    echo "[Nacos] 请先安装: curl -fsSL https://nacos.io/nacos-installer.sh | bash"
    exit 1
  fi
  # 自动启动
  nacos_dir=$(dirname "$(dirname "$nacos_dir")")
  printf '[Nacos] 正在自动启动...'
  bash "$nacos_dir/bin/startup.sh" -m standalone > /dev/null 2>&1
  # 轮询等待就绪
  if wait_nacos_ready; then
    return 0
  fi
  echo " 失败!"
  exit 1
}

这段代码体现了三个设计原则:

  1. 健康检查先行:先 curl actuator 端点,已运行就直接跳过
  2. 智能搜索:在 $HOME/nacos$HOME/nacos-*$HOME/ai-infra/nacos 三个常见路径搜索,适配不同用户的安装习惯
  3. 自动启动 + 轮询等待:找到安装目录后自动启动,并用 wait_nacos_ready 函数轮询最多 30 次(每次 2 秒),确保 Nacos 真正就绪后才继续

install_all 命令:一键安装全部中间件

对于完全没有中间件的环境,脚本提供了 install 命令,自动下载并安装所有依赖:

sh start-all.sh install

这个命令会依次处理:

  • Nacos:检测 → 配置免密模式 → 启动
  • RocketMQ 5.5.0:从 Apache 镜像下载 zip 包 → 解压
  • MySQL:通过 Homebrew 安装 → 设置密码 → 初始化数据库
  • Seata Server:从 GitHub 下载源码 → Maven 构建
  • Kafka 4.3.1:下载 tgz → 解压
  • PostgreSQL 18 + pgvector:Homebrew 安装 → 启动服务

每个中间件的安装都遵循幂等设计:已安装则跳过,未安装则自动下载。多次执行不会产生副作用。

Kafka 集群管理:3 节点 KRaft 编排

Kafka 模块的管理脚本 kafka.sh 是一个独立的亮点。它管理的是一个 3 节点 KRaft 模式集群(无 ZooKeeper),包含完整的生命周期:

bash kafka.sh start    # 检测/解压 → 创建配置 → 格式化 → 启动 3 节点
bash kafka.sh stop     # 停止集群
bash kafka.sh status   # 查看 3 个 Broker 状态
bash kafka.sh topics   # 列出所有 Topic
bash kafka.sh clean    # 停止 + 清理存储

start 命令内部实现了 5 步流程:

do_start() {
  # Step 1: 查找 Kafka 安装(支持 $HOME、$HOME/Downloads、压缩包自动解压)
  kafka_home=$(find_kafka_home)

  # Step 2: 检查/创建 3 份 KRaft 配置文件
  if [ "$config_ok" = true ]; then
    info "配置文件已存在,跳过创建"
  else
    create_configs "$kafka_home"  # 生成 server-{1,2,3}.properties
  fi

  # Step 3: 格式化 KRaft 存储(幂等:已格式化则跳过)
  if [ "$need_format" = true ]; then
    format_storage "$kafka_home"
  fi

  # Step 4: 依次启动 3 个 Broker
  "$kafka_home/bin/kafka-server-start.sh" -daemon "$kafka_home/config/server-1.properties"
  "$kafka_home/bin/kafka-server-start.sh" -daemon "$kafka_home/config/server-2.properties"
  "$kafka_home/bin/kafka-server-start.sh" -daemon "$kafka_home/config/server-3.properties"

  # Step 5: 轮询等待 3 个 Broker 全部就绪
  while [ $waited -lt 60 ]; do
    running=$(check_brokers)  # nc -z 检测 9092/9094/9096
    if [ "$running" -eq 3 ]; then
      info "Kafka 3 节点集群启动成功!"
      return 0
    fi
    sleep 2
  done
}

每个节点的配置文件包含了 Share Groups 特性支持(share.group.protocol=share),这是 Kafka 4.x 的新特性。脚本自动处理了集群编排的细节,用户无需手动配置。


🏗️ 第二层:Docker 容器化部署——Profile 分组 + 宿主机桥接

架构设计:中间件本地,微服务容器化

这个项目的 Docker 方案不是"全部扔进容器",而是一个混合架构

Mac 宿主机
├── 本地中间件: Nacos(8848) / RocketMQ(9876) / MySQL(3306) / PostgreSQL(5432)
│
└── Docker 容器 (通过 host.docker.internal 连宿主机)
    ├── 核心微服务 (9个): gateway / consumer / provider / grpc-server ...
    ├── Stream 消息         (profile: stream)
    ├── Spring AI           (profile: ai)
    └── Seata 分布式事务   (profile: seata)

为什么这样设计?因为中间件(尤其是 Kafka 3 节点集群、Seata Server)容器化后配置复杂,而微服务本身是无状态的,天然适合容器。

Docker Compose Profile 分组

docker-compose.yml 使用 Docker Compose 的 Profile 机制 实现按需启动:

# 核心微服务 (9个)
nacos-discovery:
  profiles: [core, all]
  environment:
    NACOS_SERVER_ADDR: host.docker.internal:8848

# Stream 消息
stream:
  profiles: [stream, all]
  environment:
    ROCKETMQ_NAME_SERVER: host.docker.internal:9876

# Seata 分布式事务 (7个)
seata-business:
  profiles: [seata, all]
  depends_on:
    - seata-storage-dubbo
    - seata-order-dubbo

用户可以通过 Profile 精确控制启动范围:

docker compose --profile core up -d    # 仅核心 9 个微服务
docker compose --profile stream up -d  # 追加 Stream 模块
docker compose --profile ai up -d      # 追加 AI 模块
docker compose --profile seata up -d   # 追加 Seata 7 个模块
docker compose --profile all up -d     # 全部启动

通用配置抽取:x-common 锚点

所有服务共享的基础配置通过 YAML 锚点抽取,避免重复:

x-common: &common
  restart: unless-stopped
  extra_hosts:
    - "host.docker.internal:host-gateway"

# 每个服务引用
services:
  provider:
    <<: *common

host.docker.internal:host-gateway 是关键——它让容器内的微服务能通过 host.docker.internal 域名访问宿主机上运行的中间件。

统一 Dockerfile:ARG 参数化多模块构建

项目只用一个 Dockerfile 服务所有 16 个模块:

FROM eclipse-temurin:21-jre-alpine
ARG MODULE
COPY ${MODULE}/target/ /tmp/build/
RUN JAR_NAME=$(basename ${MODULE}) && \
    if [ -f /tmp/build/${JAR_NAME}.jar ]; then \
      cp /tmp/build/${JAR_NAME}.jar /app/app.jar; \
    elif ls /tmp/build/${JAR_NAME}-*.jar 1>/dev/null 2>&1; then \
      cp /tmp/build/${JAR_NAME}-*.jar /app/app.jar; \
    fi
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app/app.jar"]

通过 --build-arg MODULE=cloud-provider-sample 参数指定模块名,构建时自动从 target/ 目录找到对应的 JAR 包。这意味着新增模块时无需修改 Dockerfile,只需在 docker-compose.yml 中添加一个服务定义。

docker-build.sh:构建编排脚本

docker-build.sh 封装了构建和部署的完整流程:

./docker-build.sh build       # Maven 打包 + 构建所有 Docker 镜像
./docker-build.sh build-one cloud-provider-sample  # 构建单个模块
./docker-build.sh up          # 启动核心 9 个微服务
./docker-build.sh up-seata    # 启动 Seata 7 个模块
./docker-build.sh down        # 停止
./docker-build.sh status      # 查看容器状态
./docker-build.sh logs [svc]  # 查看日志

🏗️ 第三层:一键启停主控——start-all.sh 的 1182 行设计

start-all.sh 是整个脚本体系的核心,1182 行代码管理了 16 个模块的完整生命周期。

模块注册表:声明式定义

所有模块通过一个声明式数组注册,每个条目包含目录名、显示名、端口三元组:

# 核心模块(按启动顺序)
MODULES=(
  "cloud-nacos-discovery-sample|nacos-discovery|8760"
  "cloud-nacos-config-sample|nacos-config|8761"
  "cloud-gateway-sample|gateway|8764"
  "cloud-provider-sample|provider|8765"
  "cloud-provider-reactive-sample|provider-reactive|8762"
  "cloud-provider-dubbo-sample|provider-dubbo|50051"
  "cloud-grpc-server-sample|grpc-server|8090"
  "cloud-consumer-sample|consumer|8766"
  "cloud-consumer-reactive-sample|consumer-reactive|8763"
)

# 特殊模块(需额外中间件条件)
STREAM_MODULE=("cloud-stream-sample|stream|8767")
SEATA_MODULES=(
  "cloud-seata-sample/business-service|seata-business|18081"
  "cloud-seata-sample/storage-service|seata-storage|18082"
  # ... 共 7 个
)
AI_MODULE=("cloud-ai-sample|ai|8888")
RAG_MODULE=("cloud-ai-rag-sample|ai-rag|8889")
KAFKA_MODULE=("cloud-kafka-sample|kafka-sample|8768")

新增模块只需在数组中追加一行,脚本的启动、停止、验证、状态查询都会自动覆盖。

通用启动函数:start_module

所有模块共享同一个启动函数,封装了幂等启动 + 健康检查 + 超时控制

start_module() {
  local module_dir="$1"
  local display_name="$2"
  local port="$3"
  local pid_file="$PID_DIR/$display_name.pid"
  local log_file="$LOG_DIR/$display_name.log"

  # 1. 幂等检查:PID 文件存在且进程存活 → 跳过
  if [ -f "$pid_file" ] && kill -0 "$(cat "$pid_file")" 2>/dev/null; then
    echo "[$display_name] 已在运行 (PID: $(cat "$pid_file"))"
    return
  fi

  # 2. 端口检查:端口已被占用 → 跳过
  if curl -s -o /dev/null --connect-timeout 2 "http://localhost:$port" 2>/dev/null; then
    echo "[$display_name] 端口 $port 已被占用,跳过启动"
    return
  fi

  # 3. 后台启动 + PID 记录
  nohup ./mvnw -pl "$module_dir" spring-boot:run > "$log_file" 2>&1 &
  local pid=$!
  echo "$pid" > "$pid_file"

  # 4. 轮询等待就绪(最多 60 秒)
  for i in $(seq 1 60); do
    if curl -s -o /dev/null "http://localhost:$port" 2>/dev/null; then
      echo " 成功 (PID: $pid, port: $port)"
      return
    fi
    sleep 1
  done

  # 5. 超时处理
  echo " 超时! 请查看日志: $log_file"
  rm -f "$pid_file"
  return 1
}

这个函数的设计要点:

  • 幂等性:已运行的模块不会重复启动
  • 端口感知:即使没有 PID 文件,也能通过端口检测判断服务是否已在运行
  • 统一超时:60 秒轮询,每秒检查一次,避免无限等待
  • 日志隔离:每个模块的日志输出到独立文件 logs/{name}.log

条件启动:按需加载特殊模块

特殊模块(Stream / Seata / AI / RAG / Kafka)不是无脑启动,而是根据中间件状态动态决定

cmd_start() {
  check_java
  check_nacos
  configure_nacos_noauth
  check_special_prerequisites  # 检测各中间件状态,设置 START_XXX 标志

  # 核心模块:无条件启动
  for entry in "${MODULES[@]}"; do
    start_module ...
  done

  # 特殊模块:条件启动
  $START_STREAM && start_single STREAM_MODULE
  $START_SEATA && start_seata_services
  $START_AI && start_single AI_MODULE
  $START_RAG && start_single RAG_MODULE
  $START_KAFKA && start_single KAFKA_MODULE
}

每个 check_xxx 函数负责检测对应中间件的状态,并设置标志位:

check_kafka() {
  if nc -z 127.0.0.1 9092 2>/dev/null; then
    echo "[Kafka] ✓ Kafka 集群已运行"
    START_KAFKA=true
    create_kafka_topics  # 自动创建所需 Topic
  else
    echo "[Kafka] ✗ Kafka 集群未运行,跳过 Kafka 模块"
  fi
}

这意味着:如果你的环境没有 Kafka,脚本会自动跳过 Kafka 模块,不会报错中断。 其他模块照常启动和验证。

Seata 分层启动:依赖感知的三层编排

Seata 模块包含 7 个微服务,它们之间存在调用依赖。脚本实现了三层依赖编排

start_seata_services() {
  publish_seata_nacos_config  # 先发布 Seata 配置到 Nacos

  # 第一层:基础层(account + storage,无下游依赖)
  for entry in \
    "cloud-seata-sample/account-dubbo-service|seata-account-dubbo|50071" \
    "cloud-seata-sample/account-service|seata-account|18084" \
    "cloud-seata-sample/storage-dubbo-service|seata-storage-dubbo|50072" \
    "cloud-seata-sample/storage-service|seata-storage|18082"; do
    start_module ...
  done

  # 第二层:order(依赖 account)
  for entry in \
    "cloud-seata-sample/order-dubbo-service|seata-order-dubbo|50073" \
    "cloud-seata-sample/order-service|seata-order|18083"; do
    start_module ...
  done

  # 第三层:business(依赖 storage + order)
  start_module "cloud-seata-sample/business-service" "seata-business" "18081"
}

这确保了上游服务在下游客启动前已经就绪,避免了分布式事务中常见的"服务未注册"错误。

优雅停止:两阶段清理

stop_all 函数实现了两阶段停止策略:

stop_all() {
  # 第一阶段:通过 PID 文件停止脚本启动的进程
  for pid_file in "$PID_DIR"/*.pid; do
    kill "$(cat "$pid_file")" 2>/dev/null || true
  done
  # 统一等待 15 秒
  for i in $(seq 1 15); do
    # 检查所有进程是否已退出
  done
  # 强杀剩余进程
  for pid in "${pending_pids[@]}"; do
    kill -9 "$pid" 2>/dev/null || true
  done

  # 第二阶段:扫描外部启动的进程(IDE、手动 java -jar 等)
  for entry in "${all_modules[@]}"; do
    pgrep -f "${module_dir}.*(spring-boot:run|\.jar)" | xargs kill 2>/dev/null
  done

  # 清理 RocketMQ、Seata Server、日志目录、PID 文件
  pkill -f "rocketmq" 2>/dev/null || true
  rm -rf "$LOG_DIR" "$PID_DIR"
}

第一阶段停脚本启动的,第二阶段扫外部启动的。先 SIGTERM 优雅退出,超时再 SIGKILL 强杀。最后清理日志和 PID 目录,确保下次启动是干净状态。

自动验证:demo_urls 汇总报告

启动完成后,demo_urls 函数自动执行全量验证:

demo_urls() {
  # Nacos Discovery 验证
  verify_url "http://localhost:8760/discovery/services" "Nacos Discovery 获取服务列表"

  # Web 服务注册与发现
  verify_url "http://localhost:8766/hi?name=hongxi" "直接访问 consumer"
  verify_url "http://localhost:8764/consumer-sample/hi?name=hongxi" "通过网关访问 consumer"

  # Dubbo / gRPC 链路
  verify_url "http://localhost:8766/dubbo?name=hongxi" "consumer → provider-dubbo"
  verify_url "http://localhost:8766/grpc?name=hongxi" "consumer → grpc-server"

  # ... 共 20+ 条验证

  # 汇总结果
  echo "验证结果: 通过 $VERIFY_PASS 项, 失败 $VERIFY_FAIL 项"
  if [ "$VERIFY_FAIL" -eq 0 ]; then
    echo "★ 全部验证通过! ★"
  fi
}

每条 verify_url 调用会记录 HTTP 状态码,最终汇总通过/失败数量。如果全部通过,还会输出后续深度验证的提示

💡 当前已验证: 服务注册发现、健康检查、基础调用链路

📌 还可深入验证以下高级功能:
  1️⃣  Trace 链路追踪 → scripts/verify-trace.sh
  2️⃣  Nacos Config 动态配置
  3️⃣  Sentinel 限流与熔断降级
  4️⃣  Stream 消息收发
  5️⃣  Seata 分布式事务

命令分发:9 个子命令

case "${1:-start}" in
  start)    cmd_start ;;       # 启动所有服务
  stop)     stop_all ;;        # 停止所有服务
  install)  install_all ;;     # 安装中间件 + 打包
  infra)    cmd_infra ;;       # 仅启动中间件
  seata)    cmd_seata ;;       # 仅启动 Seata 7 个模块
  build)    build_all ;;       # 打包所有模块
  verify)   demo_urls ;;       # 执行验证
  logs)     logs_all "$2" ;;   # 查看模块日志
  status)   status_all ;;      # 查看服务状态
esac

每个子命令都是独立的、可单独执行的。你可以只用 status 查看状态,或只用 verify 验证已运行的服务,不需要走完整流程。


🏗️ 第四层:场景化验证脚本——一键到底的自动化

verify-trace.sh:五条链路 Trace 传播验证

这个脚本自动验证 5 条跨服务链路的 Trace ID 传播:

1. Web → Web         : consumer → provider (RestTemplate / FeignClient)
2. Web → gRPC        : consumer → grpc-server
3. Web → Dubbo       : consumer → provider-dubbo
4. Reactive → Reactive: consumer-reactive → provider-reactive (WebClient 手动传递)
5. Reactive → Dubbo  : consumer-reactive → provider-dubbo

核心验证逻辑:

# 1. 生成 W3C 标准 trace ID
TRACE_ID=$(printf '%08x%08x%08x%08x' $RANDOM $RANDOM $RANDOM $RANDOM)

# 2. 携带 traceparent header 发起请求
curl -s -H "traceparent: 00-${TRACE_ID}-${SPAN_ID}-01" \
  "http://localhost:8766/hi?name=traceWeb"

# 3. 从 consumer 日志中提取 trace ID
CONSUMER_LINE=$(extract_trace "$CONSUMER_LOG" "$TRACE_ID")
CONSUMER_TID=$(get_trace_id "$CONSUMER_LINE")

# 4. 从 provider 日志中提取 trace ID,验证是否一致
PROVIDER_LINE=$(extract_trace "$PROVIDER_LOG" "$TRACE_ID")
PROVIDER_TID=$(get_trace_id "$PROVIDER_LINE")

# 5. 比对
if [ "$PROVIDER_TID" = "$TRACE_ID" ]; then
  pass "provider 日志包含相同 trace ID (trace 传播 ✓)"
fi

脚本还实现了日志文件动态发现——通过 lsof 从运行中进程的文件描述符获取实际日志路径:

find_log_by_port() {
  local port="$1"
  local pid=$(lsof -i :"$port" -sTCP:LISTEN -t 2>/dev/null | head -1)
  local logfile=$(lsof -p "$pid" 2>/dev/null | grep "1w.*REG" | awk '{print $NF}' | head -1)
  echo "$logfile"
}

这意味着无论服务是通过 start-all.sh、IDE 还是手动 java -jar 启动的,脚本都能找到正确的日志文件。

verify-seata.sh:分布式事务全流程验证

这个脚本覆盖了 Seata 分布式事务的完整验证流程,共 6 个步骤:

Step 0: 清理旧进程
Step 1: 检查 Nacos + MySQL
Step 2: 初始化数据库 + 安装依赖 + 打包 + 启动辅助服务
Step 3: 发布 seata.properties 到 Nacos
Step 4: 启动 Seata Server(自动下载源码 + 构建)
Step 5: 按依赖顺序启动 7 个微服务
Step 6: 验证分布式事务(回滚 + 提交 + Feign + Dubbo + Xid + 数据一致性)

验证阶段会检查三个维度:

# 1. 事务回滚:预期返回 500(mock 异常触发回滚)
curl -s -o /dev/null -w "HTTP %{http_code}" http://127.0.0.1:18081/seata/rest

# 2. 事务提交:循环调用直到成功
for i in $(seq 1 20); do
  result=$(curl -s -w "\n%{http_code}" http://127.0.0.1:18081/seata/rest)
  if [ "$http_code" = "200" ]; then
    echo "✓ 第 ${i} 次调用成功,事务已提交"
    break
  fi
done

# 3. 数据一致性:SQL 查询验证余额和库存
mysql -u root -proot1234 seata -e \
  "SELECT '账户余额', money FROM account_tbl WHERE user_id='U100001'
   UNION ALL SELECT '库存数量', count FROM storage_tbl WHERE commodity_code='C00321';"

verify-stream.sh:6 种消息场景验证

Stream 验证脚本覆盖了 RocketMQ 的 6 种消息场景:

场景 验证内容
基础消费 StreamBridge → topic → Consumer
定时消息源 Supplier 每秒自动发送
消息处理管道 REST → toUpperCase 转换 → output
延迟消息 StreamBridge + DELAY header
顺序消息 10 条相同 orderKey 的消息
事务消息 本地事务提交 / 回滚

每种场景的验证方式都是检查日志中的关键字

# 延迟消息验证
SEND_TIME=$(date +"%H:%M:%S.%N")
curl -s -X POST "http://127.0.0.1:8767/stream/delay?message=hello+delay&delayLevel=2"
sleep 8
if grep -q "\[延迟消息\] 收到: hello delay" logs/stream-sample.log; then
  echo "✓ 延迟消息消费正常"
fi

脚本最后还会自动清理:停止 Stream 模块和 RocketMQ,确保环境干净。


🏗️ 第五层:AI Skill 自动化演示——让 AI 当操作员

这是本项目最具创新性的设计。通过一个 SKILL.md 文件(495 行),项目定义了 AI 编程助手的完整演示流程。

设计理念:AI 是操作员,脚本是执行引擎

用户: "演示本项目"
  ↓
AI 读取 SKILL.md
  ↓
AI 执行 start-all.sh → 环境检查 + 服务启动 + 基础验证
  ↓
AI 执行 verify-trace.sh → Trace 链路验证
AI 执行 verify-stream.sh → Stream 消息验证
AI 执行 verify-seata.sh → Seata 事务验证
  ↓
AI 执行 curl 命令 → Nacos Config / Sentinel / Spring AI 深度演示
  ↓
AI 输出汇总表格

SKILL.md 中的关键设计:

1. 精简前置原则:启动前仅检查 JDK → Nacos → 安装依赖 3 项,其他中间件按需加载

> 🔴 **精简前置原则**:启动前仅检查 **JDK → Nacos → 安装依赖模块** 3 项基本前置条件。
> 其他中间件不在启动前统一检查,而是在对应模块演示时按需准备。

2. 演示纪律:强制逐步执行,禁止跳过

- 🔴 禁止选择性演示:每个演示场景的所有步骤必须逐一执行
- 🔴 严格按步骤顺序:必须按 Step 1 → Step 2 → ... 的顺序执行
- 🔴 每步必须说明意图并评价结果
- 🔴 禁止用"参考文档"替代执行

3. 一键脚本优先:复杂场景封装为脚本,AI 只需执行一条命令

# Stream 验证:AI 只需执行一条命令
bash .qoder/skills/demo-spring-cloud/scripts/verify-stream.sh

📊 脚本体系速查表

脚本 行数 职责 核心能力
start-all.sh 1182 主控脚本 环境检查 → 中间件安装 → 打包 → 启动 → 验证 → 状态管理
docker-build.sh 300 Docker 构建 Maven 打包 → 镜像构建 → Profile 分组启动
docker-compose.yml 368 容器编排 16 个服务定义 + Profile 分组 + 宿主机桥接
Dockerfile 35 统一镜像 ARG 参数化,一个 Dockerfile 服务所有模块
verify-trace.sh 487 Trace 验证 5 条链路 × 2 端日志比对
verify-seata.sh 281 Seata 验证 数据库初始化 → 7 服务启动 → 事务回滚/提交 → 数据一致性
verify-stream.sh 296 Stream 验证 6 种消息场景全覆盖
kafka.sh 429 Kafka 管理 3 节点 KRaft 集群编排
SKILL.md 495 AI 演示 9 大场景自动化演示流程

总计约 4800 行配置/脚本代码,管理了 16 个微服务模块、7 种中间件、5 条 Trace 链路、6 种消息场景、3 种分布式事务调用路径。


💡 最佳实践总结

1. 幂等设计:可重复执行,无副作用

每个函数都遵循"检测 → 跳过/执行"模式。已运行的服务不重复启动,已安装的中间件不重复下载,已创建的 Topic 不重复创建。

2. 分层解耦:每层可独立使用

你可以只用 start-all.sh 本地启动,或只用 Docker 部署,或只用验证脚本。各层互不依赖,按需组合。

3. 条件加载:缺少依赖不阻塞

Kafka 没装?跳过 Kafka 模块。MySQL 没跑?跳过 Seata。脚本不会因缺少某个中间件而整体失败。

4. 健康检查驱动:不靠 sleep 猜

所有等待都基于健康检查端点(/actuator/healthnc -zcurl),而非固定 sleep。服务 5 秒就绪就等 5 秒,60 秒没好就超时退出。

5. 声明式注册:新增模块零成本

新增模块只需在数组中追加一行 "目录|名称|端口",启动/停止/验证/状态查询自动覆盖。

6. 日志 + PID 隔离:可追溯可管理

每个模块独立的日志文件和 PID 文件,支持 logs <模块名> 实时查看,停止时精准清理。


🔗 相关链接


📝 结语

微服务示例项目的价值不在于"写了多少功能代码",而在于能不能让别人在 5 分钟内跑起来

这个项目用 4800 行脚本/配置代码,构建了一套完整的微服务全生命周期管理体系:从环境检测、中间件安装、服务编排、健康检查、链路验证到 AI 自动化演示。每一层设计都指向同一个目标——降低使用门槛,提升演示体验

如果你正在

  • 🎯 搭建 Spring Cloud 示例项目,苦于脚本设计无从下手
  • 🚀 想给团队演示微服务,需要一个"一键到底"的方案
  • 🏗️ 管理多个中间件依赖,想要自动化而非手动排查

Star ⭐ spring-cloud-samples,体验脚本驱动的微服务管理!

git clone https://github.com/javahongxi/spring-cloud-samples.git
cd spring-cloud-samples
sh start-all.sh install  # 一键安装中间件
sh start-all.sh          # 一键启动 + 验证

© hongxi.org

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐