完整架构回顾
经过第 1–12 章的学习,eventd v2 的完整架构:
外部客户端
├── HTTP/1.1 → eventd-gateway (Reactor Netty, 第 1–3 章)
│ ├── HTTP 转发 → eventd-api
│ └── WebSocket → 实时推送
│
└── HTTPS (生产) → 负载均衡器 → gateway 集群
eventd-api (Spring WebFlux, 第 4–6 章)
├── @RestController / RouterFunction (第 4 章)
├── WebFilter 链 (第 4.3 节)
│ ├── 请求 ID (第 4.3 节)
│ ├── JWT 鉴权 (第 6.1 节)
│ └── CORS (第 6.2 节)
├── 请求体/响应流处理 (第 5 章)
├── 统一错误契约 (第 5.3 节)
└── 测试覆盖 (第 6.3 节)
eventd-data (R2DBC + Cache, 第 7–9 章)
├── ConnectionPool (第 7.1 节)
├── TransactionalOperator (第 7.2 节)
├── ReactiveCrudRepository (第 7.3 节)
├── Flyway Schema (第 8.1 节)
├── 查询优化 (第 8.2 节)
├── 连接健康 (第 8.3 节)
├── 主从路由 (第 9.1 节)
├── Caffeine Async (第 9.2 节)
└── 缓存一致性 (第 9.3 节)
eventd-monitor (Micrometer + Zipkin, 第 10 章)
├── Counter/Timer/Gauge (第 10.1 节)
├── Distributed Tracing (第 10.2 节)
└── Structured Logging (第 10.3 节)
eventd-resilience (弹性模式, 第 11 章)
├── Load Balancer (第 11.1 节)
├── Circuit Breaker (第 11.2 节)
└── Rate Limiter (第 11.3 节)
eventd-saga (事件驱动与事务编排, 第 12 章)
├── ReactiveEventBus / EventGateway (第 12.1 节)
└── Saga Orchestrator + 补偿链 (第 12.2 节)
模块划分
eventd-v2/
├── pom.xml # 父 POM,BOM 管理
├── eventd-gateway/ # 第 1–3 章
│ ├── GatewayServer.java
│ ├── RouteTable.java
│ ├── HttpProxyHandler.java
│ ├── GatewayWebSocketHandler.java
│ └── Middleware.java
├── eventd-api/ # 第 4–6 章
│ ├── UserController.java
│ ├── OrderRouter.java
│ ├── GlobalExceptionHandler.java
│ ├── SecurityConfig.java
│ └── WebConfig.java
├── eventd-data/ # 第 7–9 章
│ ├── DataSourceConfig.java
│ ├── UserRepository.java
│ ├── OrderRepository.java
│ ├── TransactionConfig.java
│ ├── CacheConfig.java
│ └── FlywayMigration.java
├── eventd-monitor/ # 第 10 章
│ ├── MetricsConfig.java
│ ├── TracingConfig.java
│ └── logging/logback-spring.xml
├── eventd-resilience/ # 第 11 章
│ ├── LoadBalancerConfig.java
│ ├── ResilienceConfig.java
│ └── ResilienceRateLimiterConfig.java
├── eventd-saga/ # 第 12 章
│ ├── ReactiveEventBus.java
│ ├── EventGateway.java
│ ├── OrderSagaOrchestrator.java
│ ├── SagaStateMachine.java
│ └── SagaLogRepository.java
└── eventd-shared/ # 共享模型
├── domain/
│ ├── User.java
│ ├── Order.java
│ └── Inventory.java
└── event/
└── OrderCreatedEvent.java
关键数据流
场景:用户下单
1. POST /api/orders → gateway (Reactor Netty)
→ 请求 ID 生成 + 日志记录
→ JWT 验证
→ CORS 检查
2. 转发到 eventd-api (WebFlux)
→ OrderController.createOrder()
→ @Valid 参数校验
3. 进入 Saga Orchestrator
→ 创建订单(R2DBC 事务)
→ 扣减库存(HTTP 调用 inventory-service)
→ 处理支付(HTTP 调用 payment-service)
→ 任一失败 → 补偿链
4. 数据库操作
→ OrderRepository.save() → R2DBC
→ TransactionalOperator 包裹
→ 提交后失效缓存
5. 缓存操作
→ Caffeine Async 本地缓存失效
→ Redis 缓存失效广播
6. 响应返回
→ 201 Created + Location 头
→ 统一错误 JSON(如失败)
7. 监控记录
→ orders.created Counter +1
→ orders.create.latency Timer 记录
→ Trace Span 结束
配置文件
# application.yml
eventd:
gateway:
port: 8080
max-connections: 10000
api:
port: 8081
db:
master: r2dbc:postgresql://db-master:5432/eventd
slave: r2dbc:postgresql://db-slave:5432/eventd
pool-max-size: 20
cache:
caffeine-max-size: 10000
caffeine-ttl: 5m
redis-ttl: 10m
resilience:
circuit-breaker-failure-rate: 50
rate-limit-per-second: 1000
eventd.* 是项目自定义前缀,Spring Boot 不会自动识别,需要自己写 @ConfigurationProperties("eventd") 绑定;数据库、Redis 和追踪导出仍然通过标准的 spring.r2dbc.*、spring.data.redis.*、management.zipkin.tracing.endpoint 配置,不要在自定义前缀下重复一份。
management:
zipkin:
tracing:
endpoint: http://zipkin:9411/api/v2/spans
可运行验收清单
部署 eventd v2 后,依次验证:
| # | 验证项 | 命令/操作 | 期望结果 |
|---|---|---|---|
| 1 | 网关启动 | curl http://localhost:8080/health | ok |
| 2 | API 启动 | curl http://localhost:8081/health | ok |
| 3 | 创建用户 | POST /api/users | 201, 返回用户 JSON |
| 4 | 查询用户 | GET /api/users/{id} | 200,返回用户 JSON;连续两次请求后 cache.gets 指标 hit 计数增长 |
| 5 | 创建订单 | POST /api/orders | 201, Saga 执行成功 |
| 6 | 订单回滚 | 支付失败模拟 | 库存恢复, 订单 CANCELLED |
| 7 | WebSocket | wscat -c ws://localhost:8080/ws/events | 实时推送 |
| 8 | 限流 | ab -n 10000 -c 100 -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/orders | 部分 429,带 Retry-After |
| 9 | 熔断 | 停掉 inventory-service,连续创建订单 | 失败率达到阈值后熔断打开,后续请求快速失败(CallNotPermittedException) |
| 10 | 指标 | curl http://localhost:8081/actuator/prometheus | 返回 Prometheus 格式 |
| 11 | 追踪 | 访问 Zipkin UI | 看到完整 Trace |
| 12 | 日志 | 查看 stdout | JSON 格式,含 requestId/traceId |
| 13 | 缓存一致性 | 更新订单后立即查询 | 读到新值;若仍为旧值,检查缓存失效是否在事务提交后执行 |
| 14 | 主从路由 | 读请求期间查 pg_stat_activity(按 application_name / 连接账号区分主从) | 读请求落在从库连接上 |
| 15 | 事务 | 并发扣库存后核对库存与订单数 | 库存不为负,售出数量与订单数一致 |
性能基线
性能基线必须在固定的硬件、固定的数据集和固定的压测脚本下测出来,不能从别的项目抄数字。每次记录以下信息:
| 项目 | 记录内容 |
|---|---|
| 环境 | CPU/内存、JDK 版本、容器限额、网络拓扑;无关的构建任务不要混入 |
| 数据集 | 表行数、索引、缓存命中率、连接池大小 |
| 工具 | wrk/k6/Gatling、并发数、持续时长、是否 keep-alive |
| 指标 | QPS、P50/P95/P99、错误率、CPU、内存、GC 暂停 |
| 场景 | 网关转发、API+缓存、API+R2DBC、Saga 全流程分别记录 |
同一配置至少跑 3 次取中位数;压测机与被压测服务分开部署,避免客户端先成为瓶颈。这里刻意不给出具体 QPS 数字:不同机器、不同数据集上的数字不可比,抄一套数字只会制造错误的预期。
生产检查清单
部署到生产前确认:
-
spring.profiles.active=prod关闭所有调试功能 - 数据库连接池 max-size 按实例数与
max_connections分摊,并留出运维连接 - Redis 配置了持久化(AOF + RDB)
- 所有密码/token 从环境变量读取,不硬编码
- 日志级别 INFO,JSON 输出到 stdout(由日志收集器转发)
- Prometheus 抓取间隔与指标精度匹配(Prometheus 默认
scrape_interval是 1m;很多 Helm chart 会改成 15s/30s,按实际抓取配置和告警窗口对齐) - 追踪采样率按流量设置,避免全量采集拖垮后端
- 限流阈值根据压测数据设置,留 50% 余量
- 断路器阈值根据历史错误率设置
- Flyway baseline 已执行,迁移脚本版本正确
- SSL/TLS 证书有效
- CORS allowed origins 不包含
* - 健康检查端点
/actuator/health可被监控访问
小结
第 12 章是 eventd v2 的全链路集成与验收。至此,卷二的前四个部分全部完成:
- 第一部分(第 1–3 章):Reactor Netty 网络服务与网关
- 第二部分(第 4–6 章):WebFlux API 层
- 第三部分(第 7–9 章):R2DBC 数据访问与缓存
- 第四部分(第 10–12 章):监控、弹性与事件驱动集成
下一部分(第五部分)进入测试与验收:StepVerifier 与响应式测试工具链。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。