背景问题
一个 Java 应用里往往同时有连接池、限流器、线程池、分布式锁,每一种都有自己的 API、配置风格和监控方式:N 种资源就是 N 套 API、N 种配置、N 种监控。出问题时要同时看 Hikari 的 MBean、限流计数和线程栈,每一层都是孤岛。
MetaPool 不重造连接池,而是在 HikariCP、Bucket4j、Redisson 等成熟库之上做一层治理控制面:统一的是生命周期、可观测、动态调参与优雅停机,而不是用法。
架构
点击或聚焦节点,查看组件说明与连接关系
连接关系
组件说明
- ResourceManager
- 控制面:注册表 + 编排 + 聚合 health;统一 metrics、动态调参与逆序优雅停机。
- ManagedResource
- 所有资源都实现的治理契约:生命周期(start / stop / health)+ 指标(统一 tag)。
- Tunable
- 可选能力:白名单动态调参,启动期校验,不支持的 key 直接 fail-fast。
- Pool<T>
- 可选能力:借还。只有真正有借还语义的资源才实现,编译期隔离,根除 LSP 破坏。
- RateLimiter
- 可选能力:限流。Bucket4jAdapter 不实现 Pool,instanceof Pool 编译不过。
- DistributedLock
- 可选能力:加锁只发放持有凭证,不提供 unlock(key),避免解掉别人的锁。
- ManagedExecutor
- 可选能力:extends Executor 但绝不 extends ExecutorService,不开第二个停机入口。
- ResourceAdapterFactory · SPI
- JDK ServiceLoader 扩展点:类路径多一个 adapter jar 就多支持一种资源,核心零改动。
关键设计决策与取舍
推翻自己的 1.0:从「自研资源池」转向「治理成熟库」
1.0 尝试自研 7 类资源池。JMH 实测自研线程池的吞吐只有 JDK 的 48%;而且用一个 acquire / release 接口硬套所有资源,导致不支持借还的资源只能抛 UnsupportedOperationException,这是典型的里氏替换破坏。2.0 放弃自研,转为治理成熟库。
能力接口可选,并在编译期隔离
把 borrow / release、tryAcquire 等功能性 API 从统一契约中剥离为可选能力接口,谁有谁实现:Bucket4jAdapter 不实现 Pool,instanceof Pool 在编译期就通不过。反过来同样严格:Redis 适配器不实现 Pool(单连接多路复用没有借还语义),Netty 适配器实现 Pool 但注明 release 是引用计数减一——语义更强可以映射并注明,语义不存在只能靠撒谎才能映射。
SPI 扩展,核心零改动
扩展点使用 JDK ServiceLoader:类路径上多一个 adapter jar,就多支持一种资源。此后五次接入新资源(线程池、分布式锁、对象池、Redis、堆外内存),metapool-core 与 metapool-common 均零改动,可以用 commit 核验。
参数直通底层库,调参统一为一个门面
配置直接使用底层库的原生参数名(如 Hikari 的 maximum-pool-size),不发明第二套 DSL。运行时调参把 HikariConfigMXBean 与 Bucket4j 的 replaceConfiguration 两套异构 API 统一为 apply(patch),只允许白名单中的参数;白名单里写了不支持的参数,启动时就报错,而不是等到调参时才拒绝。
我的角色
独立开发
负责设计、实现、测试、发布与持续集成的全部工作。
结果与指标
- 12 个构件(含 BOM)发布到 Maven Central,从 2.0.0 到 2.4.0 共 6 个版本
- 193 个测试,有 Docker 时 0 跳过;另有从 Central 拉取正式构件的使用方冒烟测试
- JMH 实测治理开销:限流 +1.75%,连接借还约 20 ns 的常量开销,真实 PostgreSQL 场景下 < 0.1%
- GitHub Actions CI:ubuntu、windows × JDK 17