Orleans之开胃小菜1
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现在日益火热,但不见的得持久恒定。在有限的时间内,为了不让知识贬值,除了快速学习新知识外,更要注意基础知识的沉淀。就像上面的两个例子,无论方法名,基础思想在那放着,不会太变质。
但这些是远远不够的。变化的永远是上层。沉淀到基础层次,如进程线程之类的,就比较稳定了。除了这些代码层次的,更重要的是思想的沉淀,如《人月神话》《失控》《领域驱动涉及》,敏捷开发等方法论,依然很有用,更加基础的冯诺依曼计算器体系结构更是奠基者。
理论才是跨各种语言平台的。只记得一些方法命名空间而根基不扎实的,是无根之木。未来的日子,勉励自己继续扩充理论知识。