时序数据库InfluxDb服务化

时序数据库InfluxDb服务化

  • 背景
  • 环境
  • 代码实现

背景

        因工控软件开发中需要保存大量设备数据,因此通过在网上查阅发现时序数据库InfluxDb用的还是很多的,InfluxDb集群版是商业软件,且在工控机上也不太适合用集群版本。单机版InfluxDb是基于命令的,一直有个黑窗口,现场操作工不小心很容易把它关掉,因此我就把InfluxDb封装成一个Windows服务,隐藏掉黑窗口,并随开机自启动。

环境

InfluxDb版本:influxdb-1.4.3-1
开发语言:C#
Windows宿主框架:TopShelf
任务调度框架:Quartz
日志记录框架:Log4net

代码实现

关键代码就是如何隐藏黑窗口打开Influxd.exe进程,如下

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace Mesnac.Basic
{
    /// 
    /// windows命令辅助类
    /// 
    public class WinCmdHelper
    {
        #region 执行Dos命令

        /// 
        /// 执行Dos命令
        /// 
        /// Dos命令及参数
        /// 是否显示cmd窗口
        /// 执行完毕后是否关闭cmd进程
        /// 成功返回true,失败返回false
        public static bool ExcuteDosCommand(string cmd, bool isShowCmdWindow, bool isCloseCmdProcess)
        {
            try
            {
                Process p = new Process();
                p.StartInfo.FileName = "cmd";
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardInput = true;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.CreateNoWindow = !isShowCmdWindow;
                p.OutputDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e) {
                    if (!String.IsNullOrEmpty(e.Data))
                    {
                        Mesnac.Log.LogService<WinCmdHelper>.Instance.Debug(e.Data);
                    }
                });
                p.Start();
                StreamWriter cmdWriter = p.StandardInput;
                p.BeginOutputReadLine();
                if (!String.IsNullOrEmpty(cmd))
                {
                    cmdWriter.WriteLine(cmd);
                }
                cmdWriter.Close();
                p.WaitForExit();
                if (isCloseCmdProcess)
                {
                    p.Close();
                }
                Mesnac.Log.LogService<WinCmdHelper>.Instance.Info(String.Format("成功执行Dos命令[{0}]!", cmd));
                return true;
            }
            catch (Exception ex)
            {
                Mesnac.Log.LogService<WinCmdHelper>.Instance.Error("执行命令失败,请检查输入的命令是否正确:" + ex.Message, ex);
                return false;
            }
        }

        #endregion

        #region 判断指定的进程是否在运行中

        /// 
        /// 判断指定的进程是否在运行中
        /// 
        /// 要判断的进程名称,不包括扩展名exe
        /// 进程文件的完整路径
        /// 存在返回true,否则返回false
        public static bool CheckProcessExists(string processName, string processFileName)
        {
            Process[] processes = Process.GetProcessesByName(processName);
            foreach (Process p in processes)
            {
                if (!String.IsNullOrEmpty(processFileName))
                {
                    if (processFileName == p.MainModule.FileName)
                    {
                        return true;
                    }
                }
                else
                {
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region 结束指定的windows进程

        /// 
        /// 结束指定的windows进程,如果进程存在
        /// 
        /// 进程名称,不包含扩展名
        /// 进程文件完整路径,如果为空则删除所有进程名为processName参数值的进程
        public static bool KillProcessExists(string processName, string processFileName)
        {
            try
            {
                Process[] processes = Process.GetProcessesByName(processName);
                foreach (Process p in processes)
                {
                    if (!String.IsNullOrEmpty(processFileName))
                    {
                        if (processFileName == p.MainModule.FileName)
                        {
                            p.Kill();
                            p.Close();
                        }
                    }
                    else
                    {
                        p.Kill();
                        p.Close();
                    }
                }
                Mesnac.Log.LogService<WinCmdHelper>.Instance.Info(String.Format("成功结束[{0}]进程!", processes));
                return true;
            }
            catch(Exception ex)
            {
                Mesnac.Log.LogService<WinCmdHelper>.Instance.Error("结束指定的Widnows进程异常:" + ex.Message, ex);
                return false;
            }
        }

        #endregion
    }
}

读取App.config的辅助类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;

namespace Mesnac.Basic
{
    /// 
    /// 应用程序配置文件访问辅助类
    /// 
    public class AppConfigHelper
    {
        /// 
        /// 读取string配置项
        /// 
        /// AppSetting配置项的Key值
        /// 默认值
        /// 返回对应配置项的string类型value值
        public static string GetAppSettingValue(string appSettingKey, string defaultValue)
        {
            string result = ConfigurationManager.AppSettings[appSettingKey];
            if (String.IsNullOrEmpty(result))
            {
                return defaultValue;
            }
            return result;
        }

        /// 
        /// 读取bool配置项
        /// 
        /// AppSetting配置项的Key值
        /// 默认值
        /// 返回对应配置项的bool类型value值
        public static bool GetAppSettingValue(string appSettingKey, bool defaultValue)
        {
            string result = ConfigurationManager.AppSettings[appSettingKey];
            if (String.IsNullOrEmpty(result))
            {
                return defaultValue;
            }
            bool boolResult = false;
            if (bool.TryParse(result, out boolResult))
            {
                return boolResult;
            }
            else
            {
                return defaultValue;
            }
        }

        /// 
        /// 读取int配置项
        /// 
        /// AppSetting配置项的Key值
        /// 默认值
        /// 返回对应配置项的int类型value值
        public static int GetAppSettingValue(string appSettingKey, int defaultValue)
        {
            string result = ConfigurationManager.AppSettings[appSettingKey];
            if (String.IsNullOrEmpty(result))
            {
                return defaultValue;
            }
            int intResult = 0;
            if (int.TryParse(result, out intResult))
            {
                return intResult;
            }
            else
            {
                return defaultValue;
            }
        }

        /// 
        /// 读取double类型配置项
        /// 
        /// AppSetting配置项的Key值
        /// 默认值
        /// 返回对应配置项的double类型value值
        public static double GetAppSettingValue(string appSettingKey, double defaultValue)
        {
            string result = ConfigurationManager.AppSettings[appSettingKey];
            if (String.IsNullOrEmpty(result))
            {
                return defaultValue;
            }
            double doubleResult = 0.0;
            if (double.TryParse(result, out doubleResult))
            {
                return doubleResult;
            }
            else
            {
                return defaultValue;
            }
        }
    }
}

日志框架封装代码

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.IO;
using log4net;
using log4net.Config;

namespace Mesnac.Log
{
    /// 
    /// 日志服务类,封装Log4Net
    /// 
    public sealed class LogService<T>
    {
        #region 单例实现

        private static LogService<T> _instance = null;
        private ILog log;

        private LogService()
        {
            XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
            log = LogManager.GetLogger(typeof(T));
        }

        public static LogService<T> Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new LogService<T>();
                }
                return _instance;
            }
        }

        #endregion

        #region 封装log4net的ILog方法

        public void Debug(object message)
        {
            log.Debug(message);
        }

        public void DebugFormatted(string format, params object[] args)
        {
            log.DebugFormat(CultureInfo.InvariantCulture, format, args);
        }

        public void Info(object message)
        {
            log.Info(message);
        }

        public void InfoFormatted(string format, params object[] args)
        {
            log.InfoFormat(CultureInfo.InvariantCulture, format, args);
        }

        public void Warn(object message)
        {
            log.Warn(message);
        }

        public void Warn(object message, Exception exception)
        {
            log.Warn(message, exception);
        }

        public void WarnFormatted(string format, params object[] args)
        {
            log.WarnFormat(CultureInfo.InvariantCulture, format, args);
        }

        public void Error(object message)
        {
            log.Error(message);
        }

        public void Error(object message, Exception exception)
        {
            log.Error(message, exception);
        }

        public void ErrorFormatted(string format, params object[] args)
        {
            log.ErrorFormat(CultureInfo.InvariantCulture, format, args);
        }

        public void Fatal(object message)
        {
            log.Fatal(message);
        }

        public void Fatal(object message, Exception exception)
        {
            log.Fatal(message, exception);
        }

        public void FatalFormatted(string format, params object[] args)
        {
            log.FatalFormat(CultureInfo.InvariantCulture, format, args);
        }

        public bool IsDebugEnabled
        {
            get
            {
                return log.IsDebugEnabled;
            }
        }

        public bool IsInfoEnabled
        {
            get
            {
                return log.IsInfoEnabled;
            }
        }

        public bool IsWarnEnabled
        {
            get
            {
                return log.IsWarnEnabled;
            }
        }

        public bool IsErrorEnabled
        {
            get
            {
                return log.IsErrorEnabled;
            }
        }

        public bool IsFatalEnabled
        {
            get
            {
                return log.IsFatalEnabled;
            }
        }

        #endregion

        #region 删除日志文件
        /// 
        /// 删除日志
        /// 
        public void DeleteLogFile()
        {
            try
            {
                int maxSize = 90;       //默认保留90天的日志

                if (!String.IsNullOrEmpty(System.Configuration.ConfigurationManager.AppSettings["LogSaveDays"]))
                {
                    string strMaxSize = System.Configuration.ConfigurationManager.AppSettings["LogSaveDays"];
                    int.TryParse(strMaxSize, out maxSize);
                }

                string logFilePath = String.Empty;      //定义日志文件目录变量
                log4net.Appender.IAppender[] appenders = this.log.Logger.Repository.GetAppenders();
                foreach(log4net.Appender.IAppender appender in appenders)
                {
                    if (appender.Name == "FileAppender" && appender.GetType().Name == "RollingFileAppender")
                    {
                        log4net.Appender.RollingFileAppender rollingFileAppender = appender as log4net.Appender.RollingFileAppender;
                        logFilePath = Path.GetDirectoryName(rollingFileAppender.File);     //获取日志文件目录
                        break;
                    }
                }
                if (!String.IsNullOrEmpty(logFilePath))
                {
                    //logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, logFilePath);
                    if (Directory.Exists(logFilePath))
                    {
                        DirectoryInfo dir = new DirectoryInfo(logFilePath);
                        FileInfo[] logFiles = dir.GetFiles();
                        if (logFiles != null && logFiles.Length > maxSize)
                        {
                            Dictionary<string, FileInfo> dicFiles = new Dictionary<string, FileInfo>();
                            List<string> lstFileNames = new List<string>();
                            foreach (FileInfo fi in logFiles)
                            {
                                dicFiles.Add(fi.FullName, fi);
                                lstFileNames.Add(fi.FullName);
                            }
                            lstFileNames.Sort();
                            for (int i = 0; i < lstFileNames.Count - maxSize; i++)
                            {
                                dicFiles[lstFileNames[i]].Delete();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.Error("删除日志文件发生错误:" + ex.Message);
            }
        }

        #endregion
    }
}

写一个启动influxd.exe的任务类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Mesnac.Log;
using Quartz;

namespace Mesnac.Jobs
{
    public class StartInfluxDbServerJob : IJob
    {
        private string influxDbPath = @"D:\influxdb-1.4.3-1";
        public void Execute(IJobExecutionContext context)
        {
            if (!Mesnac.Basic.WinCmdHelper.CheckProcessExists("influxd", String.Empty))
            {
                //System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(this.DoStart));
                //t.Start();
                this.DoStart();
            }
        }

        public void DoStart()
        {
            string cmd = String.Format("{0} -config {1}", System.IO.Path.Combine(this.influxDbPath, "influxd.exe"), System.IO.Path.Combine(this.influxDbPath, "influxdb.conf"));
            bool result = Mesnac.Basic.WinCmdHelper.ExcuteDosCommand(cmd, false, false);
            if (result)
            {
                LogService<StartInfluxDbServerJob>.Instance.Info("The InfluxDb Server start success!");
            }
            else
            {
                LogService<StartInfluxDbServerJob>.Instance.Error("The InfluxDb Server Start failure!");
            }
        }
    }
}

写任务调度类

using System;
using System.Collections.Generic;
using System.Text;
using Mesnac.Log;
using Topshelf;
using Quartz;
using Quartz.Impl;

namespace InfluxDbService
{
    public sealed class ServiceRunner : ServiceControl, ServiceSuspend
    {
        private readonly IScheduler scheduler;  //定义任务调度器

        /// 
        /// 构造方法
        /// 
        public ServiceRunner()
        {
            scheduler = StdSchedulerFactory.GetDefaultScheduler();          //从任务调度工厂获取默认的任务调度器
        }

        /// 
        /// 服务启动
        /// 
        /// 
        /// 
        public bool Start(HostControl hostControl)
        {
            try
            {
                this.scheduler.Start();
                LogService<ServiceRunner>.Instance.Info("已启动InfluxDbServer!");
                return true;
            }
            catch (Exception ex)
            {
                LogService<ServiceRunner>.Instance.Error("服务启动失败:" + ex.Message);
                return false;
            }
        }

        /// 
        /// 服务继续
        /// 
        /// 
        /// 
        public bool Continue(HostControl hostControl)
        {
            try
            {
                this.scheduler.ResumeAll();
                return true;
            }
            catch (Exception ex)
            {
                LogService<ServiceRunner>.Instance.Error("服务继续失败:" + ex.Message);
                return false;
            }
        }

        /// 
        /// 服务暂停
        /// 
        /// 
        /// 
        public bool Pause(HostControl hostControl)
        {
            try
            {
                this.scheduler.PauseAll();
                return true;
            }
            catch (Exception ex)
            {
                LogService<ServiceRunner>.Instance.Error("服务暂停失败:" + ex.Message);
                return false;
            }
        }

        /// 
        /// 服务停止
        /// 
        /// 
        /// 
        public bool Stop(HostControl hostControl)
        {
            try
            {
                bool result = Mesnac.Basic.WinCmdHelper.KillProcessExists("influxd", String.Empty);
                if (result)
                {
                    LogService<ServiceRunner>.Instance.Info("已终止InfluxDbServer!");
                }
                this.scheduler.Shutdown(false);
                return true;
            }
            catch (Exception ex)
            {
                LogService<ServiceRunner>.Instance.Error("服务停止失败:" + ex.Message);
                return false;
            }
        }
    }
}

程序入口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Topshelf;
using Topshelf.HostConfigurators;

namespace InfluxDbService
{
    class Program
    {
        static void Main(string[] args)
        {
            Global.Init();

            Action<HostConfigurator> d = new Action<HostConfigurator>(Global.ActionMethod);
            HostFactory.Run(d);
        }
    }
}

完整代码下载:时序数据库InfluxDb服务化完整代码

你可能感兴趣的:(InfluxDb)