Skip to content

feat: 调整trpc生命周期 - #153

Merged
wardseptember merged 1 commit into
masterfrom
feat/lifecycle_order
Nov 4, 2026
Merged

wardseptember merged 1 commit into
masterfrom
feat/lifecycle_order

Conversation

@wardseptember

@wardseptember wardseptember commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

把 tRPC 的启动和停止拆成两个监听器:启动推迟到 LOWEST - 20,等 RefreshScope 等 refresh监听器执行完;停止仍在 HIGHEST + 20000 最先执行。springmvc 路由导出也改成 tRPC 启动后才做;同时只处理所属上下文的事件,启动失败(含 Error)时回滚状态,用 destroy() 兜底停止,启动标记改为实例级。

… 监听器执行完;停止仍在 HIGHEST + 20000 最先执行。springmvc 路由导出也改成 tRPC 启动后才做;同时只处理所属上下文的事件,启动失败(含 Error)时回滚状态,用 destroy() 兜底停止,启动标记改为实例级。
@codecov

codecov Bot commented Nov 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.16375%. Comparing base (64a4d5e) to head (ba208d7).

Additional details and impacted files

Impacted file tree graph

@@                  Coverage Diff                  @@
##                master        #153         +/-   ##
=====================================================
- Coverage     86.03810%   84.16375%   -1.87436%     
- Complexity        4348        4397         +49     
=====================================================
  Files              436         461         +25     
  Lines            14382       14852        +470     
  Branches          1287        1328         +41     
=====================================================
+ Hits             12374       12500        +126     
- Misses            2008        2352        +344     
Files with missing lines Coverage Δ
...arters/listener/TRpcApplicationFailedListener.java 100.00000% <100.00000%> (ø)
...tencent/trpc/spring/context/TRpcConfiguration.java 100.00000% <100.00000%> (ø)
...cent/trpc/spring/context/TRpcLifecycleManager.java 100.00000% <100.00000%> (+37.50000%) ⬆️
...cent/trpc/spring/context/TRpcShutdownListener.java 100.00000% <100.00000%> (ø)
...cent/trpc/springmvc/TRpcServiceExportListener.java 100.00000% <100.00000%> (ø)
...cent/trpc/springmvc/TRpcServiceHandlerMapping.java 100.00000% <ø> (ø)
...trpc/springmvc/TRpcSpringMvcAutoConfiguration.java 100.00000% <100.00000%> (ø)

... and 24 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wardseptember

Copy link
Copy Markdown
Collaborator Author

一、核心目的:调整 tRPC 的启动和停止时机

原来的问题

TRpcLifecycleManager 在 ContextRefreshedEvent 时启动 tRPC,order 是 HIGHEST_PRECEDENCE + 20000,几乎排在最前面。所以 tRPC 开始接流量时,其他 refresh 监听器还没执行。例如 Spring Cloud RefreshScope 的预初始化(order 是 LOWEST - 100),这时请求可能会访问到还没准备好的 bean。

改法

把启动和停止拆成两个监听器,分别用不同的 order:

事件 监听器 order 作用
ContextRefreshedEvent TRpcLifecycleManager LOWEST - 20 尽量晚启动,等 RefreshScope 等监听器执行完
ContextRefreshedEvent TRpcServiceExportListener(新增) LOWEST - 10 在 tRPC 启动后导出 rest 路由
ContextClosedEvent TRpcShutdownListener(新增) HIGHEST + 20000 和原来一样尽早停止:先注销、等待,再停服务,然后才轮到 Tomcat 和业务 bean 关闭

原来一个监听器同时处理两个事件,只能用同一个 order,没法做到"启动要晚、停止要早",所以需要拆开。

二、springmvc 导出逻辑独立出来

原来:TRpcServiceHandlerMapping 自己监听 ContextRefreshedEvent 来导出路由。它的 getOrder() 本来是 HandlerMapping 的优先级(HIGHEST + 50000),却同时被当成了监听器的执行顺序。

问题:tRPC 启动改到 LOWEST - 20 之后,导出会比启动先执行。这时 ProviderConfig 的 ref 还没替换成 Spring bean,导出的路由是错的。

改法

  • TRpcServiceHandlerMapping 不再实现 ApplicationListener,原来的 onApplicationEvent 改成公开方法 exportTRpcServices()。HandlerMapping 自身的优先级不变。
  • 新增 TRpcServiceExportListener,order 为 LOWEST - 10,并且只在 tRPC 已经启动时才调用导出。
  • TRpcSpringMvcAutoConfiguration 注册这个监听器,通过 ObjectProvider 获取 TRpcLifecycleManager,拿不到时跳过导出。

三、健壮性修复(第 1、3、4、5 点)

