Skip to content

项目 A:部署与运维 ​

S5-006必学高级约 60 分钟JDK 21项目实战 · 项目 A:测试管理平台

1. 本节定位 ​

《项目 A:测试方案与分层测试》证明了功能是对的;本节把它跑起来并保持健康。

★ 本节的交付物是一个真正能用的启动脚本 ✓:

powershell
pwsh -File scripts/run-project-a.ps1
# 打包 → 停旧进程 → 启动 → 等健康检查通过 → 打印访问方式

★ 它刻意做了五件事(每一件都对应一个真实会踩的坑):

#做什么不做的后果
①总是重新打包跑的是旧代码(本项目踩过 ✗)
②先停掉占用端口的旧进程端口被占,启动失败 ✗
③等健康检查真的通过固定 sleep 不可靠 ✗✗
④失败时打印日志尾部只能盲猜 ✗
⑤打印访问方式与验收清单不知道怎么验证 ✓

2. 学习目标 ​

  • 能用一条命令把项目跑起来 ★★★;
  • 知道启动脚本该做的五件事 ★★★;
  • 知道"等健康检查"比"固定 sleep"可靠 ★★★;
  • 能把配置外置(profile / 环境变量) ★★★;
  • 会按四步排障(端口 → 健康 → 日志 → 配置) ★★★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

★ 最常见的部署失败,往往不是代码问题 ✗:

text
① 端口被占          → "Port 18080 was already in use" ✗
② 跑的是旧 jar      → 改了代码但没重新打包,现象是"改了没生效" ✗✗
③ 健康检查还没通过就访问 → Connection refused ✗
④ 配置没外置        → 换环境要改代码重打包 ✗
⑤ 看不到日志        → 起不来也不知道为什么 ✗✗

★ 本节的脚本就是逐条解决这五个 ✓✓

5. 核心概念 ​

5.1 打包:一条命令产出可执行 jar ★★★ ​

powershell
cd labs
.\mvnw.cmd -pl project-a package -DskipTests
# 产物:labs/project-a/target/project-a-1.0.0.jar

★ 为什么用 spring-boot-maven-plugin 的 repackage ✓:

普通 mvn package 出来的是普通 jar(不含依赖)✗

Spring Boot 的 repackage 会打成 fat jar(依赖都进去)—— 于是 java -jar 就能跑,不需要外部 classpath ✓✓

★★ 一条纪律 ✓✓:

测试与打包分开(package -DskipTests)✓

但打包前测试必须已经在别处跑过(本项目:CI / 本地 mvn test)✓

否则就是把"没测过的代码"发出去 ✗✗

5.2 启动脚本该做的五件事 ★★★ ​

★ 逐条说明 ✓:

① 总是重新打包 ✓

text
本项目踩过:"页面代码改了,但跑的是旧 jar,测试报 404"(见约束 6.3)
→ 缓存可以缓存依赖,但**绝不能缓存"上次的产物"**(S4-028)

② 停掉占用端口的旧进程 ✓

powershell
$conn = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue
if ($conn) { Stop-Process -Id $conn.OwningProcess -Force }

③ 等健康检查真的通过 ★★★

powershell
while ((Get-Date) -lt $deadline) {
    $resp = Invoke-WebRequest -Uri $healthUrl -UseBasicParsing -TimeoutSec 3
    if ($resp.StatusCode -eq 200) { $ready = $true; break }
    Start-Sleep -Seconds 2
}

为什么不用 Start-Sleep 10 ✗: · 机器慢时 → 假失败(还没起来就去访问) · 机器快时 → 白等(明明 3 秒就好了)

这与 《Docker Compose 与多容器编排》 的"启动竞态"是同一个道理 ✓✓

④ 失败时打印日志尾部 ✓

powershell
if (-not $ready) {
    Get-Content $errLog -Tail 30      # ★ 立刻看到原因,而不是盲猜
    exit 1
}

⑤ 打印访问方式与验收清单 ✓(脚本末尾的"验收清单"就是照着 《项目 A:需求分析与范围定义》 的验收标准写的 ✓)

5.3 健康检查:从"能 ping"到"能服务" ★★★ ​

★ Spring Boot Actuator 的配置 ✓:

yaml
management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      show-details: always        # ★ 让健康检查返回详细信息(排障有用)

★ 三个层次的"健康" ✓✓:

层次检查什么例子
进程活着 ★端口在监听最弱,但最常用 ✓
应用就绪 ★★★/actuator/health 返回 UPSpring Boot 默认 ✓✓
依赖可用 ★★★健康检查里带上数据库/Redis生产必配(management.health.db.enabled)✓✓

★★ 一条重要的实践 ✓✓:

健康检查要能反映"依赖是否可用" —— 否则数据库挂了,健康检查还是 UP,流量会继续打到一个已经不能服务的实例 ✗✗

(这与 《Kubernetes 入门》 的 readinessProbe 是同一件事 ✓)

★ 但要小心 ✓✓:

别把健康检查做得太重(查一堆依赖 + 慢查询)✗ —— 它会被频繁调用(K8s 默认每 10 秒一次),本身就会成为压力 ⚠️

5.4 配置外置:profile 与环境变量 ★★★ ​

★ 本项目用两个 profile ✓:

text
默认      → H2 内存库(一条命令就能跑,零依赖)✓
postgres  → 真实 PostgreSQL(验证真实行为)✓
powershell
pwsh -File scripts/run-project-a.ps1 -Profile postgres

★ 配置写法 ✓:

yaml
spring:
  datasource:
    url: jdbc:postgresql://${PGHOST:localhost}:${PGPORT:5432}/${PGDATABASE:kb_learn}
    username: ${PGUSER:postgres}
    password: ${PGPASSWORD:}

★★ 三条规矩 ✓✓:

#规矩不做会怎样
①密码等敏感配置走环境变量,不写进文件 ★★★密码进 Git ✗✗
②默认值用 ${VAR:默认} 语法 ★★不传就启动失败 ✗
③不同环境用 profile 隔离 ★★★换环境要改代码重打包 ✗

★ 一条判断标准 ✓✓: "换一个环境部署,需要改代码吗?" —— 需要就说明配置没外置 ✗

5.5 日志:看得见才排得了 ★★★ ​

★ 本项目的日志配置 ✓:

yaml
logging:
  level:
    root: INFO
    cn.kb.learn.library: DEBUG     # 自己的包开 DEBUG

★ 四条实践 ✓✓:

#实践原因
①日志输出到 stdout(不要写容器内文件)★★★容器写文件会让可写层膨胀(《Docker 基础与镜像容器》)✗
②带 TraceId(跨服务排障)★★★否则不知道一次请求经过了什么 ✓
③5xx 必须记 error 日志 ★★★线上排查的唯一线索 ✓
④别打敏感信息(密码、身份证)★★★日志泄露是常见事故 ✗✗

★ 本项目的启动脚本把日志重定向到文件 ✓:

text
labs/project-a/target/app.log    → 标准输出
labs/project-a/target/app.err    → 错误输出

5.6 排障四步法 ★★★ ​

text
1. 端口    → 是不是被占了?进程在不在?
2. 健康    → /actuator/health 返回什么?(UP / DOWN / 超时)
3. 日志    → 应用自己怎么说(看 app.log / app.err 尾部)
4. 配置    → profile 对不对?数据库连得上吗?密码对吗?

★ 常见故障速查 ✓:

现象原因处理
Port 18080 was already in use ✗端口被占脚本会自动停掉;或换端口
启动后立刻退出 ✗配置错 / 端口占看 app.err 尾部
Connection refused ✗还没就绪就访问等健康检查(脚本已做)
健康检查 DOWN ✗数据库连不上查库是否启动、密码、库名
改了代码没生效 ✗没重新打包脚本默认总是重新打包 ✓

6. 递进示例 ​

6.1 最小示例:一行启动 ​

powershell
pwsh -File scripts/run-project-a.ps1

6.2 业务示例:换库运行 ​

powershell
pwsh -File scripts/run-project-a.ps1 -Profile postgres

6.3 反例:手工启动且不等健康检查 ​

powershell
# ✗ 三处问题:没重新打包、没停旧进程、固定 sleep 不可靠
java -jar target/project-a-1.0.0.jar
Start-Sleep 10
curl localhost:18080/api/books      # 可能还没起来 ✗

7. 完整可运行代码与实测 ​

代码:scripts/run-project-a.ps1

运行:

powershell
cd <仓库根目录>
pwsh -File scripts/run-project-a.ps1

★ 实测输出 ✓:

