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

using Apewer;
using Apewer.Surface;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading;
namespace Apewer.Web
{
/// <summary>Cron 调度器。</summary>
public sealed class CronInvoker
{
#region Instance
private List<Assembly> _assemblies = null;
private List<CronInstance> _instances = null;
private bool _break = false;
private Action<string> _log = null;
/// <summary>获取或设置 Log 处理程序。</summary>
public Action<string> LogAction { get { return _log; } set { _log = value; } }
internal void Log(params object[] content) => Logger?.Text(typeof(CronInvoker), content);
/// <summary>加载程序集。</summary>
public void Load(IEnumerable<Assembly> assemblies)
{
Log("开始加载程序集。");
if (_assemblies == null) _assemblies = new List<Assembly>();
if (_assemblies.Count > 0)
{
Log("程序集列表为空。");
return;
}
if (assemblies == null)
{
_assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies());
}
else
{
foreach (var assembly in assemblies)
{
if (assembly == null) continue;
if (_assemblies.Contains(assembly)) continue;
_assemblies.Add(assembly);
}
}
var count = _assemblies.Count.ToString();
Log($"已加载 {count} 个程序集。");
}
/// <summary>通知打断循环,所有 Cron 执行结束后退出。</summary>
public void Break()
{
_break = true;
}
/// <summary>开始 Cron 调用。</summary>
public void Start()
{
if (_instances != null) return;
_instances = GetInstances();
var count = _assemblies.Count.ToString();
Log($"检查到 {count} 个 Cron 类型。");
foreach (var i in _instances) Log(i.Type.FullName);
while (true)
{
// CronLog.Write("Tick");
var alive = 0;
foreach (var i in _instances)
{
// 跳出。
if (i.Alive) alive++;
if (_break)
{
i.Break = true;
break;
}
// 当前线程正在活动。
if (i.Alive)
{
i.Latest = true;
continue;
}
// 记录 Cron 结束时间,根据结束时间判断再次启动 Cron。
if (i.Latest)
{
Log($"类型 {i.Type.FullName} 已结束。");
i.Ended = DateTime.Now;
i.Latest = false;
}
if (i.Ended == null)
{
Log($"准备开始类型 {i.Type.FullName}。");
i.Start();
i.Latest = true;
}
else
{
var span = DateTime.Now - i.Ended.Value;
if (span.TotalMilliseconds >= Convert.ToDouble(i.Interval))
{
Log($"准备开始类型 {i.Type.FullName}。");
i.Start();
i.Latest = true;
}
}
}
if (_break && alive < 1)
{
break;
}
Thread.Sleep(1000);
GC.Collect();
}
Log("循环结束,即将退出。");
}
private List<CronInstance> GetInstances()
{
var list = new List<CronInstance>();
var types = GetTypes();
foreach (var type in types)
{
var attribute = RuntimeUtility.GetAttribute<CronAttribute>(type, false);
if (attribute == null) continue;
var instance = new CronInstance();
instance.Invoker = this;
instance.Attribute = attribute;
instance.Type = type;
list.Add(instance);
}
return list;
}
private List<Type> GetTypes()
{
var list = new List<Type>();
var assemblies = _assemblies;
foreach (var assembly in assemblies)
{
var types = RuntimeUtility.GetTypes(assembly);
foreach (var type in types)
{
if (!type.IsPublic) continue;
if (type.IsAbstract) continue;
if (!RuntimeUtility.CanNew(type)) continue;
list.Add(type);
}
}
return list;
}
#endregion
#region Static
/// <summary>获取或设置日志记录器。</summary>
public static Logger Logger { get; set; }
/// <summary>在当前线程开始 Cron 调用,可能会阻塞当前线程。可指定 Log 处理程序。</summary>
public static CronInvoker Start(Assembly assembly, Action<string> log = null)
{
var assemblies = new Assembly[] { assembly };
return Start(assemblies);
}
/// <summary>在当前线程开始 Cron 调用,可能会阻塞当前线程。可指定 Log 处理程序。</summary>
public static CronInvoker Start(IEnumerable<Assembly> assemblies, Action<string> log = null)
{
var instance = new CronInvoker();
instance.LogAction = log;
instance.Load(assemblies);
instance.Start();
return instance;
}
#endregion
}
}