Micronaut响应式ReactorHttpClient异常处理方案选型咨询
场景背景
技术栈为Micronaut + Kotlin + Project Reactor,搭建两层结构的非阻塞API服务:
- Controller层
PaymentController暴露POST接口makePayment,方法签名为fun makePayment(payment: Payment): Mono<PaymentDetails> - 接口调用Service层
PaymentService的同名方法执行业务逻辑 - Service层通过
io.micronaut.reactor.http.client.ReactorHttpClient发起对银行端支付接口的非阻塞调用
需要确认该场景下符合响应式规范的异常处理最优实践,同时明确@Error(global = true)全局异常处理器的运行模式。
现有可选方案
方案1:响应式链路内直接处理
在ReactorHttpClient发起调用的响应式链上直接挂载异常处理逻辑:
reactorHttpClient.exchange().onErrorResume()
在onErrorResume实现中把异常映射为带对应HTTP状态码的自定义错误响应,不额外配置全局异常处理逻辑。
方案2:全局异常处理器统一处理
基于@Error(global = true)注解实现全局异常处理器,统一捕获全链路抛出的所有异常做转换返回,和Spring的ControllerAdvice思路一致。
两种方案实测都能正常返回结果,需要结合非阻塞特性、响应式流规范判断最优解。
最优实践说明
推荐采用局部业务异常就近处理 + 全局异常兜底的组合方案,不要单独使用任意一种:
- 针对调用银行支付接口这类有明确业务语义的异常场景(比如银行端返回的支付失败、超时、签名错误等和当前业务强绑定的错误),直接在调用链上用
onErrorResume处理是最符合响应式设计原则的:异常处理逻辑完全在Reactor的异步调度链路内执行,不会打破非阻塞执行模型,逻辑和业务调用点强关联,内聚性更强,后续维护时不需要跨类找对应的错误映射规则。 - 全局异常处理器作为兜底层,统一处理参数校验异常、框架层抛出的异常、业务代码未覆盖的意料外异常,避免重复编写通用异常的转换逻辑,也防止未捕获异常直接透传返回框架默认的错误格式。
如果只使用局部onErrorResume处理,会导致大量通用异常的处理逻辑重复散落在各个业务方法中;如果只使用全局异常处理器,会把特定业务的错误判断逻辑和业务代码割裂,维护成本高。
@Error(global = true)运行模式说明 该注解实现的全局异常处理器不强制阻塞或非阻塞,运行模式由方法返回值决定:
- 如果异常处理方法返回
Mono/Flux这类响应式发布者类型,Micronaut会自动按非阻塞响应式模式调度执行,全程不会阻塞事件循环线程,完全适配响应式栈的要求 - 如果异常处理方法返回普通的非响应式Java/Kotlin对象,框架会走常规阻塞调度逻辑执行。
在全链路非阻塞的场景下编写全局异常处理器时,只需要让方法返回响应式类型即可保证非阻塞特性。
内容的提问来源于stack exchange,提问作者vinilpj
相关产品推荐
相关产品推荐

