ProcessAppServer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using SuperSocket.Common;
  10. using SuperSocket.SocketBase;
  11. using SuperSocket.SocketBase.Config;
  12. using SuperSocket.SocketBase.Metadata;
  13. using SuperSocket.SocketBase.Provider;
  14. namespace SuperSocket.SocketEngine
  15. {
  16. [StatusInfo(StatusInfoKeys.CpuUsage, Name = "CPU Usage", Format = "{0:0.00}%", DataType = typeof(double), Order = 112)]
  17. [StatusInfo(StatusInfoKeys.MemoryUsage, Name = "Physical Memory Usage", Format = "{0:N}", DataType = typeof(double), Order = 113)]
  18. [StatusInfo(StatusInfoKeys.TotalThreadCount, Name = "Total Thread Count", Format = "{0}", DataType = typeof(double), Order = 114)]
  19. [StatusInfo(StatusInfoKeys.AvailableWorkingThreads, Name = "Available Working Threads", Format = "{0}", DataType = typeof(double), Order = 512)]
  20. [StatusInfo(StatusInfoKeys.AvailableCompletionPortThreads, Name = "Available Completion Port Threads", Format = "{0}", DataType = typeof(double), Order = 513)]
  21. [StatusInfo(StatusInfoKeys.MaxWorkingThreads, Name = "Maximum Working Threads", Format = "{0}", DataType = typeof(double), Order = 513)]
  22. [StatusInfo(StatusInfoKeys.MaxCompletionPortThreads, Name = "Maximum Completion Port Threads", Format = "{0}", DataType = typeof(double), Order = 514)]
  23. partial class ProcessAppServer : IsolationAppServer, IProcessServer
  24. {
  25. private const string m_AgentUri = "ipc://{0}/WorkItemAgent.rem";
  26. private const string m_PortNameTemplate = "{0}[SuperSocket.Agent:{1}]";
  27. private const string m_AgentAssemblyName = "SuperSocket.Agent.exe";
  28. private Process m_WorkingProcess;
  29. private string m_ServerTag;
  30. public string ServerTag
  31. {
  32. get { return m_ServerTag; }
  33. }
  34. private ProcessLocker m_Locker;
  35. private AutoResetEvent m_ProcessWorkEvent = new AutoResetEvent(false);
  36. private string m_ProcessWorkStatus = string.Empty;
  37. private ProcessPerformanceCounterHelper m_PerformanceCounterHelper;
  38. private bool m_AutoStartAfterUnexpectedShutdown = true;
  39. /// <summary>
  40. /// Initializes a new instance of the <see cref="ProcessAppServer" /> class.
  41. /// </summary>
  42. /// <param name="serverTypeName">Name of the server type.</param>
  43. /// <param name="serverStatusMetadata">The server status metadata.</param>
  44. public ProcessAppServer(string serverTypeName, StatusInfoAttribute[] serverStatusMetadata)
  45. : base(serverTypeName, serverStatusMetadata)
  46. {
  47. }
  48. /// <summary>
  49. /// Gets the process id.
  50. /// </summary>
  51. /// <value>
  52. /// The process id. If the process id is zero, the server instance is not running
  53. /// </value>
  54. public int ProcessId
  55. {
  56. get
  57. {
  58. if (m_WorkingProcess == null)
  59. return 0;
  60. return m_WorkingProcess.Id;
  61. }
  62. }
  63. public override bool Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories)
  64. {
  65. if (!base.Setup(bootstrap, config, factories))
  66. return false;
  67. if ("false".Equals(config.Options.GetValue("autoStartAfterUnexpectedShutdown"), StringComparison.OrdinalIgnoreCase))
  68. m_AutoStartAfterUnexpectedShutdown = false;
  69. return true;
  70. }
  71. protected override IWorkItemBase Start()
  72. {
  73. var currentDomain = AppDomain.CurrentDomain;
  74. var workingDir = Path.Combine(Path.Combine(currentDomain.BaseDirectory, WorkingDir), Name);
  75. if (!Directory.Exists(workingDir))
  76. Directory.CreateDirectory(workingDir);
  77. m_Locker = new ProcessLocker(workingDir, "instance.lock");
  78. var portName = string.Format(m_PortNameTemplate, Name, "{0}");
  79. var process = m_Locker.GetLockedProcess();
  80. if (process == null)
  81. {
  82. var args = string.Join(" ", (new string[] { Name, portName, workingDir }).Select(a => "\"" + a + "\"").ToArray());
  83. ProcessStartInfo startInfo;
  84. if (!Platform.IsMono)
  85. {
  86. startInfo = new ProcessStartInfo(m_AgentAssemblyName, args);
  87. }
  88. else
  89. {
  90. startInfo = new ProcessStartInfo((Path.DirectorySeparatorChar == '\\' ? "mono.exe" : "mono"), "--runtime=v" + Environment.Version.ToString(2) + " \"" + m_AgentAssemblyName + "\" " + args);
  91. }
  92. startInfo.CreateNoWindow = true;
  93. startInfo.WindowStyle = ProcessWindowStyle.Hidden;
  94. startInfo.WorkingDirectory = currentDomain.BaseDirectory;
  95. startInfo.UseShellExecute = false;
  96. startInfo.RedirectStandardOutput = true;
  97. startInfo.RedirectStandardError = true;
  98. startInfo.RedirectStandardInput = true;
  99. try
  100. {
  101. m_WorkingProcess = Process.Start(startInfo);
  102. }
  103. catch (Exception e)
  104. {
  105. OnExceptionThrown(e);
  106. return null;
  107. }
  108. m_WorkingProcess.EnableRaisingEvents = true;
  109. m_WorkingProcess.ErrorDataReceived += new DataReceivedEventHandler(m_WorkingProcess_ErrorDataReceived);
  110. m_WorkingProcess.OutputDataReceived += new DataReceivedEventHandler(m_WorkingProcess_OutputDataReceived);
  111. m_WorkingProcess.BeginErrorReadLine();
  112. m_WorkingProcess.BeginOutputReadLine();
  113. }
  114. else
  115. {
  116. m_WorkingProcess = process;
  117. m_WorkingProcess.EnableRaisingEvents = true;
  118. }
  119. portName = string.Format(portName, m_WorkingProcess.Id);
  120. m_ServerTag = portName;
  121. var remoteUri = string.Format(m_AgentUri, portName);
  122. IRemoteWorkItem appServer = null;
  123. if (process == null)
  124. {
  125. var startTimeOut = 0;
  126. int.TryParse(Config.Options.GetValue("startTimeOut", "0"), out startTimeOut);
  127. if (startTimeOut <= 0)
  128. {
  129. startTimeOut = 10;
  130. }
  131. if (!m_ProcessWorkEvent.WaitOne(startTimeOut * 1000))
  132. {
  133. ShutdownProcess();
  134. OnExceptionThrown(new Exception("The remote work item was timeout to setup!"));
  135. return null;
  136. }
  137. if (!"Ok".Equals(m_ProcessWorkStatus, StringComparison.OrdinalIgnoreCase))
  138. {
  139. OnExceptionThrown(new Exception("The Agent process didn't start successfully!"));
  140. return null;
  141. }
  142. appServer = GetRemoteServer(remoteUri);
  143. if (appServer == null)
  144. return null;
  145. var bootstrapIpcPort = AppDomain.CurrentDomain.GetData("BootstrapIpcPort") as string;
  146. if (string.IsNullOrEmpty(bootstrapIpcPort))
  147. throw new Exception("The bootstrap's remoting service has not been started.");
  148. var ret = false;
  149. Exception exc = null;
  150. try
  151. {
  152. var startupConfigFile = Bootstrap.StartupConfigFile;
  153. if (!string.IsNullOrEmpty(startupConfigFile))
  154. {
  155. if (!Path.IsPathRooted(startupConfigFile))
  156. startupConfigFile = Path.Combine(currentDomain.BaseDirectory, startupConfigFile);
  157. }
  158. //Setup and then start the remote server instance
  159. ret = appServer.Setup(ServerTypeName, "ipc://" + bootstrapIpcPort + "/Bootstrap.rem", currentDomain.BaseDirectory, Config, Factories, startupConfigFile);
  160. }
  161. catch (Exception e)
  162. {
  163. exc = e;
  164. }
  165. if (!ret)
  166. {
  167. ShutdownProcess();
  168. OnExceptionThrown(new Exception("The remote work item failed to setup!", exc));
  169. return null;
  170. }
  171. try
  172. {
  173. ret = appServer.Start();
  174. }
  175. catch (Exception e)
  176. {
  177. ret = false;
  178. exc = e;
  179. }
  180. if (!ret)
  181. {
  182. ShutdownProcess();
  183. OnExceptionThrown(new Exception("The remote work item failed to start!", exc));
  184. return null;
  185. }
  186. m_Locker.SaveLock(m_WorkingProcess);
  187. }
  188. else
  189. {
  190. appServer = GetRemoteServer(remoteUri);
  191. if (appServer == null)
  192. return null;
  193. }
  194. m_WorkingProcess.Exited += new EventHandler(m_WorkingProcess_Exited);
  195. m_PerformanceCounterHelper = new ProcessPerformanceCounterHelper(m_WorkingProcess);
  196. return appServer;
  197. }
  198. IRemoteWorkItem GetRemoteServer(string remoteUri)
  199. {
  200. try
  201. {
  202. return (IRemoteWorkItem)Activator.GetObject(typeof(IRemoteWorkItem), remoteUri);
  203. }
  204. catch(Exception e)
  205. {
  206. ShutdownProcess();
  207. OnExceptionThrown(new Exception("Failed to get server instance of a remote process!", e));
  208. return null;
  209. }
  210. }
  211. void m_WorkingProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
  212. {
  213. if (string.IsNullOrEmpty(e.Data))
  214. return;
  215. if (string.IsNullOrEmpty(m_ProcessWorkStatus))
  216. {
  217. m_ProcessWorkStatus = e.Data.Trim();
  218. m_ProcessWorkEvent.Set();
  219. return;
  220. }
  221. }
  222. void m_WorkingProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
  223. {
  224. if (string.IsNullOrEmpty(e.Data))
  225. return;
  226. OnExceptionThrown(new Exception(e.Data));
  227. }
  228. void m_WorkingProcess_Exited(object sender, EventArgs e)
  229. {
  230. m_PerformanceCounterHelper = null;
  231. m_Locker.CleanLock();
  232. OnStopped();
  233. }
  234. protected override void OnStopped()
  235. {
  236. var unexpectedShutdown = (State == ServerState.Running);
  237. base.OnStopped();
  238. m_WorkingProcess.OutputDataReceived -= m_WorkingProcess_OutputDataReceived;
  239. m_WorkingProcess.ErrorDataReceived -= m_WorkingProcess_ErrorDataReceived;
  240. m_WorkingProcess = null;
  241. m_ProcessWorkStatus = string.Empty;
  242. if (unexpectedShutdown && m_AutoStartAfterUnexpectedShutdown)
  243. {
  244. //auto restart if meet a unexpected shutdown
  245. ((IWorkItemBase)this).Start();
  246. }
  247. }
  248. private void ShutdownProcess()
  249. {
  250. if (m_WorkingProcess != null)
  251. {
  252. try
  253. {
  254. m_WorkingProcess.Kill();
  255. }
  256. catch
  257. {
  258. }
  259. }
  260. }
  261. protected override void Stop()
  262. {
  263. ShutdownProcess();
  264. }
  265. protected override bool StatusMetadataExtended
  266. {
  267. get
  268. {
  269. return true;
  270. }
  271. }
  272. public override StatusInfoCollection CollectServerStatus(StatusInfoCollection nodeStatus)
  273. {
  274. var status = base.CollectServerStatus(nodeStatus);
  275. status.Tag = m_ServerTag;
  276. if(m_PerformanceCounterHelper != null)
  277. m_PerformanceCounterHelper.Collect(status);
  278. return status;
  279. }
  280. }
  281. }