.NET:監視およびサーバー管理用の拡張可能なプラグインシステムの作成

こんにちは、私の意見では、非常に興味深く、おそらく誰かにとって役立つプロジェクトについてお話したいと思います。



タスクは、さまざまな人が作成するプラグインでシステムを拡張可能にすることでした。 プラグインは、サーバーから情報を収集(パフォーマンス、インストールされているプログラムのバージョン、データベースからの選択)するか、何らかの変更を加える(サービスの再起動など)必要があります。 サーバーとの通信は、TCPプロトコルと特定のポートを介して実行する必要があります。 結果はHTMLページに表示する必要があります。 さまざまな都市に多くのサーバーがあり、接続は非常に悪く、サーバーの生産性はそれほど高くありません。ソフトウェアは1日に最大10回更新できます。一般に、パフォーマンスを完全に制御する必要があります。



推論した後、それは決定されました:







開発は、IPluginインターフェイスの説明から始まりました。




namespace Interface { public interface IPlugin { string Name { get; } string Version { get; } string Author { get; } object Data(Dictionary<string, string> query); } }
      
      







Data()は、ブラウザのリクエストでサービスが呼び出す関数です。 また、クエリにはパラメータが含まれます。



次のステップは、サービスを作成することです。






このアルゴリズムの実装


 class HostServer : IDisposable { private Thread mThread; private HttpListener listener; private static string sVersion { get; set; } private static string sName { get; set; } private static string sIPAdress { get; set; } private static List<IPlugin> sPlugins { get; set; } public HostServer(int port) { sVersion = "0.6"; sName = "ISHOST"; sIPAdress = "http://" + System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[0].ToString() + ":" + port.ToString() + "/"; LoadPlugins(); listener = new HttpListener(); listener.Prefixes.Add(sIPAdress); } private static void LoadPlugins() { try { sPlugins = new List<IPlugin>(); string folder = System.AppDomain.CurrentDomain.BaseDirectory; string[] files = System.IO.Directory.GetFiles(folder, "*.dll"); foreach (string file in files) { IPlugin plugin = IsPlugin(file); if (plugin != null) sPlugins.Add(plugin); } } catch (Exception e) { ToLog(e.Message); } } private static IPlugin IsPlugin(byte[] file) { try { System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(file); foreach (Type type in assembly.GetTypes()) { Type iface = type.GetInterface("Interface.IPlugin"); if (iface != null) { IPlugin plugin = (IPlugin)Activator.CreateInstance(type); return plugin; } } } catch (Exception e) { ToLog(e.Message); } return null; } private static IPlugin IsPlugin(string file_url) { try { byte[] b = System.IO.File.ReadAllBytes(file_url); return IsPlugin(b); } catch (Exception e) { ToLog(e.Message); } return null; } public void Start() { mThread = new Thread(new ThreadStart(delegate() { try { listener.Start(); ToLog("Started on " + sIPAdress); while (true) { IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener); result.AsyncWaitHandle.WaitOne(); } } catch (Exception e) { if (e.GetType() != typeof(ObjectDisposedException)) ToLog(e.Message); } })); mThread.Start(); } public void Stop() { if (listener != null) listener.Close(); if (mThread != null) mThread.Join(10000); ToLog("Stoped"); } public static void ListenerCallback(IAsyncResult result) { try { HttpListener listener = (HttpListener)result.AsyncState; HttpListenerContext context = listener.EndGetContext(result); HttpListenerRequest request = context.Request; HttpListenerResponse response = context.Response; using (System.IO.Stream output = response.OutputStream) { if (request.HasEntityBody) { SaveNewPlugin(request.InputStream, response, output); } else { string ans = string.Empty; foreach (IPlugin plugin in sPlugins) { if (request.Url.LocalPath.Replace("/", "").ToLower().StartsWith(plugin.Name.ToLower())) { // Get Parameters from url Dictionary<string, string> query = new Dictionary<string, string>(); for (int i = 0; i < request.QueryString.Keys.Count; i++) { string pName = request.QueryString.Keys[i]; string pValue = Encoding.UTF8.GetString(request.ContentEncoding.GetBytes(request.QueryString[pName])); query.Add(pName, pValue); } //Get data from plugin try { object data = plugin.Data(query); if (data.GetType() == typeof(string)) { ans = (string)data; } else { //Convert data to JSON type ans = request.QueryString["callback"] + "(" + ConvertToJSON(data) + ")"; } } catch (Exception e) { ans = e.Message; } break; } } if (string.IsNullOrEmpty(ans)) { ans = GetMainPage(); } //Send data to user SendMessage(response, output, ans); output.Flush(); output.Close(); } } } catch (Exception e) { if (e.GetType() != typeof(ObjectDisposedException)) ToLog(e.Message); } } private static string GetMainPage() { StringBuilder sb = new StringBuilder(); sb.Append("<html>"); sb.Append("<head>"); sb.Append("<title>ISHOST " + sVersion + "</title>"); sb.Append("</head>"); sb.Append("<body>"); sb.Append("<table cellpadding=\"10px\">"); sb.Append("<thead>"); sb.Append("<tr>"); sb.Append("<td>"); sb.Append("<b>NAME</b>"); sb.Append("</td>"); sb.Append("<td>"); sb.Append("<b>VERSION</b>"); sb.Append("</td>"); sb.Append("<td>"); sb.Append("<b>AUTHOR</b>"); sb.Append("</td>"); sb.Append("</tr>"); sb.Append("</thead>"); foreach (IPlugin plugin in sPlugins) { sb.Append("<tr>"); sb.Append("<td>"); sb.Append(plugin.Name); sb.Append("</td>"); sb.Append("<td>"); sb.Append(plugin.Version); sb.Append("</td>"); sb.Append("<td>"); sb.Append(plugin.Author); sb.Append("</td>"); sb.Append("</tr>"); } sb.Append("</table>"); sb.Append("<div>"); sb.Append("<form action=\"" + sIPAdress + "\" enctype=\"multipart/form-data\" method=\"post\">"); sb.Append("<input type=\"file\" name=\"somename\" size=\"chars\">"); sb.Append("<input type=\"submit\" value=\"Send\">"); sb.Append("</form>"); sb.Append("</div>"); sb.Append("</body>"); sb.Append("</html>"); return sb.ToString(); } private static void SendMessage(HttpListenerResponse response, Stream output, string ans) { byte[] buffer = System.Text.Encoding.UTF8.GetBytes(ans); response.ContentLength64 = buffer.Length; output.Write(buffer, 0, buffer.Length); } private static string ConvertToJSON(object data) { string result = string.Empty; try { result = JsonConvert.SerializeObject(data, Formatting.Indented); } catch (Exception e) { ToLog(e.Message); } return result; } static void ToLog(string text) { try { string folder = System.AppDomain.CurrentDomain.BaseDirectory; using (StreamWriter sw = new StreamWriter(folder + @"\Log.txt", true)) { sw.WriteLine(text+ " : " + DateTime.Now); } } catch { } } public void Dispose() { Stop(); } }
      
      







サービスを再起動せずにプラグイン(DLL)を更新するために、dllファイルからバイトを読み取りましたが、ファイルをメモリにロードしませんでした。 さらに、インターフェイスの実装の読み取りバイトをチェックしました。 この方法は、メモリから開いているdllを時間をかけてアンロードするよりも優れているように思えました。

オブジェクトのJSONマークアップへの変換は、Newtonsoftライブラリを使用して行われました。



ポートでリッスンするためのサービスコード。


 class Program: ServiceBase { HostServer server; public Program() { this.ServiceName = "ISHOST"; server = new HostServer(28555); } protected override void OnStart(string[] args) { server.Start(); base.OnStart(args); } protected override void OnStop() { server.Stop(); base.OnStop(); } static void Main(string[] args) { System.ServiceProcess.ServiceBase.Run(new Program()); } }
      
      







サービスインストーラー


 [RunInstaller(true)] public class ISHOSTInstaller : Installer { public ISHOSTInstaller() { var processInstaller = new ServiceProcessInstaller(); var serviceInstaller = new ServiceInstaller(); processInstaller.Account = ServiceAccount.LocalSystem; serviceInstaller.DisplayName = "ISHOST"; serviceInstaller.StartType = ServiceStartMode.Automatic; serviceInstaller.ServiceName = "ISHOST"; this.Installers.Add(processInstaller); this.Installers.Add(serviceInstaller); } }
      
      







InstallUtil.exeユーティリティ(InstallUtil ISHOST.exe)を使用してサーバーにサービスをインストールした後、サーバーアドレス10.14.3.160:28555に移動します。 このページが開きます:

画像



プラグインを作成する


 class TestPlugin : IPlugin { public string Author { get { return "Yura"; } } public string Name { get { return "TestPlugin"; } } public string Version { get { return "0.1"; } } public object Data(Dictionary<string, string> query) { return "<h1>HELLO</h1>"; } }
      
      





コンパイル後、再度ブラウザーに移動して、TestPlugin.dllプラグインを選択します。

画像



「送信」ボタンをクリックした後。


画像



プラグインTestPluginを呼び出す


画像



次の記事では、JavaScriptとjQueryを使用して情報を収集する方法を記述し、ソースをレイアウトします。

コメントをお待ちしております。



All Articles