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

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

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
相关推荐
码事漫谈1 小时前
C++模板元编程从入门到精通
后端
_風箏1 小时前
Java【代码 14】一个用于判断磁盘空间和分区表是否需要清理的工具类
后端
_風箏1 小时前
Java【代码 13】前端动态添加一条记后端使用JDK1.8实现map对象根据key的部分值进行分组(将map对象封装成指定entity对象)
后端
_風箏1 小时前
Java【代码 12】判断一个集合是否包含另一个集合中的一个或多个元素 retainAll() 及其他方法
后端
Java中文社群1 小时前
Coze开源版?别吹了!
人工智能·后端·开源
懂得节能嘛.2 小时前
【SpringAI实战】ChatPDF实现RAG知识库
java·后端·spring
站大爷IP2 小时前
Python爬虫库性能与选型实战指南:从需求到落地的全链路解析
后端
小杰来搬砖2 小时前
在 Java 的 MyBatis 框架中,# 和 $ 的区别
后端
wenb1n2 小时前
【安全漏洞】隐藏在HTTP请求中的“隐形杀手”:Host头攻击漏洞深度剖析
java·后端
snakeshe10102 小时前
Java开发中的最佳实践与代码优化技巧
后端