0 来张图
1 首先定义远程服务提供的服务
1.1 定义接口
/**
* Created by haoran_10 on 2017/8/6.
* 远程服务
*/
public interface RpcService {
/**
* 执行服务
* @param inParam
* @return
*/
public Result<T> execute(ContextDTO contextDTO);
/**
* 取消服务
* @param contextDTO
* @return
*/
public Result<T> cancel(ContextDTO contextDTO);
}
1.2 返回结果
- 成功结果
- 业务类型错误结果
- 不可预知系统错误结果
1.3 远程服务要求
- 执行服务&取消服务 都要求幂等性
2.调用远程服务
public boolean doDistributedTransaction(ContextDTO contextDTO){
try{
//1.执行本地服务
callLocalService(contextDTO);
//2.执行远程服务A
try{
//2.1 调用远程服务
Result rpcA = rpcServiceA.execute(contextDTO);
//2.1.1 业务结果错误,直接返回
if(rpcA.businessError()){
log.error("call rpc execute business error,rpcA:{}",rpcA);
//抛出回滚
throw RollbackException(contextDTO);
}
//2.1.2 系统结果错误,加入重试任务
if(rpcA.systemError()){
log.error("call rpc execute system error,rpcA:{}",rpcA);
saveRetryTask(contextDTO);
}
//2.1.3 调用成功,继续下面的远程调用
if(rpcA.success()){
}
}catch(Exception rpcAException){
//2.1.4 捕获异常,执行重试
log.error("call rpc execute is error,{}",rpcAException);
saveRetryTask(contextDTO);
}
//3. 执行远程服务B
Result rpcB = rpcServiceB.confirm(contextDTO);
//....
//n. 执行远方服务N
//...
}catch(Exception e){
//记录错误日志
log.error("doDistributedTransaction is error,contextDTO:{};e:{}",contextDTO,e);
//如果回滚异常,回滚服务
if(e instanceof RollbackException){
rollBack(contextDTO);
}else{
saveRetryTask(contextDTO);
}
}
}
- 要求请求远程服务时,由调用方生成唯一key,确保业务唯一性
3.回滚服务
public boolean rollBack(ContextDTO contextDTO){
rpcServiceA.cancel(contextDTO);
rpcServiceB.cancel(contextDTO);
rpcServiceN.cancel(contextDTO);
}
- 这里要求取消必定成功,如果不成功,可以加入重试任务服务重试回滚
4.重试服务
//异步式执行重试任务
public void retryTaskCenter(){
int defineRetryTimes = 3;//定义重试次数
while(true){
//查询出需要重试任务的数据
List<RetryTask> list = queryRetryTask(queryParam);
for(RetryTask retryTask:list){
ContextDTO contextDTO = buildContextDTOFromRetryTask(retryTask);
int retryTimes = 0;
while(retryTimes<defineRetryTimes){
//继续执行远程服务
//这里可根据状态定位到执行调用哪一个远程服务,避免每次重复调用
doDistributedTransaction(contextDTO);
retryTimes++;
}
}
}
}
5.使用场景
- 调用链路较短
- 要求分布式强一致性
如果时调用链路非常长,则非常推荐使用一致性消息机制