Redis,資料庫,MQ,檔案服務等 多個資源的業務操作事務一致性思路
public interface Service{
//業務方法
doBusiness();
//資料庫更新方法
doBusinessTX();
}
@Service
public class ServiceImpl implements Service,ApplicationContextAware{
//自己注入自己
@Autowired
private Service ServiceProxy;
//或者使用Aware介面獲取BeanFactory獲取當前物件事務代理物件
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException{
this.Service = ctx.getBean(Service.class);
}
@Transactional(rollback=RuntimeException.class)//預設RuntimeException和虛擬機器Error回滾
public void doBusinessTX(){
資料庫更新 可丟擲RuntimeException 自動觸發資料庫回滾
return;
}
public void operateRedis(){
//redisTemplate.eval 涉及多個KEY操作使用LUA指令碼
}
public void operateRedisRollback(){
//redisTemplate.eval 執行LUA指令碼 多個KEY同時操作 原子執行 回退redis修改
}
public void operateMQ() throws MQException{
//傳送MQ 捕捉RuntimeException 轉換為MQException再丟擲 其他異常轉換為MQException
}
@Override
public void doBusiness(){
try{
this.operateRedis();//先操作redis
this.ServiceProxy.doBusinessTX();//再操作資料庫,代理物件呼叫事務方法 方法結束事務提交
this.operateMQ();//最後傳送MQ 傳送失敗需重發 重發仍失敗丟擲MQException
}catch(RuntimeException e){
//執行時異常 事務方法丟擲後 資料庫已回滾 需手動回滾redis
this.operateRedisRollback();
}catch(MQException me){
//MQException 後發MQ,如果時先發MQ,則可能涉及資料庫事務失敗再發送撤銷MQ,可能觸發MQ關聯絡統回滾,增加複雜性,如先發MQ最後確認,則資料庫事務期間佔用MQ連結而不確認,實際效果同後發MQ,如後發MQ重試仍傳送失敗,通常業務狀態置為異常,需手動衝正或非同步定時任務處理或延遲傳送,如果還有檔案伺服器的操作,一般不能將耗時IO操作放在資料庫事務中,可非同步調起檔案服務操作,並可使用類似訊號量限制IO。
}finally{
}
}
}