/// <summary> /// 移除超过24小时已完成的事件锁、事件监视器、队列信息 /// 我们不能在执行完领域事件后立即删除这些信息,因为有可能是外界调用本地的事件,稍后可能外界要求回逆事件 /// 因此我们只删除24小时过期的信息,因为外界不可能过了24小时后又要求回逆 /// </summary> /// <param name="minutes"></param> private static void Clear() { DataContext.NewScope(() => { var repository = Repository.Create <IEventLockRepository>(); var locks = repository.FindExpireds(24); foreach (var @lock in locks) { var queueId = @lock.Id; var queue = EventQueue.Find(queueId); if (!queue.IsSucceeded) { continue; //对于没有执行成功的队列,我们不删除日志等信息,这样管理员可以排查错误 } DataContext.NewScope(() => { var monitor = EventMonitor.Find(queueId); if (!monitor.IsEmpty()) { EventMonitor.Delete(monitor); } EventQueue.Delete(queueId); EventLogEntry.Deletes(queueId); //删除日志的所有条目 EventLog.Delete(queueId); }, true); EventLock.Delete(@lock); } }, true); }
/// <summary> /// 有两种情况会恢复: /// 1.调用方主动要求恢复(这一般是因为调用方出了错误,要求被调用的领域事件恢复),这时候本地的事件队列就算是成功执行完毕的(非中断),也需要恢复 /// 2.本地机器故障或业务错误,要求恢复 /// </summary> /// <param name="queueId"></param> /// <param name="reason"></param> /// <param name="forceRestore">即使监视器不会中断的,也要回逆,这一般是因为调用方发布的回逆要求</param> /// <returns></returns> public static bool TryRestore(Guid queueId, Exception reason, bool forceRestore) { bool success = false; //是否成功恢复 try { DataContext.NewScope(() => { var @lock = EventLock.Find(queueId, QueryLevel.Single);//这段代码将入口锁住,所以监视器和队列也间接被锁住了 if (@lock.IsEmpty()) { return; //没有锁,那么意味着队列及相关的数据已被恢复了 } var monitor = EventMonitor.Find(queueId); var queue = EventQueue.Find(queueId); if (queue.IsEmpty()) { //队列不存在,要么是队列已被恢复了,要么就是队列还没初始化之前,就被终止了 //删除监视器 if (!monitor.IsEmpty()) { EventMonitor.Delete(monitor); } //删除锁 EventLock.Delete(@lock); success = true; return; } if (!forceRestore && !monitor.Interrupted) { //由于中断的监视器有可能是正在运行中的(正在执行的队列我们会把监视器设置为中断,当队列执行完毕后恢复为非中断),所以这类监视器运行完毕后,就不再中断了 //所以就不需要恢复 success = false; return; } //防止恢复过程中也断电或故障,这里主动设置为中断的 monitor.Interrupted = true; EventMonitor.UpdateAndFlush(monitor); Restore(queueId, reason); //恢复完毕后,删除监视器 EventMonitor.Delete(monitor); //删除锁 EventLock.Delete(@lock); }, true); DomainEvent.OnFailed(queueId, new EventFailedException(reason)); success = true; } catch (EventRestoreException) { //Restore方法已写入日志,就不再写入 DomainEvent.OnError(queueId, new EventErrorException(reason)); throw; } catch (Exception ex) { LogWrapper.Default.Fatal(ex); } return(success); }
/// <summary> /// 使用队列 /// </summary> /// <param name="queueId">队列编号</param> /// <param name="newQueue">是否新建队列</param> /// <param name="action">使用队列完成的任务</param> /// <param name="error">发生错误时的回调</param> public static void UseQueue(Guid queueId, bool newQueue, Action <EventCallback> action, Action <Exception> error = null) { EventCallback callBack = new EventCallback(); Exception exception = null; try { DataContext.NewScope(() => { var @lock = newQueue ? EventLock.GetOrCreate(queueId) : EventLock.Find(queueId, QueryLevel.Single);//这段代码将入口锁住,所以监视器和队列也间接被锁住了 if (@lock.IsEmpty()) { return; //无相关的信息,直接返回(保证幂等性,有可能队列被多次重复处理) } var monitor = newQueue ? EventMonitor.GetOrCreate(queueId) : EventMonitor.Find(queueId); if (monitor.IsEmpty()) { return; } if (monitor.Interrupted) { return; //监视器被中断,那么需要等待恢复,不必执行队列 } monitor.Interrupted = true; EventMonitor.UpdateAndFlush(monitor); //主动将监视器设置为中断的,这样后续的操作中如果出错或者中途断电,监视器就是中断的了,可以被恢复 action(callBack); if (callBack.HasAction) { //执行注册的回调方法 callBack.Execute(); } monitor.Interrupted = false; EventMonitor.Update(monitor); //修改监视器为不中断的,但是此处不提交,而是跟整体操作一起提交才生效 }, true); } catch (Exception ex) { exception = ex; } if (exception != null) { try { LogWrapper.Default.Fatal(exception); //自定义错误处理 if (error != null) { error(exception); } } catch (Exception ex) { //如果自定义错误处理也出错,那么写入日志 LogWrapper.Default.Fatal(ex); } //恢复 TryRestore(queueId, exception, true); } }