服务间调用的一个实用结果类

服务间调用的一个实用结果类

Feign 跨服务调用异常封装与结果处理设计

📌 背景

在实现账号管理功能中,涉及调用外部服务(如注册账号等)。由于跨服务调用异常常常无法直接被调用方感知,导致调用失败但本地业务依然继续执行,产生了数据一致性问题。

我们希望通过一种 轻量、安全、可控 的方式,增强跨服务调用的"原子性约束"和错误感知能力。


💡 核心思路

1. 封装统一的返回结构体 RemoteResponse<T>

  • 统一异常处理方式,避免业务逻辑中手动 try-catch;
  • 通过 execute 方法包裹调用逻辑;
  • 使用 assertSuccessonSuccess 控制业务流程;
  • 提供静态方法处理无返回值逻辑。

2. 强制在服务实现中优先处理调用逻辑

  • 外部服务调用优先执行,如失败则阻断本地逻辑。
  • 统一通过 .assertSuccess() 捕获异常,避免后续业务脏写。

🧱 封装类定义:FeignBaseResponse

typescript 复制代码
@Slf4j
@Data
public class RemoteResponse<T> {

    /**
     * 错误信息,非空表示远程调用失败
     */
    private String errorMessage;

    /**
     * 返回的业务数据结果
     */
    private T result;

    /**
     * 包裹业务执行逻辑,并捕获可能的异常
     */
    public RemoteResponse<T> execute(Supplier<T> supplier) {
        try {
            this.result = supplier.get();
        } catch (Exception e) {
            log.error("Remote execution failed: {}", e.getMessage(), e);
            this.errorMessage = e.getMessage() != null ? e.getMessage() : "Unknown error";
        }
        return this;
    }

    /**
     * 若结果存在,执行回调逻辑(消费函数)
     */
    public void onSuccess(Consumer<T> consumer) {
        if (this.result != null) {
            consumer.accept(this.result);
            this.result = null; // 防止多次处理
        }
    }

    /**
     * 若存在错误,则抛出 ServiceException 终止业务流程
     */
    public void assertSuccess() {
        if (CharSequenceUtil.isNotBlank(errorMessage)) {
            throw new ServiceException(errorMessage);
        }
    }

    /**
     * 静态工具方法:用于判断远程返回是否为 "success"
     */
    public static boolean assertSuccess(String message) {
        if (!Objects.equals("success", message)) {
            throw new ServiceException(message);
        }
        return true;
    }

    /**
     * 用于执行无返回值的远程逻辑(如写操作),异常则返回错误信息
     */
    public static String tryExecute(Consumer<Void> consumer) {
        String msg = "success";
        try {
            consumer.accept(null);
        } catch (Exception e) {
            log.error("Remote void execution failed: {}", e.getMessage(), e);
            msg = e.getMessage() != null ? e.getMessage() : "Unknown error";
        }
        return msg;
    }
}

🛠 使用示例

➤ 被调用服务端实现

java 复制代码
@Override
public RemoteResponse<Long> createAccount(AccountCreateParam param) {
    return new RemoteResponse<Long>().execute(() -> {
        validate(param); // 参数校验
        AccountEntity entity = accountService.create(param);
        if (entity == null) {
            throw new ServiceException("账号创建失败");
        }
        return entity.getId();
    });
}

➤ 调用方代码

ini 复制代码
RemoteResponse<Long> response = accountClient.createAccount(param);

// 判断是否成功
response.assertSuccess();

// 若成功则消费返回值
response.onSuccess(accountId -> {
    tenantBinder.bindAccount(accountId);
});

✅ 方案优势总结

优点 说明
🎯 异常感知增强 所有调用失败统一写入 errorMessage,避免静默失败
🔄 结构清晰 统一接口返回结构,便于日志收集与调用链跟踪
✂️ 逻辑解耦 异常处理与主流程分离,避免在业务逻辑中写大量 try-catch
📦 支持链式调用 支持 executeassertSuccessonSuccess 的清晰逻辑链
🔧 可扩展性强 可扩展字段如 statusCode, traceId, debugInfo
相关推荐
PanZonghui3 分钟前
Centos项目部署之安装数据库MySQL8
linux·后端·mysql
Victor3566 分钟前
MySQL(119)如何加密存储敏感数据?
后端
用户39661446871917 分钟前
TypeScript 系统入门到项目实战-慕课网
后端
guojl22 分钟前
Dubbo SPI原理与设计精要
后端
Lemon程序馆24 分钟前
搞懂 GO 的垃圾回收机制
后端·go
用户81221993672234 分钟前
React18+Next.js14+Nest.js全栈开发复杂低代码项目-仿问卷星
后端
喜欢吃豆38 分钟前
目前最火的agent方向-A2A快速实战构建(二): AutoGen模型集成指南:从OpenAI到本地部署的全场景LLM解决方案
后端·python·深度学习·flask·大模型
寻月隐君42 分钟前
Rust 网络编程实战:用 Tokio 手写一个迷你 TCP 反向代理 (minginx)
后端·rust·github
汪子熙1 小时前
理解 git checkout 与 git reset 的联系和区别
后端