线程池检查
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 诊断。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。