1.简单介绍
XAgent为大石头带领下的新生命团队自己开发的一个.Net下的常用的Windows服务管理组件利器,通过在控制台中简单的输入1,2,3,4,5等数字可以实现一步安装、卸载WindowsService以及单步调试和循环调试、服务停止,重启、运行,用起来很方便。看效果(具体使用方法可以去XAgent官网查看):
2.当前使用场景介绍及实现
我的这个项目下面有很多控制台应用程序需要运行,平时部署很麻烦,不方便管理。我的想法是把这些程序以插件的形式放到一个应用程序中运行管理。这里写了一个Ijob接口。看代码:
Ijob相关代码

1 /// <summary>工作接口</summary> 2 public interface IJob 3 { 4 /// <summary> 5 /// 工作组件顺序 6 /// </summary> 7 int Sort { get; set; } 8 9 /// <summary> 10 /// 间隔时间 11 /// </summary> 12 int JobInterval { get; set; } 13 14 /// <summary>启动</summary> 15 /// <returns></returns> 16 bool Start(); 17 18 /// <summary>停止</summary> 19 /// <returns></returns> 20 bool Stop(); 21 22 /// <summary>工作</summary> 23 bool Work(); 24 25 //void WriteLog(string format, params object[] args); 26 } 27 28 /// <summary>基础工作组件</summary> 29 public class Job : IJob 30 { 31 ///// <summary> 32 ///// 是否执行 33 ///// </summary> 34 //public bool IsExcute { get; set; } 35 36 public Job() 37 { 38 Sort = 0; 39 DisplayName = "基础工作组件"; 40 JobInterval = 60; //默认60秒 41 //IsExcute = true;//默认执行 42 } 43 44 /// <summary>工作组件名</summary> 45 public virtual string DisplayName { get; set; } 46 47 /// <summary>工作组件顺序</summary> 48 public virtual int Sort { get; set; } 49 50 /// <summary> 51 /// 时间间隔 52 /// </summary> 53 public int JobInterval { get; set; } 54 55 /// <summary>启动</summary> 56 public virtual bool Start() 57 { 58 WriteLog("开始工作"); 59 return false; 60 } 61 62 /// <summary>停止</summary> 63 /// <returns></returns> 64 public virtual bool Stop() 65 { 66 WriteLog("停止工作"); 67 return false; 68 } 69 70 /// <summary>工作</summary> 71 public virtual bool Work() 72 { 73 WriteLog("正在运行"); 74 return false; 75 } 76 77 public virtual void WriteLog(string format, params object[] args) 78 { 79 //XTrace.WriteLine("[" + this.GetType() + "] [" + DisplayName + "] " + format, args); 80 XTrace.WriteLine("[" + DisplayName + "] " + format, args); 81 }

