countdownlatch是最直接可靠的本地协调方案:主线程按确定组件数初始化latch,各组件异步初始化并在finally中无条件countdown,主线程带超时await,超时或失败时清理已初始化资源并降级,全部倒数完成且校验通过后才启动核心服务。

在微服务启动阶段,让 Redis 客户端和数据库连接池“并行初始化、齐备再启服务”,CountDownLatch 是最直接可靠的本地协调方案。关键不是等得久,而是等得准、退得稳。
初始化一个共享的 CountDownLatch 实例
主线程在 Spring Boot 的 ApplicationRunner 或自定义启动 Bean 中,根据确定的本地依赖数量创建实例。比如明确要初始化 RedisTemplate 和 HikariDataSource 两个组件,就写:
- new CountDownLatch(2) —— 数值必须静态可得,不能靠运行时探测动态调整
- 把这个 latch 实例通过构造参数、方法传参或 Spring 上下文注入方式,交给 Redis 初始化逻辑和 DB 初始化逻辑共用
- 切忌每个组件自己 new 一个 latch,否则主线程 await 的对象没人通知,必然卡死
各组件异步初始化,并在 finally 块中 countDown
Redis 和 DB 的初始化都应提交到独立线程(如通过 TaskExecutor),且必须保证无论成功失败都执行 countDown():
- DB 初始化示例:提交任务后,在 try-catch-finally 中,init() 成功则记录日志,异常则打 ERROR 级别日志,finally 里无条件 latch.countDown()
- Redis 初始化同理:连接工厂 build() 或 setConnectionFactory() 后触发 ping 测试,失败也不跳过 countDown
- 这样即使某个组件初始化失败,主线程也不会永久阻塞,而是能及时超时处理或降级启动
主线程带超时调用 await,失败后主动清理
所有初始化任务提交完毕后,主线程立即调用带超时的 await:
- 推荐 latch.await(10, TimeUnit.SECONDS) —— 太短容错不足,太长拖慢就绪时间
- 返回 false(超时)时,应主动关闭已成功初始化的资源:比如调用 HikariDataSource.close()、RedisConnectionUtils.unbindConnection()
- 抛出 RuntimeException 或标记应用为 FAILED 状态,避免“半初始化”服务对外提供不可靠能力
- 若被中断(InterruptedException),需恢复中断状态:Thread.currentThread().interrupt(),然后退出
全部倒数完成才启动核心服务逻辑
await 正常返回(true)仅表示两个组件都完成了初始化流程,不代表 100% 可用:
- 建议在 DB 初始化内部做一次 connection.isValid() 校验,在 Redis 初始化后执行 redisTemplate.getConnectionFactory().getConnection().ping()
- 只有这两个校验都通过,才认为“齐备”;任一失败,仍应 countDown 并由主线程统一兜底
- 之后才启动 WebMvcConfigurer、注册 Actuator HealthIndicator、开启消息监听容器等后续流程











