主题
界面
主题
《项目 A:测试方案与分层测试》证明了功能是对的;本节把它跑起来并保持健康。
★ 本节的交付物是一个真正能用的启动脚本 ✓:
pwsh -File scripts/run-project-a.ps1
# 打包 → 停旧进程 → 启动 → 等健康检查通过 → 打印访问方式★ 它刻意做了五件事(每一件都对应一个真实会踩的坑):
| # | 做什么 | 不做的后果 |
|---|---|---|
| ① | 总是重新打包 | 跑的是旧代码(本项目踩过 ✗) |
| ② | 先停掉占用端口的旧进程 | 端口被占,启动失败 ✗ |
| ③ | 等健康检查真的通过 | 固定 sleep 不可靠 ✗✗ |
| ④ | 失败时打印日志尾部 | 只能盲猜 ✗ |
| ⑤ | 打印访问方式与验收清单 | 不知道怎么验证 ✓ |
★ 最常见的部署失败,往往不是代码问题 ✗:
① 端口被占 → "Port 18080 was already in use" ✗
② 跑的是旧 jar → 改了代码但没重新打包,现象是"改了没生效" ✗✗
③ 健康检查还没通过就访问 → Connection refused ✗
④ 配置没外置 → 换环境要改代码重打包 ✗
⑤ 看不到日志 → 起不来也不知道为什么 ✗✗★ 本节的脚本就是逐条解决这五个 ✓✓
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)✓否则就是把"没测过的代码"发出去 ✗✗
★ 逐条说明 ✓:
① 总是重新打包 ✓
本项目踩过:"页面代码改了,但跑的是旧 jar,测试报 404"(见约束 6.3)
→ 缓存可以缓存依赖,但**绝不能缓存"上次的产物"**(S4-028)② 停掉占用端口的旧进程 ✓
$conn = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue
if ($conn) { Stop-Process -Id $conn.OwningProcess -Force }③ 等健康检查真的通过 ★★★
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 与多容器编排》 的"启动竞态"是同一个道理 ✓✓
④ 失败时打印日志尾部 ✓
if (-not $ready) {
Get-Content $errLog -Tail 30 # ★ 立刻看到原因,而不是盲猜
exit 1
}⑤ 打印访问方式与验收清单 ✓(脚本末尾的"验收清单"就是照着 《项目 A:需求分析与范围定义》 的验收标准写的 ✓)
★ Spring Boot Actuator 的配置 ✓:
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
show-details: always # ★ 让健康检查返回详细信息(排障有用)★ 三个层次的"健康" ✓✓:
| 层次 | 检查什么 | 例子 |
|---|---|---|
| 进程活着 ★ | 端口在监听 | 最弱,但最常用 ✓ |
| 应用就绪 ★★★ | /actuator/health 返回 UP | Spring Boot 默认 ✓✓ |
| 依赖可用 ★★★ | 健康检查里带上数据库/Redis | 生产必配(management.health.db.enabled)✓✓ |
★★ 一条重要的实践 ✓✓:
健康检查要能反映"依赖是否可用" —— 否则数据库挂了,健康检查还是 UP,流量会继续打到一个已经不能服务的实例 ✗✗
(这与 《Kubernetes 入门》 的 readinessProbe 是同一件事 ✓)
★ 但要小心 ✓✓:
别把健康检查做得太重(查一堆依赖 + 慢查询)✗ —— 它会被频繁调用(K8s 默认每 10 秒一次),本身就会成为压力 ⚠️
★ 本项目用两个 profile ✓:
默认 → H2 内存库(一条命令就能跑,零依赖)✓
postgres → 真实 PostgreSQL(验证真实行为)✓pwsh -File scripts/run-project-a.ps1 -Profile postgres★ 配置写法 ✓:
spring:
datasource:
url: jdbc:postgresql://${PGHOST:localhost}:${PGPORT:5432}/${PGDATABASE:kb_learn}
username: ${PGUSER:postgres}
password: ${PGPASSWORD:}★★ 三条规矩 ✓✓:
| # | 规矩 | 不做会怎样 |
|---|---|---|
| ① | 密码等敏感配置走环境变量,不写进文件 ★★★ | 密码进 Git ✗✗ |
| ② | 默认值用 ${VAR:默认} 语法 ★★ | 不传就启动失败 ✗ |
| ③ | 不同环境用 profile 隔离 ★★★ | 换环境要改代码重打包 ✗ |
★ 一条判断标准 ✓✓: "换一个环境部署,需要改代码吗?" —— 需要就说明配置没外置 ✗
★ 本项目的日志配置 ✓:
logging:
level:
root: INFO
cn.kb.learn.library: DEBUG # 自己的包开 DEBUG★ 四条实践 ✓✓:
| # | 实践 | 原因 |
|---|---|---|
| ① | 日志输出到 stdout(不要写容器内文件)★★★ | 容器写文件会让可写层膨胀(《Docker 基础与镜像容器》)✗ |
| ② | 带 TraceId(跨服务排障)★★★ | 否则不知道一次请求经过了什么 ✓ |
| ③ | 5xx 必须记 error 日志 ★★★ | 线上排查的唯一线索 ✓ |
| ④ | 别打敏感信息(密码、身份证)★★★ | 日志泄露是常见事故 ✗✗ |
★ 本项目的启动脚本把日志重定向到文件 ✓:
labs/project-a/target/app.log → 标准输出
labs/project-a/target/app.err → 错误输出1. 端口 → 是不是被占了?进程在不在?
2. 健康 → /actuator/health 返回什么?(UP / DOWN / 超时)
3. 日志 → 应用自己怎么说(看 app.log / app.err 尾部)
4. 配置 → profile 对不对?数据库连得上吗?密码对吗?★ 常见故障速查 ✓:
| 现象 | 原因 | 处理 |
|---|---|---|
Port 18080 was already in use ✗ | 端口被占 | 脚本会自动停掉;或换端口 |
| 启动后立刻退出 ✗ | 配置错 / 端口占 | 看 app.err 尾部 |
Connection refused ✗ | 还没就绪就访问 | 等健康检查(脚本已做) |
| 健康检查 DOWN ✗ | 数据库连不上 | 查库是否启动、密码、库名 |
| 改了代码没生效 ✗ | 没重新打包 | 脚本默认总是重新打包 ✓ |
pwsh -File scripts/run-project-a.ps1pwsh -File scripts/run-project-a.ps1 -Profile postgres# ✗ 三处问题:没重新打包、没停旧进程、固定 sleep 不可靠
java -jar target/project-a-1.0.0.jar
Start-Sleep 10
curl localhost:18080/api/books # 可能还没起来 ✗代码:scripts/run-project-a.ps1
运行:
cd <仓库根目录>
pwsh -File scripts/run-project-a.ps1★ 实测输出 ✓:
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 ✓
-SkipBuild 加上再改代码:观察"改了没生效" ✗✓;app.log 里有没有 TraceId / 请求日志 ✓。| # | 错误 | 说明 |
|---|---|---|
| 1 | 不重新打包就启动 ✗✗ | 改了没生效 ✓✓ |
| 2 | 固定 sleep 代替健康检查 ✗✗ | 慢机器假失败、快机器白等 ✓ |
| 3 | 密码写进配置文件 ✗✗ | 进 Git ✓ |
| 4 | 换环境要改代码 ✗✗ | 配置没外置 ✓ |
| 5 | 健康检查不查依赖 ✗✗ | 依赖挂了还在接流量 ✓✓ |
| 6 | 日志写进容器文件 ✗✗ | 可写层膨胀 ✓ |
| 7 | 看不到日志 ✗✗ | 起不来也猜不出原因 ✓✓ |
目标:用一条命令把项目跑起来,并亲手制造一次故障再定位。
步骤:
**看脚本如何处理** ✓;验收方式:能说出"你的服务起不来时,第一个看什么、第二个看什么"。
扩展任务:给项目加优雅停机:
server.shutdown=graceful + 超时 ✓;sleep 10 等待,就一定可以访问了。(对 / 错)spring:
datasource:
password: Prod@123456 # 直接写死有什么问题?怎么改? 5. (排障)服务启动后 /actuator/health 一直 DOWN,你的排查顺序是什么?
${VAR:默认};环境用 profile 隔离 ✓${DB_PASSWORD} 从环境变量/密钥管理服务注入 ✓✓★① 看 health 的详细内容(show-details: always 会说明是哪个组件 DOWN)✓; ② 看日志(数据库连不上会有明确异常)✓✓; ③ 验证依赖本身(数据库是否启动、网络是否通、账号密码是否正确)✓; ④ 验证配置(profile 是否激活、环境变量是否注入)✓《项目 A:复盘与简历表达》:怎么把这个项目讲成你的东西。