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

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

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
相关推荐
怒放吧德德23 分钟前
Python3基础:基础实战巩固,从“会用”到“活用”
后端·python
苏三说技术1 小时前
xxl-job 和 elastic-job,哪个更好?
后端
三小河1 小时前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
三小河2 小时前
前端视角详解 Agent Skill
前端·javascript·后端
牛奔2 小时前
Go 是如何做抢占式调度的?
开发语言·后端·golang
颜酱2 小时前
二叉树遍历思维实战
javascript·后端·算法
爱装代码的小瓶子2 小时前
【C++与Linux基础】进程间通讯方式:匿名管道
android·c++·后端
程序员良许2 小时前
嵌入式处理器架构
后端·单片机·嵌入式
MrSYJ3 小时前
Redis 做分布式 Session
后端·spring cloud·微服务
Cache技术分享3 小时前
318. Java Stream API - 深入理解 Java Stream 的中间 Collector —— mapping、filtering 和 fla
前端·后端