using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Metadata; using SuperSocket.SocketBase.Provider; namespace SuperSocket.SocketEngine { [StatusInfo(StatusInfoKeys.CpuUsage, Name = "CPU Usage", Format = "{0:0.00}%", DataType = typeof(double), Order = 112)] [StatusInfo(StatusInfoKeys.MemoryUsage, Name = "Physical Memory Usage", Format = "{0:N}", DataType = typeof(double), Order = 113)] [StatusInfo(StatusInfoKeys.TotalThreadCount, Name = "Total Thread Count", Format = "{0}", DataType = typeof(double), Order = 114)] [StatusInfo(StatusInfoKeys.AvailableWorkingThreads, Name = "Available Working Threads", Format = "{0}", DataType = typeof(double), Order = 512)] [StatusInfo(StatusInfoKeys.AvailableCompletionPortThreads, Name = "Available Completion Port Threads", Format = "{0}", DataType = typeof(double), Order = 513)] [StatusInfo(StatusInfoKeys.MaxWorkingThreads, Name = "Maximum Working Threads", Format = "{0}", DataType = typeof(double), Order = 513)] [StatusInfo(StatusInfoKeys.MaxCompletionPortThreads, Name = "Maximum Completion Port Threads", Format = "{0}", DataType = typeof(double), Order = 514)] partial class ProcessAppServer : IsolationAppServer, IProcessServer { private const string m_AgentUri = "ipc://{0}/WorkItemAgent.rem"; private const string m_PortNameTemplate = "{0}[SuperSocket.Agent:{1}]"; private const string m_AgentAssemblyName = "SuperSocket.Agent.exe"; private Process m_WorkingProcess; private string m_ServerTag; public string ServerTag { get { return m_ServerTag; } } private ProcessLocker m_Locker; private AutoResetEvent m_ProcessWorkEvent = new AutoResetEvent(false); private string m_ProcessWorkStatus = string.Empty; private ProcessPerformanceCounterHelper m_PerformanceCounterHelper; private bool m_AutoStartAfterUnexpectedShutdown = true; /// /// Initializes a new instance of the class. /// /// Name of the server type. /// The server status metadata. public ProcessAppServer(string serverTypeName, StatusInfoAttribute[] serverStatusMetadata) : base(serverTypeName, serverStatusMetadata) { } /// /// Gets the process id. /// /// /// The process id. If the process id is zero, the server instance is not running /// public int ProcessId { get { if (m_WorkingProcess == null) return 0; return m_WorkingProcess.Id; } } public override bool Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories) { if (!base.Setup(bootstrap, config, factories)) return false; if ("false".Equals(config.Options.GetValue("autoStartAfterUnexpectedShutdown"), StringComparison.OrdinalIgnoreCase)) m_AutoStartAfterUnexpectedShutdown = false; return true; } protected override IWorkItemBase Start() { var currentDomain = AppDomain.CurrentDomain; var workingDir = Path.Combine(Path.Combine(currentDomain.BaseDirectory, WorkingDir), Name); if (!Directory.Exists(workingDir)) Directory.CreateDirectory(workingDir); m_Locker = new ProcessLocker(workingDir, "instance.lock"); var portName = string.Format(m_PortNameTemplate, Name, "{0}"); var process = m_Locker.GetLockedProcess(); if (process == null) { var args = string.Join(" ", (new string[] { Name, portName, workingDir }).Select(a => "\"" + a + "\"").ToArray()); ProcessStartInfo startInfo; if (!Platform.IsMono) { startInfo = new ProcessStartInfo(m_AgentAssemblyName, args); } else { startInfo = new ProcessStartInfo((Path.DirectorySeparatorChar == '\\' ? "mono.exe" : "mono"), "--runtime=v" + Environment.Version.ToString(2) + " \"" + m_AgentAssemblyName + "\" " + args); } startInfo.CreateNoWindow = true; startInfo.WindowStyle = ProcessWindowStyle.Hidden; startInfo.WorkingDirectory = currentDomain.BaseDirectory; startInfo.UseShellExecute = false; startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; startInfo.RedirectStandardInput = true; try { m_WorkingProcess = Process.Start(startInfo); } catch (Exception e) { OnExceptionThrown(e); return null; } m_WorkingProcess.EnableRaisingEvents = true; m_WorkingProcess.ErrorDataReceived += new DataReceivedEventHandler(m_WorkingProcess_ErrorDataReceived); m_WorkingProcess.OutputDataReceived += new DataReceivedEventHandler(m_WorkingProcess_OutputDataReceived); m_WorkingProcess.BeginErrorReadLine(); m_WorkingProcess.BeginOutputReadLine(); } else { m_WorkingProcess = process; m_WorkingProcess.EnableRaisingEvents = true; } portName = string.Format(portName, m_WorkingProcess.Id); m_ServerTag = portName; var remoteUri = string.Format(m_AgentUri, portName); IRemoteWorkItem appServer = null; if (process == null) { var startTimeOut = 0; int.TryParse(Config.Options.GetValue("startTimeOut", "0"), out startTimeOut); if (startTimeOut <= 0) { startTimeOut = 10; } if (!m_ProcessWorkEvent.WaitOne(startTimeOut * 1000)) { ShutdownProcess(); OnExceptionThrown(new Exception("The remote work item was timeout to setup!")); return null; } if (!"Ok".Equals(m_ProcessWorkStatus, StringComparison.OrdinalIgnoreCase)) { OnExceptionThrown(new Exception("The Agent process didn't start successfully!")); return null; } appServer = GetRemoteServer(remoteUri); if (appServer == null) return null; var bootstrapIpcPort = AppDomain.CurrentDomain.GetData("BootstrapIpcPort") as string; if (string.IsNullOrEmpty(bootstrapIpcPort)) throw new Exception("The bootstrap's remoting service has not been started."); var ret = false; Exception exc = null; try { var startupConfigFile = Bootstrap.StartupConfigFile; if (!string.IsNullOrEmpty(startupConfigFile)) { if (!Path.IsPathRooted(startupConfigFile)) startupConfigFile = Path.Combine(currentDomain.BaseDirectory, startupConfigFile); } //Setup and then start the remote server instance ret = appServer.Setup(ServerTypeName, "ipc://" + bootstrapIpcPort + "/Bootstrap.rem", currentDomain.BaseDirectory, Config, Factories, startupConfigFile); } catch (Exception e) { exc = e; } if (!ret) { ShutdownProcess(); OnExceptionThrown(new Exception("The remote work item failed to setup!", exc)); return null; } try { ret = appServer.Start(); } catch (Exception e) { ret = false; exc = e; } if (!ret) { ShutdownProcess(); OnExceptionThrown(new Exception("The remote work item failed to start!", exc)); return null; } m_Locker.SaveLock(m_WorkingProcess); } else { appServer = GetRemoteServer(remoteUri); if (appServer == null) return null; } m_WorkingProcess.Exited += new EventHandler(m_WorkingProcess_Exited); m_PerformanceCounterHelper = new ProcessPerformanceCounterHelper(m_WorkingProcess); return appServer; } IRemoteWorkItem GetRemoteServer(string remoteUri) { try { return (IRemoteWorkItem)Activator.GetObject(typeof(IRemoteWorkItem), remoteUri); } catch(Exception e) { ShutdownProcess(); OnExceptionThrown(new Exception("Failed to get server instance of a remote process!", e)); return null; } } void m_WorkingProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) return; if (string.IsNullOrEmpty(m_ProcessWorkStatus)) { m_ProcessWorkStatus = e.Data.Trim(); m_ProcessWorkEvent.Set(); return; } } void m_WorkingProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) { if (string.IsNullOrEmpty(e.Data)) return; OnExceptionThrown(new Exception(e.Data)); } void m_WorkingProcess_Exited(object sender, EventArgs e) { m_PerformanceCounterHelper = null; m_Locker.CleanLock(); OnStopped(); } protected override void OnStopped() { var unexpectedShutdown = (State == ServerState.Running); base.OnStopped(); m_WorkingProcess.OutputDataReceived -= m_WorkingProcess_OutputDataReceived; m_WorkingProcess.ErrorDataReceived -= m_WorkingProcess_ErrorDataReceived; m_WorkingProcess = null; m_ProcessWorkStatus = string.Empty; if (unexpectedShutdown && m_AutoStartAfterUnexpectedShutdown) { //auto restart if meet a unexpected shutdown ((IWorkItemBase)this).Start(); } } private void ShutdownProcess() { if (m_WorkingProcess != null) { try { m_WorkingProcess.Kill(); } catch { } } } protected override void Stop() { ShutdownProcess(); } protected override bool StatusMetadataExtended { get { return true; } } public override StatusInfoCollection CollectServerStatus(StatusInfoCollection nodeStatus) { var status = base.CollectServerStatus(nodeStatus); status.Tag = m_ServerTag; if(m_PerformanceCounterHelper != null) m_PerformanceCounterHelper.Collect(status); return status; } } }