必须填写至少10个字的日志
nnhy authored at 2012-07-27 18:48:21
30.94 KiB
X
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using NewLife.Configuration;
using NewLife.Log;
using NewLife.Model;
using NewLife.Reflection;
using NewLife.Threading;

namespace XAgent
{
    /// <summary>服务程序基类</summary>
    /// <typeparam name="TService">服务类型</typeparam>
    public abstract class AgentServiceBase<TService> : AgentServiceBase
         where TService : AgentServiceBase<TService>, new()
    {
        #region 构造
        static AgentServiceBase()
        {
            if (Instance == null) Instance = new TService();
        }
        #endregion

        #region 静态辅助函数
        /// <summary>服务主函数</summary>
        public static void ServiceMain()
        {
            //提升进程优先级
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

            // 根据配置修改服务名
            String name = Config.GetConfig<String>("XAgent.ServiceName");
            if (!String.IsNullOrEmpty(name)) Instance.ServiceName = name;

            //Instance.MakeBat();

            String[] Args = Environment.GetCommandLineArgs();

            if (Args.Length > 1)
            {
                #region 命令
                String cmd = Args[1].ToLower();
                if (cmd == "-s")  //启动服务
                {
                    ServiceBase[] ServicesToRun = new ServiceBase[] { Instance };

                    try
                    {
                        ServiceBase.Run(ServicesToRun);
                    }
                    catch (Exception ex)
                    {
                        XTrace.WriteLine(ex.ToString());
                    }
                    return;
                }
                else if (cmd == "-i") //安装服务
                {
                    Install(true);
                    return;
                }
                else if (cmd == "-u") //卸载服务
                {
                    Install(false);
                    return;
                }
                else if (cmd == "-start") //启动服务
                {
                    ControlService(true);
                    return;
                }
                else if (cmd == "-stop") //停止服务
                {
                    ControlService(false);
                    return;
                }
                else if (cmd == "-run") //循环执行任务
                {
                    TService service = new TService();
                    service.StartWork();
                    Console.ReadKey(true);
                    return;
                }
                else if (cmd == "-step") //单步执行任务
                {
                    TService service = new TService();
                    for (int i = 0; i < service.ThreadCount; i++)
                    {
                        service.Work(i);
                    }
                    return;
                }
                #endregion
            }
            else
            {
                Console.Title = AgentDisplayName;

                #region 命令行
                //XTrace.OnWriteLog += new EventHandler<WriteLogEventArgs>(XTrace_OnWriteLog);
                XTrace.UseConsole();

                TService serivce = Instance as TService;

                //输出状态
                serivce.ShowStatus();

                while (true)
                {
                    //输出菜单
                    serivce.ShowMenu();
                    Console.Write("请选择操作(-x是命令行参数):");

                    //读取命令
                    ConsoleKeyInfo key = Console.ReadKey();
                    if (key.KeyChar == '0') break;
                    Console.WriteLine();
                    Console.WriteLine();

                    switch ((Int32)(key.KeyChar - '0'))
                    {
                        case 1:
                            //输出状态
                            serivce.ShowStatus();

                            break;
                        case 2:
                            if (IsInstalled == true)
                                Install(false);
                            else
                                Install(true);
                            break;
                        case 3:
                            if (IsRunning == true)
                                ControlService(false);
                            else
                                ControlService(true);
                            break;
                        case 4:
                            #region 单步调试
                            try
                            {
                                Int32 n = 0;
                                if (Instance.ThreadCount > 1)
                                {
                                    Console.Write("请输入要调试的任务(任务数:{0}):", Instance.ThreadCount);
                                    ConsoleKeyInfo k = Console.ReadKey();
                                    Console.WriteLine();
                                    n = k.KeyChar - '0';
                                }

                                Console.WriteLine("正在单步调试……");
                                if (n < 0 || n > Instance.ThreadCount - 1)
                                {
                                    for (int i = 0; i < Instance.ThreadCount; i++)
                                    {
                                        serivce.Work(i);
                                    }
                                }
                                else
                                {
                                    serivce.Work(n);
                                }
                                Console.WriteLine("单步调试完成!");
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.ToString());
                            }
                            #endregion
                            break;
                        case 5:
                            #region 循环调试
                            try
                            {
                                Console.WriteLine("正在循环调试……");
                                serivce.StartWork();

                                Console.WriteLine("任意键结束循环调试!");
                                Console.ReadKey(true);

                                serivce.StopWork();
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.ToString());
                            }
                            #endregion
                            break;
                        case 6:
                            #region 附加服务
                            Console.WriteLine("正在附加服务调试……");
                            serivce.StartAttachServers();

                            Console.WriteLine("任意键结束附加服务调试!");
                            Console.ReadKey(true);

                            serivce.StopAttachServers();
                            #endregion
                            break;
                        case 7:
                            if (WatchDogs.Length > 0) CheckWatchDog();
                            break;
                        case 8:
                            RunUI();
                            break;
                        default:
                            break;
                    }
                }
                #endregion
            }
        }

        /// <summary>生成批处理</summary>
        protected virtual void MakeBat()
        {
            File.WriteAllText("安装.bat", ExeName + " -i");
            File.WriteAllText("卸载.bat", ExeName + " -u");
            File.WriteAllText("启动.bat", ExeName + " -start");
            File.WriteAllText("停止.bat", ExeName + " -stop");
        }

        //static void XTrace_OnWriteLog(object sender, WriteLogEventArgs e)
        //{
        //    Console.WriteLine(e.ToString());
        //}

        /// <summary>显示状态</summary>
        protected virtual void ShowStatus()
        {
            var color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;

            if (AgentServiceName != AgentDisplayName)
                Console.WriteLine("服务:{0}({1})", AgentDisplayName, AgentServiceName);
            else
                Console.WriteLine("服务:{0}", AgentServiceName);
            Console.WriteLine("描述:{0}", AgentDescription);
            Console.Write("状态:");
            if (IsInstalled == null)
                Console.WriteLine("未知");
            else if (IsInstalled == false)
                Console.WriteLine("未安装");
            else
            {
                if (IsRunning == null)
                    Console.WriteLine("未知");
                else
                {
                    if (IsRunning == false)
                        Console.WriteLine("未启动");
                    else
                        Console.WriteLine("运行中");
                }
            }

            var asm = AssemblyX.Create(Assembly.GetExecutingAssembly());
            Console.WriteLine();
            Console.WriteLine("核心:{0}", asm.Version);
            //Console.WriteLine("文件:{0}", asm.FileVersion);
            Console.WriteLine("发布:{0:yyyy-MM-dd HH:mm:ss}", asm.Compile);

            var asm2 = AssemblyX.Create(Assembly.GetEntryAssembly());
            if (asm2 != asm)
            {
                Console.WriteLine();
                Console.WriteLine("程序:{0}", asm2.Version);
                Console.WriteLine("发布:{0:yyyy-MM-dd HH:mm:ss}", asm2.Compile);
            }

            Console.ForegroundColor = color;
        }

        /// <summary>显示菜单</summary>
        protected virtual void ShowMenu()
        {
            var color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Yellow;

            Console.WriteLine();
            Console.WriteLine("1 显示状态");

            if (IsInstalled == true)
            {
                if (IsRunning == true)
                {
                    Console.WriteLine("3 停止服务 -stop");
                }
                else
                {
                    Console.WriteLine("2 卸载服务 -u");

                    Console.WriteLine("3 启动服务 -start");
                }
            }
            else
            {
                Console.WriteLine("2 安装服务 -i");
            }

            if (IsRunning != true)
            {
                Console.WriteLine("4 单步调试 -step");
                Console.WriteLine("5 循环调试 -run");

                var dic = Config.GetConfigByPrefix("XAgent.AttachServer.");
                if (dic != null && dic.Count > 0)
                {
                    var dic2 = new Dictionary<String, String>();
                    foreach (var item in dic2)
                    {
                        if (!item.Key.IsNullOrWhiteSpace() && !item.Value.IsNullOrWhiteSpace()) dic2.Add(item.Key, item.Value);
                    }
                    dic = dic2;

                    if (dic != null && dic.Count > 0)
                    {
                        Console.WriteLine("6 附加服务调试");
                        foreach (var item in dic)
                        {
                            Console.WriteLine("{0,10} = {1}", item.Key, item.Value);
                        }
                    }
                }
            }

            if (WatchDogs.Length > 0)
            {
                Console.WriteLine("7 看门狗保护服务 {0}", String.Join(",", WatchDogs));
            }

            Console.WriteLine("0 退出");

            Console.ForegroundColor = color;
        }
        #endregion

        #region 服务控制
        private Thread[] _Threads;
        /// <summary>线程组</summary>
        private Thread[] Threads { get { return _Threads ?? (_Threads = new Thread[ThreadCount]); } set { _Threads = value; } }

        private Dictionary<String, IServer> _AttachServers;
        /// <summary>附加服务</summary>
        public Dictionary<String, IServer> AttachServers { get { return _AttachServers ?? (_AttachServers = new Dictionary<string, IServer>()); } /*set { _AttachServers = value; }*/ }

        /// <summary>服务启动事件</summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            StartWork();

            // 处理附加服务
            try
            {
                StartAttachServers();
            }
            catch (Exception ex)
            {
                WriteLine(ex.ToString());
            }
        }

        /// <summary>服务停止事件</summary>
        protected override void OnStop()
        {
            StopWork();

            try
            {
                StopAttachServers();
            }
            catch (Exception ex)
            {
                WriteLine(ex.ToString());
            }
        }

        private void StartAttachServers()
        {
            var dic = Config.GetConfigByPrefix("XAgent.AttachServer.");
            if (dic != null && dic.Count > 0)
            {
                // 实例化
                foreach (var item in dic)
                {
                    if (!item.Key.IsNullOrWhiteSpace() && !item.Value.IsNullOrWhiteSpace())
                    {
                        WriteLine("");
                        WriteLine("正在加载:{0} = {1}", item.Key, item.Value);
                        var type = TypeX.GetType(item.Value, true);
                        if (type != null)
                        {
                            var service = TypeX.CreateInstance(type) as IServer;
                            if (service != null) AttachServers[item.Key] = service;
                        }
                    }
                }

                // 加载配置。【服务名.属性】的配置方式
                foreach (var item in AttachServers)
                {
                    if (item.Value != null)
                    {
                        var type = item.Value.GetType();
                        // 遍历所有属性,查找指定的设置项
                        foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty))
                        {
                            var name = String.Format("XAgent.{0}.{1}", item.Key, pi.Name);
                            Object value = null;
                            // 读取配置,并赋值
                            if (Config.TryGetConfig(name, pi.PropertyType, out value))
                            {
                                WriteLine("配置:{0} = {1}", name, value);
                                PropertyInfoX.Create(pi).SetValue(item.Value, value);
                            }
                        }
                    }
                }

                // 启动
                foreach (var item in AttachServers)
                {
                    if (item.Value != null)
                    {
                        WriteLine("启动:{0}", item.Key);
                        item.Value.Start();
                    }
                }
            }
        }

        private void StopAttachServers()
        {
            if (AttachServers != null)
            {
                foreach (var item in AttachServers.Values)
                {
                    if (item != null) item.Stop();
                }
            }
        }

        /// <summary>销毁资源</summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (AttachServers != null)
            {
                foreach (var item in AttachServers.Values)
                {
                    if (item != null && item is IDisposable) (item as IDisposable).Dispose();
                }
            }

            base.Dispose(disposing);
        }

        /// <summary>开始循环工作</summary>
        public virtual void StartWork()
        {
            WriteLine("服务启动");

            try
            {
                for (int i = 0; i < ThreadCount; i++)
                {
                    StartWork(i);
                }

                // 启动服务管理线程
                StartManagerThread();

                //// 显示用户界面交互窗体
                //Interactive.ShowForm();
            }
            catch (Exception ex)
            {
                WriteLine(ex.ToString());
            }
        }

        /// <summary>开始循环工作</summary>
        /// <param name="index">线程序号</param>
        public virtual void StartWork(Int32 index)
        {
            if (index < 0 || index >= ThreadCount) throw new ArgumentOutOfRangeException("index");

            // 可以通过设置任务的时间间隔小于0来关闭指定任务
            Int32 time = Intervals[0];
            // 使用专用的时间间隔
            if (index < Intervals.Length) time = Intervals[index];
            if (time < 0) return;

            Threads[index] = new Thread(workWaper);
            //String name = "XAgent_" + index;
            String name = "A" + index;
            if (ThreadNames != null && ThreadNames.Length > index && !String.IsNullOrEmpty(ThreadNames[index]))
                name = ThreadNames[index];
            Threads[index].Name = name;
            Threads[index].IsBackground = true;
            Threads[index].Priority = ThreadPriority.AboveNormal;
            Threads[index].Start(index);
        }

        /// <summary>线程包装</summary>
        /// <param name="data">线程序号</param>
        private void workWaper(Object data)
        {
            Int32 index = (Int32)data;

            // 旧异常
            Exception oldEx = null;

            while (true)
            {
                Boolean isContinute = false;
                Active[index] = DateTime.Now;

                try
                {
                    isContinute = Work(index);

                    oldEx = null;
                }
                catch (ThreadAbortException) //线程被取消
                {
                    WriteLine("线程" + index + "被取消!");
                    break;
                }
                catch (ThreadInterruptedException) //线程中断错误
                {
                    WriteLine("线程" + index + "中断错误!");
                    break;
                }
                catch (Exception ex) //确保拦截了所有的异常,保证服务稳定运行
                {
                    // 避免同样的异常信息连续出现,造成日志膨胀
                    if (oldEx == null || oldEx.GetType() != ex.GetType() || oldEx.Message != ex.Message)
                    {
                        oldEx = ex;

                        WriteLine(ex.ToString());
                    }
                }
                Active[index] = DateTime.Now;

                //检查服务是否正在重启
                if (IsShutdowning)
                {
                    WriteLine("服务准备重启," + Thread.CurrentThread.Name + "退出!");
                    break;
                }

                Int32 time = Intervals[0];
                //使用专用的时间间隔
                if (index < Intervals.Length) time = Intervals[index];

                //如果有数据库连接错误,则将等待间隔放大十倍
                //if (hasdberr) time *= 10;
                if (oldEx != null) time *= 10;

                if (!isContinute) Thread.Sleep(time * 1000);
            }
        }

        /// <summary>核心工作方法。调度线程会定期调用该方法</summary>
        /// <param name="index">线程序号</param>
        /// <returns>是否立即开始下一步工作。某些任务能达到满负荷,线程可以不做等待</returns>
        public virtual Boolean Work(Int32 index) { return false; }

        /// <summary>
        /// 停止循环工作。
        /// 只能停止循环而已,如果已经有一批任务在处理,
        /// 则内部需要捕获ThreadAbortException异常,否则无法停止任务处理。
        /// </summary>
        public virtual void StopWork()
        {
            WriteLine("服务停止");

            //停止服务管理线程
            StopManagerThread();

            //if (threads != null && threads.IsAlive) threads.Abort();
            if (Threads != null)
            {
                foreach (Thread item in Threads)
                {
                    try
                    {
                        if (item != null && item.IsAlive) item.Abort();
                    }
                    catch (Exception ex)
                    {
                        WriteLine(ex.ToString());
                    }
                }
            }

            //Interactive.Hide();
        }

        /// <summary>停止循环工作</summary>
        /// <param name="index">线程序号</param>
        public virtual void StopWork(Int32 index)
        {
            if (index < 0 || index >= ThreadCount) throw new ArgumentOutOfRangeException("index");

            Thread item = Threads[index];
            try
            {
                if (item != null && item.IsAlive) item.Abort();
            }
            catch (Exception ex)
            {
                WriteLine(ex.ToString());
            }
        }
        #endregion

        #region 服务维护线程
        /// <summary>服务管理线程</summary>
        private Thread ManagerThread;

        /// <summary>开始服务管理线程</summary>
        public void StartManagerThread()
        {
            ManagerThread = new Thread(ManagerThreadWaper);
            //ManagerThread.Name = "XAgent_Manager";
            ManagerThread.Name = "AM";
            ManagerThread.IsBackground = true;
            ManagerThread.Priority = ThreadPriority.Highest;
            ManagerThread.Start();
        }

        /// <summary>停止服务管理线程</summary>
        public void StopManagerThread()
        {
            if (ManagerThread == null) return;
            if (ManagerThread.IsAlive)
            {
                try
                {
                    ManagerThread.Abort();
                }
                catch (Exception ex)
                {
                    WriteLine(ex.ToString());
                }
            }
        }

        /// <summary>服务管理线程封装</summary>
        /// <param name="data"></param>
        protected virtual void ManagerThreadWaper(Object data)
        {
            while (true)
            {
                try
                {
                    CheckActive();

                    //如果某一项检查需要重启服务,则返回true,这里跳出循环,等待服务重启
                    if (CheckMemory()) break;
                    if (CheckThread()) break;
                    if (CheckAutoRestart()) break;

                    // 检查看门狗
                    //CheckWatchDog();
                    if (WatchDogs.Length > 0) ThreadPoolX.QueueUserWorkItem(CheckWatchDog);

                    Thread.Sleep(60 * 1000);
                }
                catch (ThreadAbortException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    WriteLine(ex.ToString());
                }
            }
        }

        private DateTime[] _Active;
        /// <summary>活动时间</summary>
        public DateTime[] Active
        {
            get
            {
                if (_Active == null)
                {
                    _Active = new DateTime[ThreadCount];
                    for (int i = 0; i < ThreadCount; i++)
                    {
                        _Active[i] = DateTime.Now;
                    }
                }
                return _Active;
            }
            set { _Active = value; }
        }

        /// <summary>检查是否有工作线程死亡</summary>
        protected virtual void CheckActive()
        {
            if (Threads == null || Threads.Length < 1) return;

            //检查已经停止了的工作线程
            for (int i = 0; i < ThreadCount; i++)
            {
                if (Threads[i] != null && !Threads[i].IsAlive)
                {
                    WriteLine(Threads[i].Name + "处于停止状态,准备重新启动!");

                    StartWork(i);
                }
            }

            //是否检查最大活动时间
            if (MaxActive <= 0) return;

            for (int i = 0; i < ThreadCount; i++)
            {
                TimeSpan ts = DateTime.Now - Active[i];
                if (ts.TotalSeconds > MaxActive)
                {
                    WriteLine(Threads[i].Name + "已经" + ts.TotalSeconds + "秒没有活动了,准备重新启动!");

                    StopWork(i);
                    //等待线程结束
                    Threads[i].Join(5000);
                    StartWork(i);
                }
            }
        }

        /// <summary>检查内存是否超标</summary>
        /// <returns>是否超标重启</returns>
        protected virtual Boolean CheckMemory()
        {
            if (MaxMemory <= 0) return false;

            Process p = Process.GetCurrentProcess();
            long cur = p.WorkingSet64 + p.PrivateMemorySize64;
            cur = cur / 1024 / 1024;
            if (cur > MaxMemory)
            {
                WriteLine("当前进程占用内存" + cur + "M,超过阀值" + MaxMemory + "M,准备重新启动!");

                RestartService();

                return true;
            }

            return false;
        }

        /// <summary>检查服务进程的总线程数是否超标</summary>
        /// <returns></returns>
        protected virtual Boolean CheckThread()
        {
            if (MaxThread <= 0) return false;

            Process p = Process.GetCurrentProcess();
            if (p.Threads.Count > MaxThread)
            {
                WriteLine("当前进程总线程" + p.Threads.Count + "个,超过阀值" + MaxThread + "个,准备重新启动!");

                RestartService();

                return true;
            }

            return false;
        }

        /// <summary>服务开始时间</summary>
        private DateTime Start = DateTime.Now;

        /// <summary>检查自动重启</summary>
        /// <returns></returns>
        protected virtual Boolean CheckAutoRestart()
        {
            if (AutoRestart <= 0) return false;

            TimeSpan ts = DateTime.Now - Start;
            if (ts.TotalMinutes > AutoRestart)
            {
                WriteLine("服务已运行" + ts.TotalMinutes + "分钟,达到预设重启时间(" + AutoRestart + "分钟),准备重启!");

                RestartService();

                return true;
            }

            return false;
        }

        /// <summary>是否正在重启</summary>
        private Boolean IsShutdowning = false;

        /// <summary>重启服务</summary>
        protected void RestartService()
        {
            WriteLine("重启服务!");

            //在临时目录生成重启服务的批处理文件
            String filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "重启.bat");
            if (File.Exists(filename)) File.Delete(filename);

            File.AppendAllText(filename, "net stop " + AgentServiceName);
            File.AppendAllText(filename, Environment.NewLine);
            File.AppendAllText(filename, "ping 127.0.0.1 -n 5 > nul ");
            File.AppendAllText(filename, Environment.NewLine);
            File.AppendAllText(filename, "net start " + AgentServiceName);

            //准备重启服务,等待所有工作线程返回
            IsShutdowning = true;
            for (int i = 0; i < 10; i++)
            {
                Boolean b = false;
                foreach (Thread item in Threads)
                {
                    if (item.IsAlive)
                    {
                        b = true;
                        break;
                    }
                }
                if (!b) break;
                Thread.Sleep(1000);
            }

            //执行重启服务的批处理
            //RunCmd(filename, false, false);
            Process p = new Process();
            ProcessStartInfo si = new ProcessStartInfo();
            si.FileName = filename;
            si.UseShellExecute = true;
            si.CreateNoWindow = true;
            p.StartInfo = si;

            p.Start();

            //if (File.Exists(filename)) File.Delete(filename);
        }
        #endregion

        #region 看门狗
        private static String[] _WatchDogs;
        /// <summary>看门狗要保护的服务</summary>
        public static String[] WatchDogs
        {
            get
            {
                if (_WatchDogs == null)
                {
                    _WatchDogs = Config.GetConfigSplit<String>("XAgent.WatchLog", null);
                    if (_WatchDogs == null) _WatchDogs = new String[0];
                }
                return _WatchDogs;
            }
        }

        /// <summary>检查看门狗。</summary>
        /// <remarks>
        /// XAgent看门狗功能由管理线程完成,每分钟一次。
        /// 检查指定的任务是否已经停止,如果已经停止,则启动它。
        /// </remarks>
        public static void CheckWatchDog()
        {
            var ss = Config.GetConfigSplit<String>("XAgent.WatchLog", null);
            if (ss == null || ss.Length < 1) return;

            foreach (var item in ss)
            {
                // 注意:IsServiceRunning返回三种状态,null表示未知
                if (IsServiceRunning(item) == false)
                {
                    WriteLine("发现服务{0}被关闭,准备启动!", item);

                    RunCmd("net start " + item, false, true);
                }
            }
        }
        #endregion
    }
}