C#对接supervisor XML-RPC API 实现进程控制

前言

最近在考虑如何实现平台的自动化运维监控,就是将系统的服务器、以及在服务器上运行的程序应用通过平台自行监控起来。
在此之前,有用过supervisor,并且使用了supervisor自带的web也能实现对程序的监控,具体实现可以查看我之前写的一篇关于supervisor配置的博客《Linux进程守护—Supervisor(ubuntu)》,但是带的那个web界面风格太过于简陋,如下图,对于目前这个看颜的时代明显不太符合要求。
在这里插入图片描述
所以我就在思考能否自己做个高大尚的界面,来实现这个监控目的。查了些资料了解到supervisor自带了XML-RPC接口《XML-RPC API Documentation

思考

既然有方法,那么我们就有了继续下去的方向,网上也有很多关于Supervisor接口对接的方法,但是大多都是用python实现的,可能因为supervisor是基于python语言开发的,针对supervisor的控制,python实现的资料也是相对较多。但是我这边项目使用的是.NET开发的web API,如果为了实现起来省事再单独做个python的WebApi,后期维护也是一个麻烦事,所以我就想尝试通过.NET来实现对Supervisor接口的调用。
既然资料不多,我们就摸索着来对接,既然了解到Supervisor的API是基于XML-RPC来实现的,那么我们就直接找有关C#实现XML-RPC的实现资料,最终终于找到了一个dll库(CookComputing.XmlRpcV2.dll),避免自己造轮子了。
我们在NuGet里面搜索xmlrpcnet就可以找到它,点击安装即可
在这里插入图片描述

实现

既然找到了轮子,那就进行下一步实现环节,首先我们找到自己想要对接的功能,可以仔细阅读一下XML-RPC API Documentation根据自己的需求选择想要实现功能。

1. 首先定义一个接口类ICallSupervisor

using CookComputing.XmlRpc;
using DevOps.SupervisorEntity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DevOps
{
    public interface ICallSupervisor : IXmlRpcProxy
    {
        /// <summary>
        /// 获取supervisor的所有方法
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("system.listMethods")]
        string[] listMethods();

        /// <summary>
        /// 获取Supervisor的版本信息
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.getSupervisorVersion")]
        string getSupervisorVersion();

        /// <summary>
        /// 获取Supervisor运行状态
        /// </summary>
        /// <returns></returns>

        [XmlRpcMethod("supervisor.getState")]
        SupervisorStruct getState();

        /// <summary>
        /// 获取Supervisor的PID
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.getPID")]
        int getPID();

        /// <summary>
        /// 关闭Supervisor的进程
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.shutdown")]
        bool shutdown();

        /// <summary>
        /// 重启Supervisor的进程
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.restart")]
        bool restart();

        /// <summary>
        /// 获取监控的所有进程信息
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.getAllProcessInfo")]
        ProcessStruct[] getAllProcessInfo();

        /// <summary>
        /// 启动进程
        /// </summary>
        /// <param name="name">group:name</param>
        /// <param name="wait">是否等待启动完毕</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.startProcess")]
        bool startProcess(string name, bool wait);

        /// <summary>
        /// 启动所有进程
        /// </summary>
        /// <param name="wait">是否等待启动完毕</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.startAllProcesses")]
        ProcessStateStruct[] startAllProcesses(bool wait);

        /// <summary>
        /// 启动该组所有进程
        /// </summary>
        /// <param name="name">group</param>
        /// <param name="wait">是否等待启动完毕</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.startProcessGroup")]
        ProcessStateStruct[] startProcessGroup(string name, bool wait);

        /// <summary>
        /// 关闭进程
        /// </summary>
        /// <param name="name">group:name</param>
        /// <param name="wait">是否等待状态返回</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.stopProcess")]
        bool stopProcess(string name, bool wait);

        /// <summary>
        /// 关闭该组进程
        /// </summary>
        /// <param name="name">group</param>
        /// <param name="wait">是否等待状态返回</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.stopProcessGroup")]
        ProcessStateStruct[] stopProcessGroup(string name, bool wait);

        /// <summary>
        /// 关闭所有进程
        /// </summary>
        /// <param name="wait">是否等待状态返回</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.stopAllProcesses")]
        ProcessStateStruct[] stopAllProcesses(bool wait);

        /// <summary>
        /// 向进程发送UNIX信号
        /// </summary>
        /// <param name="name">group:name</param>
        /// <param name="signal">UNIX信号</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.signalProcess")]
        bool signalProcess(string name, string signal);

        /// <summary>
        /// 向进程组发送UNIX信号
        /// </summary>
        /// <param name="name">group</param>
        /// <param name="signal">UNIX信号</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.signalProcessGroup")]
        object signalProcessGroup(string name, string signal);

        /// <summary>
        /// 向所有发送UNIX信号
        /// </summary>
        /// <param name="signal">UNIX信号</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.signalAllProcesses")]
        object signalAllProcesses(string signal);
        
        /// <summary>
        /// 向进程发送字符串
        /// </summary>
        /// <param name="name">group:name</param>
        /// <param name="chars">UNIX信号</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.sendProcessStdin")]
        bool sendProcessStdin(string name, string chars);

        /// <summary>
        /// 重新加载配置文件
        /// </summary>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.reloadConfig")]
        object reloadConfig();

        /// <summary>
        /// 从配置文件中将运行的进程加载出来
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.addProcessGroup")]
        bool addProcessGroup(string name);

        /// <summary>
        /// 从配置文件中将停止的进程移除
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.removeProcessGroup")]
        bool removeProcessGroup(string name);

        /// <summary>
        /// 读取进程的stdout log
        /// </summary>
        /// <param name="name">group:name</param>
        /// <param name="offset">start reading from</param>
        /// <param name="length">length number of bytes to read</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.readProcessStdoutLog")]
        object readProcessStdoutLog(string name, int offset, int length);
        
        /// <summary>
        /// 读取进程的stderr log
        /// </summary>
        /// <param name="name">group:name</param>
        /// <param name="offset">start reading from</param>
        /// <param name="length">length number of bytes to read</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.readProcessStderrLog")]
        object readProcessStderrLog(string name, int offset, int length);

        /// <summary>
        /// 跟踪进程的stdout log
        /// </summary>
        /// <param name="name"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.tailProcessStdoutLog")]
        object tailProcessStdoutLog(string name, int offset, int length);
        
        /// <summary>
        /// 跟踪进程的stderr log
        /// </summary>
        /// <param name="name"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.tailProcessStderrLog")]
        object tailProcessStderrLog(string name, int offset, int length);
        
        /// <summary>
        /// 清理单个进程的日志
        /// </summary>
        /// <param name="name">group:name</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.clearProcessLogs")]
        bool clearProcessLogs(string name);
        
        /// <summary>
        /// 清理所有进程的日志
        /// </summary>
        /// <param name="name">group:name</param>
        /// <returns></returns>
        [XmlRpcMethod("supervisor.clearAllProcessLogs")]
        object clearAllProcessLogs();
    }
}

