You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.1 KiB

  1. using Apewer;
  2. using Apewer.Surface;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Threading;
  8. namespace Apewer.Web
  9. {
  10. /// <summary>Cron 调度器。</summary>
  11. public sealed class CronInvoker
  12. {
  13. #region Instance
  14. private List<Assembly> _assemblies = null;
  15. private List<CronInstance> _instances = null;
  16. private bool _break = false;
  17. private Action<string> _log = null;
  18. /// <summary>获取或设置 Log 处理程序。</summary>
  19. public Action<string> LogAction { get { return _log; } set { _log = value; } }
  20. internal void Log(params object[] content) => Logger?.Text(typeof(CronInvoker), content);
  21. /// <summary>加载程序集。</summary>
  22. public void Load(IEnumerable<Assembly> assemblies)
  23. {
  24. Log("开始加载程序集。");
  25. if (_assemblies == null) _assemblies = new List<Assembly>();
  26. if (_assemblies.Count > 0)
  27. {
  28. Log("程序集列表为空。");
  29. return;
  30. }
  31. if (assemblies == null)
  32. {
  33. _assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies());
  34. }
  35. else
  36. {
  37. foreach (var assembly in assemblies)
  38. {
  39. if (assembly == null) continue;
  40. if (_assemblies.Contains(assembly)) continue;
  41. _assemblies.Add(assembly);
  42. }
  43. }
  44. var count = _assemblies.Count.ToString();
  45. Log($"已加载 {count} 个程序集。");
  46. }
  47. /// <summary>通知打断循环,所有 Cron 执行结束后退出。</summary>
  48. public void Break()
  49. {
  50. _break = true;
  51. }
  52. /// <summary>开始 Cron 调用。</summary>
  53. public void Start()
  54. {
  55. if (_instances != null) return;
  56. _instances = GetInstances();
  57. var count = _assemblies.Count.ToString();
  58. Log($"检查到 {count} 个 Cron 类型。");
  59. foreach (var i in _instances) Log(i.Type.FullName);
  60. while (true)
  61. {
  62. // CronLog.Write("Tick");
  63. var alive = 0;
  64. foreach (var i in _instances)
  65. {
  66. // 跳出。
  67. if (i.Alive) alive++;
  68. if (_break)
  69. {
  70. i.Break = true;
  71. break;
  72. }
  73. // 当前线程正在活动。
  74. if (i.Alive)
  75. {
  76. i.Latest = true;
  77. continue;
  78. }
  79. // 记录 Cron 结束时间,根据结束时间判断再次启动 Cron。
  80. if (i.Latest)
  81. {
  82. Log($"类型 {i.Type.FullName} 已结束。");
  83. i.Ended = DateTime.Now;
  84. i.Latest = false;
  85. }
  86. if (i.Ended == null)
  87. {
  88. Log($"准备开始类型 {i.Type.FullName}。");
  89. i.Start();
  90. i.Latest = true;
  91. }
  92. else
  93. {
  94. var span = DateTime.Now - i.Ended.Value;
  95. if (span.TotalMilliseconds >= Convert.ToDouble(i.Interval))
  96. {
  97. Log($"准备开始类型 {i.Type.FullName}。");
  98. i.Start();
  99. i.Latest = true;
  100. }
  101. }
  102. }
  103. if (_break && alive < 1)
  104. {
  105. break;
  106. }
  107. Thread.Sleep(1000);
  108. GC.Collect();
  109. }
  110. Log("循环结束,即将退出。");
  111. }
  112. private List<CronInstance> GetInstances()
  113. {
  114. var list = new List<CronInstance>();
  115. var types = GetTypes();
  116. foreach (var type in types)
  117. {
  118. var attribute = RuntimeUtility.GetAttribute<CronAttribute>(type, false);
  119. if (attribute == null) continue;
  120. var instance = new CronInstance();
  121. instance.Invoker = this;
  122. instance.Attribute = attribute;
  123. instance.Type = type;
  124. list.Add(instance);
  125. }
  126. return list;
  127. }
  128. private List<Type> GetTypes()
  129. {
  130. var list = new List<Type>();
  131. var assemblies = _assemblies;
  132. foreach (var assembly in assemblies)
  133. {
  134. var types = RuntimeUtility.GetTypes(assembly);
  135. foreach (var type in types)
  136. {
  137. if (!type.IsPublic) continue;
  138. if (type.IsAbstract) continue;
  139. if (!RuntimeUtility.CanNew(type)) continue;
  140. list.Add(type);
  141. }
  142. }
  143. return list;
  144. }
  145. #endregion
  146. #region Static
  147. /// <summary>获取或设置日志记录器。</summary>
  148. public static Logger Logger { get; set; }
  149. /// <summary>在当前线程开始 Cron 调用,可能会阻塞当前线程。可指定 Log 处理程序。</summary>
  150. public static CronInvoker Start(Assembly assembly, Action<string> log = null)
  151. {
  152. var assemblies = new Assembly[] { assembly };
  153. return Start(assemblies);
  154. }
  155. /// <summary>在当前线程开始 Cron 调用,可能会阻塞当前线程。可指定 Log 处理程序。</summary>
  156. public static CronInvoker Start(IEnumerable<Assembly> assemblies, Action<string> log = null)
  157. {
  158. var instance = new CronInvoker();
  159. instance.LogAction = log;
  160. instance.Load(assemblies);
  161. instance.Start();
  162. return instance;
  163. }
  164. #endregion
  165. }
  166. }