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

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

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
相关推荐
码事漫谈3 小时前
C++死锁深度解析:从成因到预防与避免
后端
码事漫谈3 小时前
智能体颠覆教育行业:现状、应用与未来展望调研报告
后端
蓝-萧3 小时前
【玩转全栈】----Django基本配置和介绍
java·后端
priority_key3 小时前
排序算法:堆排序、快速排序、归并排序
java·后端·算法·排序算法·归并排序·堆排序·快速排序
韩立学长3 小时前
基于Springboot的旧时月历史论坛4099k6s9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
汤姆yu4 小时前
基于SpringBoot的动漫周边商场系统的设计与开发
java·spring boot·后端
灰小猿5 小时前
Spring前后端分离项目时间格式转换问题全局配置解决
java·前端·后端·spring·spring cloud
RedJACK~6 小时前
Go Ebiten小游戏开发:扫雷
开发语言·后端·golang
老夫的码又出BUG了6 小时前
分布式Web应用场景下存在的Session问题
前端·分布式·后端
L.EscaRC8 小时前
Spring Boot 自定义组件深度解析
java·spring boot·后端