public async Task InvokeAsync(StepCallback branchStep, FormType sagaData) { // 注册branch tx id var branchStepInfo = getBranchStepInfo(branchStep); var branchTxId = await _sagaCollaborator.CreateBranchTxAsync(_xid, branchStepInfo.BranchServiceKey, branchStepInfo.BranchCompensationServiceKey); // 本次执行的jobId var jobId = Guid.NewGuid().ToString(); _logger.LogInformation($"created branch txid {branchTxId} jobId {jobId} in xid {_xid}"); try { // invoke branchStep await branchStep(sagaData); // 调用成功,通知saga server状态变化 var oldBranchTxDetail = await _sagaCollaborator.QueryBranchTxAsync(branchTxId); var sagaDataBytes = _sagaDataConverter.Serialize(sagaData.GetType(), sagaData); var newState = await _sagaCollaborator.SubmitBranchTxStateAsync(_xid, branchTxId, oldBranchTxDetail.Detail.State, saga_server.TxState.Committed, oldBranchTxDetail.Detail.Version, jobId, "", sagaDataBytes); _logger.LogInformation($"branch txid {branchTxId} state changed to {newState}"); } catch (Exception e) { _logger.LogError($"invoke branch service {branchStepInfo.BranchServiceKey} error", e); // 如果有异常,通知saga server回滚后台执行回滚,然后再抛出异常 var oldBranchTxDetail = await _sagaCollaborator.QueryBranchTxAsync(branchTxId); await _sagaCollaborator.SubmitBranchTxStateAsync(_xid, branchTxId, oldBranchTxDetail.Detail.State, saga_server.TxState.CompensationDoing, oldBranchTxDetail.Detail.Version, jobId, e.Message, null); throw e; } return; }
public async Task <string> Start <T>(T form) where T : class, SagaData { var xid = await _sagaCollaborator.CreateGlobalTxAsync(); _logger.LogInformation($"created xid {xid}"); // bind xid to current call context CallContext.SetData(SagaGlobal.SAGA_XID_CONTEXT_KEY, xid); // 用一个branchCaller服务去带着xid和sagaData去调用现有的SagaService的方法, // 从而包装好分支事务的注册 _sagaSession = new SagaSession <T>(xid, _sagaCollaborator, _sagaDataConverter, _sagaResolver, _logger); CallContext.SetData(SagaGlobal.SAGA_SESSION_CONTEXT_KEY, _sagaSession); // 上面这里的CallContext.SetData后的值下个线程就取不到了. 可能是因为不在同一个async CPS中?. 所以返回后要调用Bind() // 初始化saga data避免以后回滚时得到null sagaData await _sagaCollaborator.InitSagaDataAsync(xid, _sagaDataConverter.Serialize(form.GetType(), form)); return(xid); }
private async Task ProcessUnfinishedSagaAsync(QueryGlobalTransactionDetailReply globalTx) { // 对补偿中或者补偿过程中有失败的全局事务的分支事务找到关注的分支事务(根据branchTx.serviceKey) // 执行各自的补偿方法,并修改分支状态 // 如果某个分支的补偿key为空,则直接修改分支状态 var xid = globalTx.Xid; var branches = globalTx.Branches; var watchedBranches = new List <TransactionBranchDetail>(); Dictionary <string, Func <object, Task> > serviceMethods = new Dictionary <string, Func <object, Task> >(); foreach (var branch in branches) { var branchServiceKey = branch.BranchServiceKey; // 根据branchServiceResolver 找出关注的branches. 暂时关注所有branch var branchService = _branchServiceResolver.ResolveBranch(branchServiceKey); if (branchService == null) { continue; } watchedBranches.Add(branch); serviceMethods[branchServiceKey] = branchService; } // 如果没有关注的branches,返回 if (watchedBranches.Count < 1) { return; } watchedBranches.Reverse(); // watchedBranches改成倒叙,这是为了优先执行补偿后到的分支 // 执行回滚,以及修改分支状态 foreach (var branch in watchedBranches) { var branchId = branch.BranchId; var branchCompensationServiceKey = branch.BranchCompensationServiceKey; var oldBranchState = branch.State; var oldBranchVersion = branch.Version; var jobId = Guid.NewGuid().ToString(); if (branchCompensationServiceKey == null || branchCompensationServiceKey.Length < 1) { // 补偿方法为空,直接标记为已经补偿 await _sagaCollaborator.SubmitBranchTxStateAsync(xid, branchId, oldBranchState, TxState.CompensationDone, oldBranchVersion, jobId, "", null); continue; } // 调用 branchServiceResolver 去执行补偿方法 var branchCompensationService = _branchServiceResolver.ResolveBranch(branchCompensationServiceKey); // 调用成功后标记为已经补偿,如果调用失败或者没有找到补偿方法,上报补偿失败 var compensationSuccess = false; var errorReason = ""; if (branchCompensationService != null) { try { // 从saga server取到saga data var sagaDataReply = await _sagaCollaborator.GetSagaDataAsync(xid); byte[] sagaDataBytes = sagaDataReply.Data.ToByteArray(); var sagaData = _sagaDataConverter.Deserialize <SagaData>(sagaDataTypeResolver, sagaDataBytes); await branchCompensationService(sagaData); var changedSagaDataBytes = _sagaDataConverter.Serialize(typeof(SagaData), sagaData); await _sagaCollaborator.SubmitBranchTxStateAsync(xid, branchId, branch.State, TxState.CompensationDone, branch.Version, jobId, "", changedSagaDataBytes); compensationSuccess = true; } catch (Exception e) { compensationSuccess = false; errorReason = e.Message; _logger.LogError($"branch {branchId} compensation error", e); } } if (!compensationSuccess) { _logger.LogError($"branch compensation {branchCompensationServiceKey} fail"); try { var latestBranch = await _sagaCollaborator.QueryBranchTxAsync(branchId); await _sagaCollaborator.SubmitBranchTxStateAsync(xid, branchId, latestBranch.Detail.State, TxState.CompensationError, latestBranch.Detail.Version, jobId, errorReason, null); } catch (Exception e2) { _logger.LogError($"SubmitBranchTxStateAsync o branch {branchId} error", e2); } } } }