【Orleans开胃菜系列1】不要被表象迷惑
Orleans之开胃小菜
SafeExecute安全执行
翻看Orleans源码时,发现一句SafeExecute,就以为是什么没接触过的很深的东西。那么SafeExecute到底是什么玩意呢?
Utils.SafeExecute(() => { if (typeMapRefreshTimer != null) { typeMapRefreshTimer.Dispose(); typeMapRefreshTimer = null; } }, logger, "Client.typeMapRefreshTimer.Dispose");
接下来追踪代码如下,原来主要逻辑就是加try,catch不引发异常。
public static void SafeExecute(Action action, ILogger logger, Func<string> callerGetter) { try { action(); } catch (Exception exc) { try { if (logger != null) { string caller = null; if (callerGetter != null) { try { caller = callerGetter(); }catch (Exception) { } } foreach (var e in exc.FlattenAggregate()) { logger.Warn(ErrorCode.Runtime_Error_100325, $"Ignoring {e.GetType().FullName} exception thrown from an action called by {caller ?? String.Empty}.", exc); } } } catch (Exception) { // now really, really ignore. } } }
ExecuteWithRetries重试机制
还有例如以下代码,顾名思义就是支持重试执行
await ExecuteWithRetries( async () => this.GrainTypeResolver = await transport.GetGrainTypeResolver(this.InternalGrainFactory), retryFilter);
跟踪源码如下:
综合考虑最晚执行时间,成功执行次数,失败执行次数等因素,延迟回调.
利用do..while,Task,Func等知识来完成延时重试,并不算太复杂。
public static Task ExecuteWithRetries( Func<int, Task> action, int maxNumErrorTries, Func<Exception, int, bool> retryExceptionFilter, TimeSpan maxExecutionTime, IBackoffProvider onErrorBackOff) { Func<int, Task<bool>> function = async (int i) => { await action(i); return true; }; return ExecuteWithRetriesHelper<bool>( function, 0, 0, maxNumErrorTries, maxExecutionTime, DateTime.UtcNow, null, retryExceptionFilter, null, onErrorBackOff); } private static async Task<T> ExecuteWithRetriesHelper<T>( Func<int, Task<T>> function, int callCounter, int maxNumSuccessTries, int maxNumErrorTries, TimeSpan maxExecutionTime, DateTime startExecutionTime, Func<T, int, bool> retryValueFilter = null, Func<Exception, int, bool> retryExceptionFilter = null, IBackoffProvider onSuccessBackOff = null, IBackoffProvider onErrorBackOff = null) { T result = default(T); ExceptionDispatchInfo lastExceptionInfo = null; bool retry; do { retry = false; if (maxExecutionTime != Constants.INFINITE_TIMESPAN && maxExecutionTime != default(TimeSpan)) { DateTime now = DateTime.UtcNow; if (now - startExecutionTime > maxExecutionTime) { if (lastExceptionInfo == null) { throw new TimeoutException( $"ExecuteWithRetries has exceeded its max execution time of {maxExecutionTime}. Now is {LogFormatter.PrintDate(now)}, started at {LogFormatter.PrintDate(startExecutionTime)}, passed {now - startExecutionTime}"); } lastExceptionInfo.Throw(); } } int counter = callCounter; try { callCounter++; result = await function(counter); lastExceptionInfo = null; if (callCounter < maxNumSuccessTries || maxNumSuccessTries == INFINITE_RETRIES) // -1 for infinite retries { if (retryValueFilter != null) retry = retryValueFilter(result, counter); } if (retry) { TimeSpan? delay = onSuccessBackOff?.Next(counter); if (delay.HasValue) { await Task.Delay(delay.Value); } } } catch (Exception exc) { retry = false; if (callCounter < maxNumErrorTries || maxNumErrorTries == INFINITE_RETRIES) { if (retryExceptionFilter != null) retry = retryExceptionFilter(exc, counter); } if (!retry) { throw; } lastExceptionInfo = ExceptionDispatchInfo.Capture(exc); TimeSpan? delay = onErrorBackOff?.Next(counter); if (delay.HasValue) { await Task.Delay(delay.Value); } } } while (retry); return result; } }
耐心求索
很多基础性的实现并不完全依赖高深隐秘的技术,多思考,多实践。只要思路正确,慢慢修养,星星之火总能燎原。
众所周知,.net的应用层每隔几年总会变化,像aspx现在是完全放弃了的。.netcore现在日益火热,但不见的得持久恒定。在有限的时间内,为了不让知识贬值,除了快速学习新知识外,更要注意基础知识的沉淀。就像上面的两个例子,无论方法名,基础思想在那放着,不会太变质。
但这些是远远不够的。变化的永远是上层。沉淀到基础层次,如进程线程之类的,就比较稳定了。除了这些代码层次的,更重要的是思想的沉淀,如《人月神话》《失控》《领域驱动涉及》,敏捷开发等方法论,依然很有用,更加基础的冯诺依曼计算器体系结构更是奠基者。
理论才是跨各种语言平台的。只记得一些方法命名空间而根基不扎实的,是无根之木。
未来的日子,勉励自己继续扩充理论知识。