1 public static class JobHelper 2 { 3 static readonly List<Type> _hasInited = new List<Type>(); 4 5 private static List<IJob> op_cache = new List<IJob>(); 6 7 /// <summary>所有工作组件</summary> 8 public static List<IJob> Works 9 { 10 get 11 { 12 var list = LoadWork(); 13 foreach (var item in list) 14 { 15 EnsureInit(item); 16 } 17 op_cache = op_cache.OrderBy(j => j.Sort).ToList(); 18 return op_cache; 19 } 20 } 21 22 /// <summary>确保实体类已经执行完静态构造函数,因为那里实在是太容易导致死锁了</summary> 23 /// <param name="type">类型</param> 24 internal static void EnsureInit(Type type) 25 { 26 if (_hasInited.Contains(type)) return; 27 // 先实例化,在锁里面添加到列表但不实例化,避免实体类的实例化过程中访问CreateOperate导致死锁产生 28 var item = type.CreateInstance(); 29 lock (_hasInited) 30 // 如果这里锁定_hasInited,还是有可能死锁,因为可能实体类A的静态构造函数中可能导致调用另一个实体类的EnsureInit 31 // 其实我们这里加锁的目的,本来就是为了避免重复添加同一个type而已 32 //lock ("_hasInited" + type.FullName) 33 { 34 if (_hasInited.Contains(type)) return; 35 36 37 if (op_cache.Contains(item as IJob)) return; 38 op_cache.Add(item as IJob); 39 40 41 //type.CreateInstance(); 42 _hasInited.Add(type); 43 } 44 } 45 46 /// <summary>列出所有工作接口</summary> 47 /// <returns></returns> 48 public static List<Type> LoadWork() 49 { 50 return typeof (IJob).GetAllSubclasses(true).ToList(); 51 } 52 }
确保所有的工作组件继承Job类,重写其中的方法,将一些需要配置的属性做成xml进行管理。废话不说看代码:
这是其中一个工作插件

1 public HourStatisticsWork() 2 { 3 4 5 DisplayName = "小时统计组件"; 6 Sort = 2; 7 8 if (WorkSetting.Current.HourStatisticsEnable) 9 { 10 JobInterval = WorkSetting.Current.HourStatisticsInterval; 11 } 12 else 13 { 14 JobInterval = -1; //禁用此组件 15 } 16 } 17 18 public override bool Work() 19 { 20 StartWork(); 21 return base.Work(); 22 } 23 public override bool Stop() 24 { 25 if (_HourTimer != null) 26 { 27 _HourTimer.Dispose(); 28 } 29 return base.Stop(); 30 } 31 public void StartWork() 32 { 33 try 34 { 35 // Console.WriteLine("当前时间{0}",DateTime.Now); 36 //var time = WorkSetting.Current.HourStatisticsInterval; 37 //// 定时器一分钟后启动 38 //if (_HourTimer == null) 39 // _HourTimer = new TimerX(obj => 40 // { 41 42 43 // StartWork(); 44 // }, null, 3 * 1000, time); 45 DateTime dt = DateTime.Now.AddHours(-1); 46 cyStatistics(dt); 47 sccStatistics(dt); 48 ccStatistics(dt); 49 jcStatistics(dt); 50 mfStatistics(dt); 51 vcStatistics(dt); 52 vbStatistics(dt); 53 pulStatistics(dt); 54 icStatistics(dt); 55 rhcStatistics(dt); 56 dtrStatistics(dt); 57 58 MotorLoadStatistics(dt); 59 TimeStatistics(dt); 60 Container.Info("[HourStatisticsWork]Hour Statistics"); 61 } 62 catch (Exception ex) 63 { 64 Container.Error($"[HourStatisticsWork]{ex.Message}"); 65 } 66 67 }
相关配置项

1 [DisplayName("运行参数设置")] 2 [XmlConfigFile(@"ConfigWorkSetting.config", 15000)] 3 public class WorkSetting : XmlConfig<WorkSetting> 4 { 5 //private String _LastTool; 6 ///// <summary>最后一个使用的工具</summary> 7 //[DisplayName("最后一个使用的工具")] 8 //public String LastTool { get { return _LastTool; } set { _LastTool = value; } } 9 10 protected override void OnNew() 11 { 12 SystemName = "物联网监控系统"; 13 LastUpdateTime = new DateTime(2017, 1, 1); 14 15 RabbitMqResolveEnable = true; 16 RabbitMqResolveInterval = 1;//默认1s 17 HourStatisticsEnable = true; 18 HourStatisticsInterval = 30*60;//默认30min 19 20 DayStatisticsEnable = true; 21 DayStatisticsInterval = 12 * 60*60;//默认12个小时 22 23 EnableViewServiceLog = false; 24 } 25 26 #region 队列解析 27 28 [Description("启用队列解析")] 29 public bool RabbitMqResolveEnable { get; set; } 30 [Description("队列解析间隔时间,单位:秒")] 31 public int RabbitMqResolveInterval { get; set; } 32 33 #endregion 34 35 #region 小时统计 36 37 [Description("启用小时统计")] 38 public bool HourStatisticsEnable { get; set; } 39 [Description("小时统计间隔时间,单位:秒")] 40 public int HourStatisticsInterval { get; set; } 41 #endregion 42 43 #region 自然日统计 44 45 [Description("启用自然日统计")] 46 public bool DayStatisticsEnable { get; set; } 47 [Description("自然日统计间隔时间,单位:秒")] 48 public int DayStatisticsInterval { get; set; } 49 #endregion 50 51 #region 异常通知 52 53 [Description("启用异常通知")] 54 public bool ExceptionNotifyEnable { get; set; } 55 56 [Description("异常通知级别")] 57 public int ExceptionNotifyLevel { get; set; } 58 59 #endregion 异常通知 60 61 #region 系统 62 63 /// <summary> 64 /// 系统名称 65 /// </summary> 66 [Description("系统名称")] 67 public String SystemName { get; set; } 68 69 /// <summary>最后更新时间</summary> 70 [Description("最后更新时间")] 71 public DateTime LastUpdateTime { get; set; } 72 73 #endregion 系统 74 75 #region Windows Service 76 77 /// <summary>服务名</summary> 78 [Description("服务名")] 79 public String ServiceName { get; set; } 80 81 /// <summary>显示名</summary> 82 [Description("显示名")] 83 public String DisplayName { get; set; } 84 85 /// <summary>服务描述</summary> 86 [Description("服务描述")] 87 public String Description { get; set; } 88 89 /// <summary>查看服务运行日志</summary> 90 [Description("查看服务运行日志")] 91 public bool EnableViewServiceLog { get; set; } 92 93 #endregion Windows Service 94 }
一切准备好后,开始生成项目并运行,效果如下:
3.发现问题
看起来一切运行正常,但是等到我讲xml文件中的HourStatisticsEnable设置成false时,发现只有第一个队列解析的服务在运行。第三个自然日统计的服务没有运行。这时我注意到,原来是插件运行的先后顺序影响的,即Sort属性。本质上还是JobInterval控制了改插件是否运行。可能XAgent的源代码有些小问题,我没有继续深入到源代码中去查找解决,而是直接通过xml配置文件的Enable判断该组件是否运行。
代码如下:

1 public HourStatisticsWork() 2 { 3 4 if (WorkSetting.Current.HourStatisticsEnable) 5 { 6 DisplayName = "小时统计组件"; 7 Sort = 2; 8 JobInterval = WorkSetting.Current.HourStatisticsInterval; 9 } 10 else 11 { 12 //JobInterval = -1; //禁用此组件 13 } 14 } 15 16 public override bool Work() 17 { 18 if (WorkSetting.Current.HourStatisticsEnable) 19 { 20 StartWork(); 21 } 22 return base.Work(); 23 } 24 public override bool Stop() 25 { 26 if (_HourTimer != null) 27 { 28 _HourTimer.Dispose(); 29 } 30 return base.Stop(); 31 } 32 public void StartWork() 33 {//todo}
另外,该IJob接口借鉴了我曾经的领导的想法。
供大家学习,如有不正不妥之处请大家指正。谢谢!
需要源码的或者帮助的联系我QQ:694666781