14.1 生产检查清单

响应式系统上线前全面检查清单:EventLoop 线程池与 boundedElastic 配置、R2DBC/Redis/HTTP 连接池大小计算与超时层级设置、G1/ZGC JVM 参数、结构化日志级别与 Trace 采样率、密码 TLS CORS 安全核查与 Prometheus 告警基线。

线程池检查

Reactor Netty 的默认 IO worker 线程数是 max(CPU 核心数, 4),不是 CPU × 2。一个 EventLoop 线程通过多路复用承载很多连接,不要按「每连接一线程」估算。

// 生产环境显式配置:1 个 selector 线程 + CPU 数个 worker 线程
LoopResources loops = LoopResources.create(
    "eventd",
    1,
    Math.max(4, Runtime.getRuntime().availableProcessors()),
    true);  // 最后一个参数是 daemon

HttpServer.create().runOn(loops);
检查项建议
EventLoop 线程数默认 max(CPU, 4);只有实测确认 I/O 线程是瓶颈时才调大
boundedElastic 线程数默认 10 × CPU,上限 10 万排队任务;不要按请求数放大
parallel 线程数默认 CPU 核心数,不改
自定义线程名前缀便于线程 dump 识别

连接池检查

R2DBC

spring:
  r2dbc:
    pool:
      max-size: 20              # 不超过 PostgreSQL max_connections × 0.8 / 实例数
      max-idle-time: 10m
      max-life-time: 30m
      max-acquire-time: 5s      # 拿不到连接快速失败

Redis

spring:
  data:
    redis:
      lettuce:
        pool:
          max-active: 50
          max-idle: 20
          min-idle: 5

Spring Boot 3 的 Redis 属性在 spring.data.redis 下;spring.redis.* 是 Boot 2 的旧前缀,绑不上。

HTTP 客户端(内部调用)

ConnectionProvider provider = ConnectionProvider.builder("internal")
    .maxConnections(50)
    .pendingAcquireMaxCount(100)
    .pendingAcquireTimeout(Duration.ofSeconds(5))
    .build();

超时检查

eventd:
  timeouts:
    http-client: 10s           # 下游 HTTP 调用
    database-query: 5s         # 数据库查询
    cache-operation: 100ms     # 缓存操作
    websocket-ping: 30s        # WebSocket 心跳
    saga-step: 30s             # Saga 单步骤

原则:串行调用时,上游超时要大于下游各级超时之和 + 网络延迟;并行调用则按最慢分支计算。否则上游已经超时返回,下游还在处理,资源被白白占用。

gateway (30s) → api (20s) → database (5s)
       ↑ 必须大于 20s + 网络延迟

JVM 内存与 GC

# 推荐生产参数(注释不能写在反斜杠续行后面,否则会被当成参数)
java \
  -Xms2g -Xmx2g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/tmp/heapdump.hprof \
  -XX:+UseStringDeduplication \
  -jar eventd.jar
  • -Xms2g -Xmx2g:堆固定,避免运行时扩缩容带来的抖动;具体大小按容器内存限额设置
  • -XX:MaxGCPauseMillis=200:这是 G1 的目标值,不是硬保证
  • -XX:+UseStringDeduplication:仅 G1 支持;主要收益在大量重复字符串的场景

响应式系统对 GC 暂停敏感(暂停期间无法处理请求)。G1 或 ZGC(JDK 15+ 可生产使用,JDK 21+ 推荐分代 ZGC)都是可选方案;延迟目标很严格时优先实测 ZGC,而不是只改参数名。

日志与采样

生产日志配置:

logging:
  level:
    root: INFO
    com.plume.eventd: INFO
    io.r2dbc.postgresql: WARN       # R2DBC 驱动太吵
    reactor.netty: WARN

生产输出用第 10.3 节的 logback-spring.xml + LogstashEncoder 直接写 JSON,不要依赖日志收集器把纯文本正则转 JSON——多行堆栈和转义会经常解析失败。logging.pattern.console 只用于本地开发;一旦自定义了 logback 配置,这些 pattern 属性就不再生效。

采样率调整:

management:
  tracing:
    sampling:
      probability: 0.1      # 生产 10%
  # Boot 3 的前缀是 management.prometheus.*,Boot 2 的 management.metrics.export.prometheus 已失效
  prometheus:
    metrics:
      export:
        enabled: true       # 引入 micrometer-registry-prometheus 后默认开启

安全检查

  • 数据库密码从环境变量/密钥管理器读取,不硬编码
  • Redis 有密码认证(生产关闭无密码模式)
  • JWT secret 长度 ≥ 256 bit,定期轮换
  • CORS allowed-origins 显式列出,不包含 *
  • TLS 1.2+,证书有效期 > 30 天
  • 敏感接口(管理后台)限制 IP 白名单
  • actuator 端点不对外暴露(/actuator/prometheus 放在内网或单独端口)

监控告警基线

下面是 Prometheus 规则文件的结构,不是任意的 YAML 列表:

groups:
  - name: eventd
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
          /
          sum(rate(http_server_requests_seconds_count[5m])) > 0.01
        for: 2m

      - alert: HighLatency
        expr: |
          histogram_quantile(0.99,
            sum by (le) (rate(http_server_requests_seconds_bucket[5m]))) > 0.5
        for: 5m

      - alert: R2dbcPoolPending
        expr: r2dbc_pool_pending > 0
        for: 1m

      - alert: CircuitBreakerOpen
        expr: resilience4j_circuitbreaker_state{state="open"} == 1
        for: 0m
  • Spring Boot 的 HTTP 指标是 http_server_requests_seconds_*,不是 http_requests_total
  • R2DBC 连接池指标来自 Boot 对 PoolMetrics 的绑定,name 形如 r2dbc_pool_acquired、r2dbc_pool_pending、r2dbc_pool_max_allocated
  • 熔断器的 state 是标签,不是数值枚举,必须用 {state="open"} 选择
  • 错误率用「5xx 速率 / 总速率」,单看 5xx 速率会在低流量时误报

备份与恢复

  • 数据库:每日全量 + WAL 增量
  • Redis:AOF 持久化 + 定期 RDB
  • 配置:Git 版本控制,变更审计
  • 密钥:专用密钥管理系统(HashiCorp Vault)

小结

上线前核查列表(打印贴墙):
□ 线程池配置确认
□ 连接池大小 ≤ 数据库容量 × 0.8 ÷ 实例数(并为迁移/运维留连接)
□ 超时层级正确(父 > 子之和)
□ JVM GC 参数优化
□ 日志级别 INFO,采样率调整
□ 密码/密钥不硬编码
□ CORS 不开放 *
□ TLS 证书有效
□ 告警规则配置
□ 备份策略就绪

下一节 14.2 故障排查 :线程分析、连接泄漏、背压溢出、OOM 诊断。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 15.3 全书总结与学习路径回顾
  2. 15.2 响应式流处理的演进趋势
  3. 15.1 引擎选型指南