text
 S5 项目 A:图书借阅管理系统
 端口 18080 | profile ''
  检查端口占用...
  端口 18080 空闲
  打包(-DskipTests,测试另有命令)...
  打包完成
  jar: project-a-1.0.0.jar
  启动中(日志:...\target\app.log)...
  已启动,PID 12345
  等待健康检查:http://localhost:18080/actuator/health
  健康检查通过:{"status":"UP",...}

 访问方式:
   前端页面   本机 18080 端口
   图书接口   /api/books
   健康检查   /actuator/health

★ 手工验证过的一次 ✓:首页 200、/api/books 200 ✓

8. Debug 观察点 ​

  1. 把 -SkipBuild 加上再改代码:观察"改了没生效" ✗✓;
  2. 手动占住 18080 端口(起个别的进程):观察脚本自动停掉它 ✓;
  3. 把健康检查地址改错:观察脚本 90 秒超时并打印日志尾部 ✓✓;
  4. 把数据库密码改错(postgres profile):观察健康检查 DOWN ✗;
  5. 看 app.log 里有没有 TraceId / 请求日志 ✓。

9. 常见错误 ​

#错误说明
1不重新打包就启动 ✗✗改了没生效 ✓✓
2固定 sleep 代替健康检查 ✗✗慢机器假失败、快机器白等 ✓
3密码写进配置文件 ✗✗进 Git ✓
4换环境要改代码 ✗✗配置没外置 ✓
5健康检查不查依赖 ✗✗依赖挂了还在接流量 ✓✓
6日志写进容器文件 ✗✗可写层膨胀 ✓
7看不到日志 ✗✗起不来也猜不出原因 ✓✓

10. 动手实验 ​

目标:用一条命令把项目跑起来,并亲手制造一次故障再定位。

步骤:

  1. 运行启动脚本,按验收清单走一遍 ✓;
  2. 故意占住端口,**看脚本如何处理** ✓;
  3. 把健康检查路径改错,观察超时与日志输出 ✓✓;
  4. 加一个自己的配置项(如"超期每天罚金"),走通配置外置 ★★;
  5. 用 postgres profile 跑一次,对比两种库的行为差异 ✓。

验收方式:能说出"你的服务起不来时,第一个看什么、第二个看什么"。

扩展任务:给项目加优雅停机:

  • 配置 server.shutdown=graceful + 超时 ✓;
  • 停机前先从注册中心摘除(《Nacos 注册与配置中心》)✓✓;
  • 说明:为什么"直接 kill -9"会丢请求(提示:正在处理的请求没机会完成)★★

11. 练习题 ​

  1. 判断:启动后用 sleep 10 等待,就一定可以访问了。(对 / 错)
  2. 填空:配置外置的三条规矩是 、、______。
  3. 简答:为什么"健康检查要能反映依赖是否可用"?
  4. (判断)
yaml
spring:
  datasource:
    password: Prod@123456      # 直接写死

有什么问题?怎么改? 5. (排障)服务启动后 /actuator/health 一直 DOWN,你的排查顺序是什么?

参考答案(要点)
  1. 错。机器慢时 10 秒不够,机器快时白等 ✗ —— 应该轮询健康检查直到 UP(或超时)✓✓★
  2. 敏感配置走环境变量;默认值用 ${VAR:默认};环境用 profile 隔离 ✓
  3. 否则依赖挂了健康检查还是 UP ✗ —— 流量会继续打到已经不能服务的实例(用户全部报错,但监控显示"健康")✓✓★
  4. 密码写进配置文件会被提交到 Git ✗✗ —— 改成 ${DB_PASSWORD} 从环境变量/密钥管理服务注入 ✓✓★
  5. ① 看 health 的详细内容(show-details: always 会说明是哪个组件 DOWN)✓; ② 看日志(数据库连不上会有明确异常)✓✓; ③ 验证依赖本身(数据库是否启动、网络是否通、账号密码是否正确)✓; ④ 验证配置(profile 是否激活、环境变量是否注入)✓

12. 掌握标准 ​

  • [ ] 能用一条命令启动项目 ★★★;
  • [ ] 能说出启动脚本该做的五件事 ★★★;
  • [ ] 知道等健康检查比固定 sleep 可靠 ★★★;
  • [ ] 能把密码等敏感配置外置 ★★★;
  • [ ] 能把日志输出到 stdout 且不打敏感信息 ★★★;
  • [ ] 会按四步排障 ★★★。

13. 扩展资源 ​

14. 下一步学习 ​

《项目 A:复盘与简历表达》:怎么把这个项目讲成你的东西。

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引