1. 只处理自己所属上下文的事件

  • 原来只用 isAwareContext 判断 context id 是否以应用名开头。actuator 配置了独立管理端口时会创建子上下文(id 是 xxx:management),它会比父上下文更早 refresh。子上下文的事件冒泡上来也能通过这个判断,导致 tRPC 被提前启动,第一部分的 order 调整就失效了。
  • 现在 TRpcLifecycleManager 会记住自己所在的上下文,新增 isManagedContext(),要求事件必须来自这个上下文。另外两个监听器也都用它来判断。

3. 启动失败时回滚状态

  • catch 从 RuntimeException 改为 Throwable。原来如果抛出 NoClassDefFoundError 这类 Error,启动标记会一直停在 true,后面再 stop 会打出 "Lifecycle stop ignore" 的 ERROR 日志。

4. refresh 失败时的兜底停止

  • TRpcLifecycleManager 实现了 DisposableBean,在 destroy() 里调用 stopIfStarted()。
  • 原因:tRPC 启动后如果 refresh 失败,Spring 不会发 ContextClosedEvent,而是先销毁 bean,再发 ApplicationFailedEvent。中间这段时间 tRPC 还在用已销毁的 bean 处理请求;非 Spring Boot 场景更是根本没有人去停 tRPC。

5. 启动标记 started 去掉 static

  • 改为实例字段,isStarted() 和 stopIfStarted() 也改为实例方法,状态跟着各自的 manager 实例走。
  • 因此 TRpcApplicationFailedListener 去掉了原来进程级的 PROCESSED 标记,改成从失败的上下文里找已经创建好的 TRpcLifecycleManager 单例,再调用它的 stopIfStarted()。这里用 getSingleton,不会触发新建 bean;上下文的 bean 工厂已不可用时直接跳过。

四、文件清单

修改的文件

  • TRpcLifecycleManager:只负责启动,order 改为 LOWEST - 20,加入所属上下文判断、Throwable 回滚、destroy() 兜底,标记改为实例级。
  • TRpcConfiguration:注册 TRpcShutdownListener,并注入 TRpcLifecycleManager。
  • TRpcServiceHandlerMapping:去掉监听器身份,改为提供 exportTRpcServices()。
  • TRpcSpringMvcAutoConfiguration:注册 TRpcServiceExportListener。
  • TRpcApplicationFailedListener:改为通过已有的 manager 单例停止 tRPC。

新增的文件

  • 源码:TRpcShutdownListener、TRpcServiceExportListener
  • 测试:
    • TRpcLifecycleManagerTest:用 Mockito mock ConfigManager,只能在 JDK 8 下运行
    • TRpcShutdownListenerTest
    • TRpcServiceExportListenerTest
    • TRpcServiceHandlerMappingTest
    • TRpcSpringMvcAutoConfigurationTest
    • TRpcApplicationFailedListenerTest

五、验证情况

  • JDK 8 下三个模块的测试分别通过 104、4、15 个。
  • 涉及的 7 个类,行、分支、指令覆盖率都是 100%。
  • 用项目的 checkstyle 配置检查新增和改动的文件,没有告警。
  • TRpcHttpTests 端到端 13 个用例全部通过,日志显示 rest 服务是在 tRPC 启动之后才导出的。本机 12345 端口被占用,这次是在 target/test-classes 下的配置副本里把端口临时改成 12355 跑的,源码没动。
  • 另外查了 trpc-java-tencent 仓库:没有代码依赖这几个类或这几个事件,那里的 order 也都不在同一个阶段,不受影响。

六、对使用方的影响(建议写进 CHANGELOG)

  • 执行顺序变化:order 落在 HIGHEST + 20000 到 LOWEST - 20 之间的用户 ContextRefreshedEvent 监听器(例如 @Order(0) 的),原来在 tRPC 启动之后执行,现在改为在启动之前。依赖"服务已经注册、端口已经监听"的逻辑需要留意。
  • API 变化:
    • TRpcServiceHandlerMapping 不再是监听器,onApplicationEvent 被删除。
    • TRpcLifecycleManager 不再处理关闭事件,从 SmartApplicationListener 改为 ApplicationListener<ContextRefreshedEvent>。
    • 自定义了这两个类的子类、或者手动注册 TRpcLifecycleManager 的用户需要调整。
  • 失败处理变化:用户自己手动调用 ConfigManager.start() 启动的 tRPC,应用启动失败时不会再被这个监听器停掉。

@wardseptember wardseptember changed the title feat: 把 tRPC 的启动和停止拆成两个监听器:启动推迟到 LOWEST - 20,等 RefreshScope 等 refresh… feat: 调整trpc生命周期 Nov 2, 2026
@wardseptember
wardseptember merged commit 3f745b8 into master Nov 4, 2026
11 checks passed
@wardseptember
wardseptember deleted the feat/lifecycle_order branch November 4, 2026 03:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant