服务间调用的一个实用结果类
Feign 跨服务调用异常封装与结果处理设计
📌 背景
在实现账号管理功能中,涉及调用外部服务(如注册账号等)。由于跨服务调用异常常常无法直接被调用方感知,导致调用失败但本地业务依然继续执行,产生了数据一致性问题。
我们希望通过一种 轻量、安全、可控 的方式,增强跨服务调用的"原子性约束"和错误感知能力。
💡 核心思路
1. 封装统一的返回结构体 RemoteResponse<T>
- 统一异常处理方式,避免业务逻辑中手动 try-catch;
- 通过
execute
方法包裹调用逻辑; - 使用
assertSuccess
和onSuccess
控制业务流程; - 提供静态方法处理无返回值逻辑。
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 |
📦 支持链式调用 | 支持 execute → assertSuccess → onSuccess 的清晰逻辑链 |
🔧 可扩展性强 | 可扩展字段如 statusCode , traceId , debugInfo 等 |