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

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

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
相关推荐
星浩AI7 分钟前
AI 并不懂文字,它只认向量:一文搞懂 Embedding
后端
程序员博博8 分钟前
这才是vibe coding正确的打开方式 - 手把手教你开发一个MCP服务
javascript·人工智能·后端
90后的晨仔15 分钟前
阿里云服务器如何给子账号设置指定具体的那一台服务器?
后端
期待のcode35 分钟前
springboot热部署
java·spring boot·后端
expect7g36 分钟前
Paimon源码解读 -- FULL_COMPACTION_DELTA_COMMITS
大数据·后端·flink
踏浪无痕1 小时前
周末拆解:QLExpress 如何做到不编译就能执行?
后端·算法·架构
222you1 小时前
Spring框架的介绍和IoC入门
java·后端·spring
用户6151265617331 小时前
Java生态新纪元:虚拟线程、模式匹配与未来的编程范式
后端
风雨同舟的代码笔记1 小时前
Java并发编程基石:深入解析AQS原理与应用实战
后端
曾富贵1 小时前
【后端进阶】并发竞态与锁选型
后端