zoukankan      html  css  js  c++  java
  • 企业应用微型Windows服务架构

    企业应用微型Windows服务架构

     

    背景:质量检测集团企业内部有N多系统,这些系统之间有关联性并相互协作,很多业务需要Windows服务大显身手

    举例:客户通过网站下定单 > 企业内部收到客户快递的样品在实验室系统登记 > 实验室系统检测 > 实验室系统出具检测报告 > 客户通过浏览器查看报告

    运用Windows服务进行:

    • 报告文件同步
    • 对客户的短信、邮件通知
    • 对内部工作人员进行短信、邮件通知
    • 进行分公司内网与公共服务器数据同步

      ……

    以上只是一个例子,实际情况要复杂的多,很多个服务放在一起运行、维护很不方便,混乱不堪,如何通过一种巧妙的方式进行管理、部署、监控,让管理人员可操作性更强呢?博主写了一个小程序来达到上面的要求,请大家指点一二。

    技术点:

    • XML读写
    • 多线程
    • 反射
    • Windows服务安装卸载脚本

    思路:

    1. windows服务项目是一个装载服务的容器,它负责配置并调用功能组件
    2. 提供一个统一的编程接口,使后续所有的功能组件都需要实现这个接口方便服务容器去调用
    3. 植入功能组件:实现以上接口并单独做自己的事情,与其他功能组件互不相干
    4. 放到Windows服务容器,配置组件信息(执行间隔,程序集名称,类名等)

    以上模式有点像一个多媒体播放器程序,自身提供了解析mp3,mp4,wma,rmvb等格式的功能

    播放器就是一个装载容器,解析媒体文件格式的组件就相当于插件

    当有一种新的媒体格式比如(flv)诞生后,开发者们可以实现播放器提供的接口并解析这种(flv)媒体文件,然后通过配置文件添加到播放器目录即可

    当播放器要播放flv的文件,就会去加载解析flv的组件

    每一种对媒体格式的解析组件互不影响,这符合松耦合、工厂模式的设计思想

    废话说到这里,贴代码:

    接口:

    1 public interface IRun
    2     {
    3         void Run();
    4     }

    配置文件Config.xml,配置程序集的执行时间间隔,程序集名称,类名

    复制代码
     1 <?xml version="1.0" encoding="utf-8" ?>
     2 <Configuration>
     3   <!--
     4   assembly:程序集名称
     5   class:类名
     6   method:固定Run(),默认为Run,对应类需要实现接口IRun并实现Run()
     7   -->
     8   <AssemblyConfig>
     9       <Assembly Interval="30000" Assembly="FCL.Monitor" Class="Monitor" />
    10       <Assembly Interval="30000" Assembly="FCL.Monitor" Class="Monitor_Vancl" />
    11       <Assembly Interval="30000" Assembly="FCL.Monitor" Class="CreateNotify" />
    12       <Assembly Interval="30000" Assembly="FCL.Monitor" Class="CreateNotify_Vancl" />
    13       <Assembly Interval="1000" Assembly="FCL.Email" Class="SendEmail" />
    14   </AssemblyConfig>
    15 </Configuration>
    复制代码

    容器加载后需要构造的实体类

    复制代码
     1     public class AssemblyInfo
     2     {
     3         /// <summary>
     4         /// 执行间隔
     5         /// </summary>
     6         public int Interval { get; set; }
     7 
     8         /// <summary>
     9         /// 程序集名称
    10         /// </summary>
    11         public string AssemblyName { get; set; }
    12         /// <summary>
    13         /// 类名
    14         /// </summary>
    15         public string ClassName { get; set; }
    16         /// <summary>
    17         /// 方法名
    18         /// </summary>
    19         public readonly string MethodName = "Run";
    20     }
    复制代码

    服务启动需要加载已经配置的程序集

    复制代码
     1         /// <summary>
     2         /// 服务启动
     3         /// </summary>
     4         /// <param name="args"></param>
     5         protected override void OnStart(string[] args)
     6         {
     7             Log.log.Info(DateTime.Now + " 服务启动");
     8 
     9             //读取xml配置
    10             DataSet ds = new DataSet();
    11             ds.ReadXml(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.xml"));
    12             DataTable dt = ds.Tables[1];
    13             List<AssemblyInfo> assemblyInfoList = new List<AssemblyInfo>();
    14             AssemblyInfo assemblyInfo;
    15             foreach (DataRow row in dt.Rows)
    16             {
    17                 assemblyInfo = new AssemblyInfo();
    18                 assemblyInfo.AssemblyName = row["Assembly"].ToString();
    19                 assemblyInfo.ClassName = row["Class"].ToString();
    20                 assemblyInfo.Interval = Convert.ToInt32(row["Interval"]);
    21                 assemblyInfoList.Add(assemblyInfo);
    22 
    23                 Log.log.Info(DateTime.Now
    24                        + " 程序集名称:" + assemblyInfo.AssemblyName
    25                        + ",类名:" + assemblyInfo.ClassName
    26                        + ",执行间隔:" + assemblyInfo.Interval);
    27             }
    28 
    29             //加载XML程序集配置
    30             ExcuteAssembly.ExcuteListAssembly(assemblyInfoList);
    31         }
    复制代码


    以及相关用线程、反射执行程序集

    复制代码
        public class ExcuteAssembly
        {
            /// <summary>
            /// 执行批量程序集
            /// </summary>
            /// <param name="assemblyList">程序集集合</param>
            public static void ExcuteListAssembly(List<AssemblyInfo> assemblyList)
            {
                try
                {
                    Thread thread;
                    foreach (AssemblyInfo item in assemblyList)
                    {
                        thread = new Thread(new ParameterizedThreadStart(ExcuteSingleAssembly));
                        thread.IsBackground = true;
                        thread.Start(item);
                    }
                }
                catch (Exception ex)
                {
                    Log.log.Error(DateTime.Now + " 创建多个线程时: " + ex.Message);
                }
            }
    
            /// <summary>
            /// 执行单个程序集
            /// </summary>
            /// <param name="obj">程序集</param>
            public static void ExcuteSingleAssembly(object obj)
            {
                while (true)
                {
                    try
                    {
                        AssemblyInfo assembly = obj as AssemblyInfo;
                        Thread.Sleep(assembly.Interval);
    
                        Log.log.Debug("ExcuteSingleAssembly:" + (assembly as AssemblyInfo).AssemblyName);
                        if (assembly != null && assembly is AssemblyInfo)
                        {
                            Assembly a = Assembly.Load(assembly.AssemblyName);
                            //Type规则:命名空间名称=程序集名
                            Type type = a.GetType(assembly.AssemblyName + "." + assembly.ClassName);
                            Log.log.Debug("Type:" + type.ToString());
                            IRun run = (IRun)Activator.CreateInstance(type);
                            MethodInfo method = type.GetMethod(assembly.MethodName);
                            Log.log.Debug("MethodInfo:" + method.ToString());
                            BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
                            method.Invoke(run, flags, Type.DefaultBinder, null, null);
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.log.Error(DateTime.Now + " 执行程序集时出错: " + ex.Message);
                    }
                }
            }
        }
    复制代码

    以上就是服务容器的核心代码,下面贴一个组件代码

    复制代码
     1     /// <summary>
     2     /// 监测ERP数据库的单据状态,并生成消息日志
     3     /// </summary>
     4     public class Monitor : IRun
     5     {
     6         public void Run()
     7         {
     8             new DAL.Sys_ApplyFormNotifyLog().CreateNotify();
     9         }
    10     }
    复制代码

    关于服务的安装运行卸载也写了批处理

    安装:

    echo 清理原有服务项. . .
    %SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil /U FCL.WinService.exe
    echo 清理完毕,开始安装后台服务. . .
    %SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil FCL.WinService.exe
    echo 服务安装完毕
    pause

    启动:

    echo 启动服务. . .
    net start FCLService
    pause

    停止:

    echo 停止服务. . .
    net stop FCLService
    pause

    到此结束,程序里有很多需要改进之处,使程序更加灵活、稳定、强壮,请大家多多指点。

     
     
     
  • 相关阅读:
    注释
    选择器
    SQL语句中查找字符的位置
    SQL语句中截取字符串Substr
    IDENTITY(函数)
    SQL Server设置主键自增长列
    SQL语句操作ALTER
    表的主键
    南京夜市
    夜班
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3082672.html
Copyright © 2011-2022 走看看