2. 封装Supervisor操作方法

    public class Supervisor
    {
        ICallSupervisor proxy = null;
        public Supervisor(string url,string username,string password)
        {
            proxy = XmlRpcProxyGen.Create<ICallSupervisor>();
            proxy.Url = url;
            proxy.Credentials = new NetworkCredential(username, password);
            proxy.PreAuthenticate = true;
            proxy.XmlEncoding = Encoding.UTF8;
        }

        #region Supervisor信息接口
        /// <summary>
        /// 获取Supervisor的版本信息
        /// </summary>
        /// <returns></returns>
        public string getSupervisorVersion()
        {
            return proxy.getSupervisorVersion();
        }

        /// <summary>
        /// 获取Supervisor运行状态
        /// </summary>
        /// <returns></returns>
        public int getState()
        {
            SupervisorStruct state = proxy.getState();
            return state.statecode;
        }

        /// <summary>
        /// 关闭Supervisor的进程
        /// </summary>
        /// <returns></returns>
        public bool shutdown()
        {
            return proxy.shutdown();
        }

        /// <summary>
        /// 重启Supervisor的进程
        /// </summary>
        /// <returns></returns>
        public bool restart()
        {
            return proxy.restart();
        }
        #endregion

        #region 进程管控接口

        /// <summary>
        /// 获取监控的所有进程信息
        /// </summary>
        /// <returns></returns>
        public List<MT_SupervisorProcess> getAllProcessInfo()
        {
            try
            {
                List<MT_SupervisorProcess> list = new List<MT_SupervisorProcess>();
                ProcessStruct[] structArr = proxy.getAllProcessInfo();
                foreach (ProcessStruct item in structArr)
                {
                    MT_SupervisorProcess process = new MT_SupervisorProcess();
                    process.FPID = item.pid;
                    process.FGroup = item.group;
                    process.FName = item.name;
                    process.FStart = General.Instance.StampToDateTime(item.start * 1000);
                    process.FNow = General.Instance.StampToDateTime(item.now * 1000);
                    process.FState = item.state;
                    process.FRunDuration = item.now - item.start;
                    list.Add(process);
                }
                return list;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("getAllProcessInfo:" + ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 启动进程
        /// </summary>
        /// <param name="group"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool startProcess(string group, string name)
        {
            try
            {
                string paraName = string.Format("{0}:{1}", group, name);
                return proxy.startProcess(paraName, true);
            }
            catch (Exception ex)
            {
                Log.Instance.Error("startProcess:" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 启动所有进程
        /// </summary>
        /// <returns></returns>
        public List<MT_SupervisorProcessState> startAllProcesses()
        {
            try
            {
                List<MT_SupervisorProcessState> list = new List<MT_SupervisorProcessState>();
                ProcessStateStruct[] stateArr = proxy.startAllProcesses(true);
                foreach (ProcessStateStruct item in stateArr)
                {
                    MT_SupervisorProcessState state = new MT_SupervisorProcessState();
                    state.FGroup = item.group;
                    state.FName = item.name;
                    state.FStatus = item.status;
                    state.FDescription = item.description;
                    list.Add(state);
                }
                return list;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("startAllProcesses:" + ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 关闭进程
        /// </summary>
        /// <param name="group"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool stopProcess(string group, string name)
        {
            try
            {
                string paraName = string.Format("{0}:{1}", group, name);
                return proxy.stopProcess(paraName, true);
            }
            catch (Exception ex)
            {
                Log.Instance.Error("stopProcess:" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 关闭所有进程
        /// </summary>
        /// <returns></returns>
        public List<MT_SupervisorProcessState> stopAllProcesses()
        {
            try
            {
                List<MT_SupervisorProcessState> list = new List<MT_SupervisorProcessState>();
                ProcessStateStruct[] stateArr = proxy.stopAllProcesses(true);
                foreach (ProcessStateStruct item in stateArr)
                {
                    MT_SupervisorProcessState state = new MT_SupervisorProcessState();
                    state.FGroup = item.group;
                    state.FName = item.name;
                    state.FStatus = item.status;
                    state.FDescription = item.description;
                    list.Add(state);
                }
                return list;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("stopAllProcesses:" + ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 重启所有进程
        /// </summary>
        /// <param name="group"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool restartProcess(string group, string name)
        {
            try
            {
                string paraName = string.Format("{0}:{1}", group, name);
                bool stopState = proxy.stopProcess(paraName, true);
                if (stopState)
                {
                    bool startState = proxy.startProcess(paraName, true);
                    if (startState)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                Log.Instance.Error("restartProcess:" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 重启所有进程
        /// </summary>
        /// <returns></returns>
        public bool restartAllProcess()
        {
            try
            {
                ProcessStateStruct[] stateStopArr = proxy.stopAllProcesses(true);
                ProcessStateStruct[] stateStartArr = proxy.startAllProcesses(true);
                return true;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("restartAllProcess:"+ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 重新加载配置文件
        /// </summary>
        /// <returns></returns>
        public bool reloadConfig()
        {
            try
            {
                proxy.reloadConfig();
                return true;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("reloadConfig:"+ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 清理单个进程的日志
        /// </summary>
        /// <param name="group"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool clearProcessLogs(string group, string name)
        {
            try
            {
                string paraName = string.Format("{0}:{1}", group, name);
                bool state = proxy.clearProcessLogs(paraName);
                return state;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("clearProcessLogs:" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 清理所有进程的日志
        /// </summary>
        /// <returns></returns>
        public bool clearAllProcessLogs()
        {
            try
            {
                object obj = proxy.clearAllProcessLogs();
                return true;
            }
            catch (Exception ex)
            {
                Log.Instance.Error("clearAllProcessLogs:" + ex.Message);
                return false;
            }
        }
        #endregion
    }

最后在WebApi实现方法里面调用这些方法,提供给前端调用。
这样我们就可以根据自己的喜好设计自己的界面风格从而让自动化运维界面显得高大尚。

最后

在这里插入图片描述
在这块代码里面我是将Supervisor接口地址,以及鉴权使用的用户名,密码作为参数传入进来的,可以在Supervisor的配置文件中找到这三项信息。
在这里插入图片描述
技术之路是在思考与不断的探索中慢慢铺垫的,有兴趣的小伙伴可以扫码加我微信,一起思考,一起学习,一起进步!
请添加图片描述


版权声明:本文为qq_17486399原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。