RetryPolicy.cs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using VCommon.Logging;
  5. namespace VCommon.Diagnostics
  6. {
  7. public abstract class Retry
  8. {
  9. public static void Do(Action action, int maxTry = 3, int retryIntervalMs = 500
  10. , Action<int> onAttempting = null
  11. , Action<int> onSuccess = null
  12. , Func<int, Exception, bool> onFail = null
  13. , Action<IReadOnlyList<Exception>> onAbort = null)
  14. {
  15. Do<object>(() =>
  16. {
  17. action();
  18. return null;
  19. }, maxTry, retryIntervalMs, onAttempting, onSuccess, onFail, onAbort);
  20. }
  21. public static T Do<T>(Func<T> func, int maxTry = 3, int retryIntervalMs = 500
  22. , Action<int> onAttempting = null
  23. , Action<int> onSuccess = null
  24. , Func<int, Exception, bool> onFail = null
  25. , Action<IReadOnlyList<Exception>> onAbort = null)
  26. {
  27. var exceptions = new List<Exception>(maxTry);
  28. var attempt = 0;
  29. while (++attempt < maxTry)
  30. {
  31. try
  32. {
  33. onAttempting?.Invoke(attempt);
  34. var r = func();
  35. onSuccess?.Invoke(attempt);
  36. return r;
  37. }
  38. catch (Exception e)
  39. {
  40. exceptions.Add(e);
  41. if (false == (onFail?.Invoke(attempt, e) ?? true)) return default(T);
  42. Thread.Sleep(retryIntervalMs);
  43. }
  44. }
  45. var arr = exceptions.ToArray();
  46. onAbort?.Invoke(arr);
  47. throw new AggregateException(arr);
  48. }
  49. }
  50. public class RetryPolicy
  51. {
  52. public int MaxTry { get; }
  53. public int RetryIntervalMs { get; }
  54. public string Tag { get; }
  55. public bool ThrowOnAbort { get; set; }
  56. public RetryPolicy(string tag = null, int maxTry = 3, int retryIntervalMs = 500)
  57. {
  58. if (maxTry < 1) throw new ArgumentOutOfRangeException(nameof(maxTry));
  59. if (retryIntervalMs < 0) throw new ArgumentOutOfRangeException(nameof(maxTry));
  60. MaxTry = maxTry;
  61. RetryIntervalMs = retryIntervalMs;
  62. Tag = tag;
  63. }
  64. public void Do(Action action) => Retry.Do(action, MaxTry, RetryIntervalMs, OnAttempting, OnSuccess, OnFail, OnAbort);
  65. public T Do<T>(Func<T> func) => Retry.Do(func, MaxTry, RetryIntervalMs, OnAttempting, OnSuccess, OnFail, OnAbort);
  66. protected virtual void OnAttempting(int attempted)
  67. {
  68. //Logger.Debug($"Attempting {Tag}", new { attempted });
  69. }
  70. protected virtual void OnSuccess(int attempted)
  71. {
  72. //Logger.Debug($"Success {Tag}", new { attempted });
  73. }
  74. protected virtual bool OnFail(int attempted, Exception exception)
  75. {
  76. Logger.Warn($"Fail {Tag}", new { attempted });
  77. return true;
  78. }
  79. protected virtual void OnAbort(IReadOnlyList<Exception> exceptions)
  80. {
  81. Logger.Error($"Abort {Tag}", new { exceptions });
  82. if (ThrowOnAbort) throw new AggregateException(exceptions);
  83. }
  84. }
  85. }