void Run(List <string> args) { // prepare Console.OutputEncoding = System.Text.Encoding.UTF8; var time = DateTime.Now; var stopwatch = Stopwatch.StartNew(); var apiCall = args?.FirstOrDefault(arg => arg.IsStartsWith("/agc:")); var isUserInteractive = Environment.UserInteractive && apiCall == null; var powered = $"VIEApps NGX API Gateway - Service Hosting {RuntimeInformation.ProcessArchitecture.ToString().ToLower()} {Assembly.GetCallingAssembly().GetVersion()}"; // prepare type name this.ServiceTypeName = args?.FirstOrDefault(arg => arg.IsStartsWith("/svc:"))?.Replace(StringComparison.OrdinalIgnoreCase, "/svc:", ""); if (string.IsNullOrWhiteSpace(this.ServiceTypeName) && args?.FirstOrDefault(arg => arg.IsStartsWith("/svn:")) != null) { var configFilePath = Path.Combine($"{UtilityService.GetAppSetting("Path:APIGateway:Controller")}", $"VIEApps.Services.APIGateway.{(RuntimeInformation.FrameworkDescription.IsContains(".NET Framework") ? "exe" : "dll")}.config"); if (File.Exists(configFilePath)) { try { var xml = new System.Xml.XmlDocument(); xml.LoadXml(UtilityService.ReadTextFile(configFilePath)); this.ServiceTypeName = args.First(arg => arg.IsStartsWith("/svn:")).Replace(StringComparison.OrdinalIgnoreCase, "/svn:", "").Trim(); var typeNode = xml.SelectSingleNode($"/configuration/{UtilityService.GetAppSetting("Section:Services", "net.vieapps.services")}")?.ChildNodes?.ToList()?.FirstOrDefault(node => this.ServiceTypeName.IsEquals(node.Attributes["name"]?.Value)); this.ServiceTypeName = typeNode?.Attributes["type"]?.Value; } catch { this.ServiceTypeName = null; } } } // stop if has no type name of a service component if (string.IsNullOrWhiteSpace(this.ServiceTypeName)) { Console.Error.WriteLine(powered); Console.Error.WriteLine(""); Console.Error.WriteLine("Error: The service component is invalid (no type name)"); Console.Error.WriteLine(""); Console.Error.WriteLine("Syntax: VIEApps.Services.APIGateway /svc:<service-component-namespace,service-assembly>"); Console.Error.WriteLine(""); Console.Error.WriteLine("Ex.: VIEApps.Services.APIGateway /svc:net.vieapps.Services.Users.ServiceComponent,VIEApps.Services.Users"); Console.Error.WriteLine(""); if (isUserInteractive) { Console.ReadLine(); } return; } // prepare type name & assembly name of the service component var serviceTypeInfo = this.ServiceTypeName.ToArray(); this.ServiceTypeName = serviceTypeInfo[0]; this.ServiceAssemblyName = serviceTypeInfo.Length > 1 ? serviceTypeInfo[1] : "Unknown"; // prepare the type of the service component try { this.PrepareServiceType(); if (this.ServiceType == null) { Console.Error.WriteLine(powered); Console.Error.WriteLine(""); Console.Error.WriteLine($"Error: The service component is invalid [{this.ServiceTypeName},{this.ServiceAssemblyName}]"); if (isUserInteractive) { Console.ReadLine(); } return; } } catch (Exception ex) { Console.Error.WriteLine(powered); Console.Error.WriteLine(""); if (ex is ReflectionTypeLoadException) { Console.Error.WriteLine($"Error: The service component [{this.ServiceTypeName},{this.ServiceAssemblyName}] got an unexpected error while preparing"); (ex as ReflectionTypeLoadException).LoaderExceptions.ForEach(exception => { Console.Error.WriteLine($"{exception.Message} [{exception.GetType()}]\r\nStack: {exception.StackTrace}"); var inner = exception.InnerException; while (inner != null) { Console.Error.WriteLine($"-------------------------\r\n{inner.Message} [{inner.GetType()}]\r\nStack: {inner.StackTrace}"); inner = inner.InnerException; } }); } else { Console.Error.WriteLine($"Error: The service component [{this.ServiceTypeName},{this.ServiceAssemblyName}] got an unexpected error while preparing => {ex.Message} [{ex.GetType()}]\r\nStack: {ex.StackTrace}"); var inner = ex.InnerException; while (inner != null) { Console.Error.WriteLine($"-------------------------\r\n{inner.Message} [{inner.GetType()}]\r\nStack: {inner.StackTrace}"); inner = inner.InnerException; } } if (isUserInteractive) { Console.ReadLine(); } return; } // check the type of the service component if (!typeof(ServiceBase).IsAssignableFrom(this.ServiceType)) { Console.Error.WriteLine(powered); Console.Error.WriteLine(""); Console.Error.WriteLine($"Error: The service component is invalid [{this.ServiceTypeName},{this.ServiceAssemblyName}]"); if (isUserInteractive) { Console.ReadLine(); } return; } // initialize the instance of the service var service = this.ServiceType.CreateInstance() as ServiceBase; // prepare the signal to start/stop when the service was called from API Gateway EventWaitHandle eventWaitHandle = null; var useEventWaitHandle = !isUserInteractive && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (useEventWaitHandle) { // get the flag of the existing instance var runtimeArguments = Extensions.GetRuntimeArguments(); var name = $"{service.ServiceURI}#{$"/interactive:{isUserInteractive} /user:{runtimeArguments.Item1} /host:{runtimeArguments.Item2} /platform:{runtimeArguments.Item3} /os:{runtimeArguments.Item4}".GenerateUUID()}"; eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, name, out var createdNew); // process the call to stop if ("/agc:s".IsEquals(apiCall)) { // raise an event to stop current existing instance if (!createdNew) { eventWaitHandle.Set(); } // then exit eventWaitHandle.Dispose(); service.Dispose(); return; } } // prepare environment JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DateTimeZoneHandling = DateTimeZoneHandling.Local }; // prepare logging var loglevel = args?.FirstOrDefault(arg => arg.IsStartsWith("/loglevel:"))?.Replace(StringComparison.OrdinalIgnoreCase, "/loglevel:", ""); if (string.IsNullOrWhiteSpace(loglevel)) #if DEBUG { loglevel = UtilityService.GetAppSetting("Logs:Level", "Debug"); } #else { loglevel = UtilityService.GetAppSetting("Logs:Level", "Information"); } #endif if (!loglevel.TryToEnum(out LogLevel logLevel)) #if DEBUG { logLevel = LogLevel.Debug; } #else { logLevel = LogLevel.Information; } #endif Logger.AssignLoggerFactory(new ServiceCollection().AddLogging(builder => { builder.SetMinimumLevel(logLevel); if (isUserInteractive) { builder.AddConsole(); } }).BuildServiceProvider().GetService <ILoggerFactory>()); Components.Caching.Cache.AssignLoggerFactory(Logger.GetLoggerFactory()); var logPath = UtilityService.GetAppSetting("Path:Logs"); if (!string.IsNullOrWhiteSpace(logPath) && Directory.Exists(logPath)) { logPath = Path.Combine(logPath, "{Hour}_" + $"{service.ServiceName.ToLower()}.all.txt"); Logger.GetLoggerFactory().AddFile(logPath, logLevel); } else { logPath = null; } var logger = (service as IServiceComponent).Logger = Logger.CreateLogger(this.ServiceType); // prepare outgoing proxy var proxy = UtilityService.GetAppSetting("Proxy:Host"); if (!string.IsNullOrWhiteSpace(proxy)) { try { UtilityService.AssignWebProxy(proxy, UtilityService.GetAppSetting("Proxy:Port").CastAs <int>(), UtilityService.GetAppSetting("Proxy:User"), UtilityService.GetAppSetting("Proxy:UserPassword"), UtilityService.GetAppSetting("Proxy:Bypass")?.ToArray(";")); } catch (Exception ex) { logger.LogError($"Error occurred while assigning web-proxy => {ex.Message}", ex); } } // setup hooks void terminate(string message, bool available = true, bool disconnect = true) => (service.Disposed ? Task.CompletedTask : service.DisposeAsync(args?.ToArray(), available, disconnect, _ => logger.LogInformation(message)).AsTask()).ContinueWith(async _ => await Task.Delay(123).ConfigureAwait(false), TaskContinuationOptions.OnlyOnRanToCompletion).Wait(); AppDomain.CurrentDomain.ProcessExit += (sender, arguments) => terminate($"The service was terminated (by \"process exit\" signal) - Served times: {time.GetElapsedTimes()}", false); Console.CancelKeyPress += (sender, arguments) => { terminate($"The service was terminated (by \"cancel key press\" signal) - Served times: {time.GetElapsedTimes()}", false); Environment.Exit(0); }; // start the service logger.LogInformation($"The service is starting"); logger.LogInformation($"Service info: {service.ServiceName} - v{this.ServiceType.Assembly.GetVersion()}"); logger.LogInformation($"Working mode: {(isUserInteractive ? "Interactive app" : "Background service")}"); logger.LogInformation($"Starting arguments: {(args != null && args.Count > 0 ? args.Join(" ") : "None")}"); ServiceBase.ServiceComponent = service; service.Start( args?.ToArray(), !"false".IsEquals(args?.FirstOrDefault(a => a.IsStartsWith("/repository:"))?.Replace(StringComparison.OrdinalIgnoreCase, "/repository:", "")), _ => { logger.LogInformation($"API Gateway Router: {new Uri(Router.GetRouterStrInfo()).GetResolvedURI()}"); logger.LogInformation($"API Gateway HTTP service: {UtilityService.GetAppSetting("HttpUri:APIs", "None")}"); logger.LogInformation($"Files HTTP service: {UtilityService.GetAppSetting("HttpUri:Files", "None")}"); logger.LogInformation($"Portals HTTP service: {UtilityService.GetAppSetting("HttpUri:Portals", "None")}"); logger.LogInformation($"Passport HTTP service: {UtilityService.GetAppSetting("HttpUri:Passports", "None")}"); logger.LogInformation($"Root (base) directory: {AppDomain.CurrentDomain.BaseDirectory}"); logger.LogInformation($"Temporary directory: {UtilityService.GetAppSetting("Path:Temp", "None")}"); logger.LogInformation($"Static files directory: {UtilityService.GetAppSetting("Path:StaticFiles", "None")}"); logger.LogInformation($"Status files directory: {UtilityService.GetAppSetting("Path:Status", "None")}"); logger.LogInformation($"Logging level: {logLevel} - Local rolling log files is {(string.IsNullOrWhiteSpace(logPath) ? "disabled" : $"enabled => {logPath}")}"); logger.LogInformation($"Show debugs: {service.IsDebugLogEnabled} - Show results: {service.IsDebugResultsEnabled} - Show stacks: {service.IsDebugStacksEnabled}"); logger.LogInformation($"Service URIs:\r\n\t- Round robin: {service.ServiceURI}\r\n\t- Single (unique): {service.ServiceUniqueURI}"); logger.LogInformation($"Environment:\r\n\t{Extensions.GetRuntimeEnvironment()}\r\n\t- Node ID: {service.NodeID}\r\n\t- Powered: {powered}"); stopwatch.Stop(); logger.LogInformation($"The service was started - PID: {Process.GetCurrentProcess().Id} - Execution times: {stopwatch.GetElapsedTimes()}"); if (isUserInteractive) { logger.LogWarning($"=====> Enter \"exit\" to terminate ..............."); } }
static void Main(string[] args) { // setup environment Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); Program.Arguments = args; // prepare logging var loglevel = args?.FirstOrDefault(a => a.IsStartsWith("/loglevel:"))?.Replace(StringComparison.OrdinalIgnoreCase, "/loglevel:", ""); if (string.IsNullOrWhiteSpace(loglevel)) #if DEBUG { loglevel = UtilityService.GetAppSetting("Logs:Level", "Debug"); } #else { loglevel = UtilityService.GetAppSetting("Logs:Level", "Information"); } #endif if (!loglevel.TryToEnum(out LogLevel logLevel)) #if DEBUG { logLevel = LogLevel.Debug; } #else { logLevel = LogLevel.Information; } #endif Components.Utility.Logger.AssignLoggerFactory(new ServiceCollection().AddLogging(builder => builder.SetMinimumLevel(logLevel)).BuildServiceProvider().GetService <ILoggerFactory>()); var logPath = UtilityService.GetAppSetting("Path:Logs"); if (logPath != null && Directory.Exists(logPath)) { logPath = Path.Combine(logPath, "{Hour}_apigateway.controller.txt"); Components.Utility.Logger.GetLoggerFactory().AddFile(logPath, logLevel); } else { logPath = null; } Program.Logger = Components.Utility.Logger.CreateLogger <Controller>(); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DateTimeZoneHandling = DateTimeZoneHandling.Local }; // prepare outgoing proxy var proxy = UtilityService.GetAppSetting("Proxy:Host"); if (!string.IsNullOrWhiteSpace(proxy)) { try { UtilityService.AssignWebProxy(proxy, UtilityService.GetAppSetting("Proxy:Port").CastAs <int>(), UtilityService.GetAppSetting("Proxy:User"), UtilityService.GetAppSetting("Proxy:UserPassword"), UtilityService.GetAppSetting("Proxy:Bypass")?.ToArray(";")); } catch (Exception ex) { Program.Logger.LogError($"Error occurred while assigning web-proxy => {ex.Message}", ex); } } // setup event handlers Program.SetupEventHandlers(); // run as a Windows desktop app if (Environment.UserInteractive) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Program.MainForm = new MainForm(); Application.Run(Program.MainForm); } // run as a Windows service else { System.ServiceProcess.ServiceBase.Run(new ServiceRunner()); } }
static void Main(string[] args) { // prepare environment Program.IsUserInteractive = Environment.UserInteractive && args?.FirstOrDefault(a => a.IsStartsWith("/daemon")) == null; Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); Console.OutputEncoding = System.Text.Encoding.UTF8; // prepare logging var loglevel = args?.FirstOrDefault(a => a.IsStartsWith("/loglevel:"))?.Replace(StringComparison.OrdinalIgnoreCase, "/loglevel:", ""); if (string.IsNullOrWhiteSpace(loglevel)) #if DEBUG { loglevel = UtilityService.GetAppSetting("Logs:Level", "Debug"); } #else { loglevel = UtilityService.GetAppSetting("Logs:Level", "Information"); } #endif if (!loglevel.TryToEnum(out LogLevel logLevel)) #if DEBUG { logLevel = LogLevel.Debug; } #else { logLevel = LogLevel.Information; } #endif Components.Utility.Logger.AssignLoggerFactory(new ServiceCollection().AddLogging(builder => { builder.SetMinimumLevel(logLevel); if (Program.IsUserInteractive) { builder.AddConsole(); } }).BuildServiceProvider().GetService <ILoggerFactory>()); var logPath = UtilityService.GetAppSetting("Path:Logs"); if (logPath != null && Directory.Exists(logPath)) { logPath = Path.Combine(logPath, "{Hour}_apigateway.controller.txt"); Components.Utility.Logger.GetLoggerFactory().AddFile(logPath, logLevel); } else { logPath = null; } Program.Logger = Components.Utility.Logger.CreateLogger <Controller>(); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Formatting = Formatting.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DateTimeZoneHandling = DateTimeZoneHandling.Local }; // prepare outgoing proxy var proxy = UtilityService.GetAppSetting("Proxy:Host"); if (!string.IsNullOrWhiteSpace(proxy)) { try { UtilityService.AssignWebProxy(proxy, UtilityService.GetAppSetting("Proxy:Port").CastAs <int>(), UtilityService.GetAppSetting("Proxy:User"), UtilityService.GetAppSetting("Proxy:UserPassword"), UtilityService.GetAppSetting("Proxy:Bypass")?.ToArray(";")); } catch (Exception ex) { Program.Logger.LogError($"Error occurred while assigning web-proxy => {ex.Message}", ex); } } // prepare event handlers Program.SetupEventHandlers(); // prepare hooks AppDomain.CurrentDomain.ProcessExit += (sender, arguments) => { if (!Program.IsStopped) { Program.Logger.LogWarning(">>> Terminated by signal of process exit"); Program.Stop(); } }; Console.CancelKeyPress += (sender, arguments) => { Program.Logger.LogWarning(">>> Terminated by signal of cancel key press"); Program.Stop(); Environment.Exit(0); }; // start Program.Start(args); // processing commands util got an exit signal (not available when running in Docker) if (Program.IsUserInteractive && args?.FirstOrDefault(a => a.IsStartsWith("/docker")) == null) { var command = Console.ReadLine(); while (command != null) { var commands = command.ToArray(' '); if (commands[0].IsEquals("info")) { var controllerID = commands.Length > 1 ? commands[1].ToLower() : "local"; if (controllerID.IsEquals("global")) { var controllers = Program.Manager.AvailableControllers; var info = $"Controllers - Total instance(s): {Program.Manager.AvailableControllers.Count:#,##0} - Available instance(s): {Program.Manager.AvailableControllers.Where(kvp => kvp.Value.Available).Count():#,##0}"; Program.Manager.AvailableControllers.ForEach(controller => info += "\r\n\t" + $"- ID: {controller.ID} - Status: {(controller.Available ? "Available" : "Unavailable")} - Working mode: {controller.Mode} - Platform: {controller.Platform}"); info += "\r\n" + $"Services - Total: {Program.Manager.AvailableServices.Count:#,##0} - Available: {Program.Manager.AvailableServices.Where(kvp => kvp.Value.FirstOrDefault(svc => svc.Available) != null).Count():#,##0} - Running: {Program.Manager.AvailableServices.Where(kvp => kvp.Value.FirstOrDefault(svc => svc.Running) != null).Count():#,##0}"; Program.Manager.AvailableServices.OrderBy(kvp => kvp.Key).ForEach(kvp => info += "\r\n\t" + $"- URI: services.{kvp.Key} - Available instance(s): {kvp.Value.Where(svc => svc.Available).Count():#,##0} - Running instance(s): {kvp.Value.Where(svc => svc.Running).Count():#,##0}"); Program.Logger.LogInformation(info); } else if (controllerID.IsEquals("local") || controllerID.IsEquals(Program.Controller.Info.ID)) { var info = $"Controller:" + "\r\n\t" + $"- Version: {Assembly.GetExecutingAssembly().GetVersion()}" + "\r\n\t" + $"- Working mode: {(Environment.UserInteractive ? "Interactive app" : "Background service")}" + "\r\n\t" + $"- Environment:\r\n\t\t{Extensions.GetRuntimeEnvironment("\r\n\t\t")}" + "\r\n\t" + $"- API Gateway Router: {new Uri(Router.GetRouterStrInfo()).GetResolvedURI()}" + "\r\n\t" + $"- Incoming channel session identity: {Router.IncomingChannelSessionID}" + "\r\n\t" + $"- Outgoing channel session identity: {Router.OutgoingChannelSessionID}" + "\r\n\t" + $"- Number of helper services: {Program.Controller.NumberOfHelperServices:#,##0}" + "\r\n\t" + $"- Number of scheduling timers: {Program.Controller.NumberOfTimers:#,##0}" + "\r\n\t" + $"- Number of scheduling tasks: {Program.Controller.NumberOfTasks:#,##0}"; var services = Program.Controller.AvailableBusinessServices.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Instance); info += "\r\n" + $"Services - Available: {services.Count:#,##0} - Running: {services.Where(kvp => kvp.Value != null).Count():#,##0}"; services.ForEach(kvp => { info += "\r\n\t" + $"- URI: services.{kvp.Key}"; if (kvp.Value != null) { var svcArgs = kvp.Value.Arguments?.ToArray(' ') ?? new string[] { }; info += $" (services.{Extensions.GetUniqueName(kvp.Key.ToArray('.').Last(), svcArgs)}) - Status: Running"; if (kvp.Value.ID != null) { info += $" - Process ID: {kvp.Value.ID.Value}"; } if (kvp.Value.StartTime != null) { info += $" - Serving times: {kvp.Value.StartTime.Value.GetElapsedTimes()}"; } var user = svcArgs.FirstOrDefault(a => a.IsStartsWith("/call-user:"******" - Invoked by: {user.Replace(StringComparison.OrdinalIgnoreCase, "/call-user:"******"").UrlDecode()}"; var host = svcArgs.FirstOrDefault(a => a.IsStartsWith("/call-host:")); var platform = svcArgs.FirstOrDefault(a => a.IsStartsWith("/call-platform:")); var os = svcArgs.FirstOrDefault(a => a.IsStartsWith("/call-os:")); if (!string.IsNullOrWhiteSpace(host) && !string.IsNullOrWhiteSpace(platform) && !string.IsNullOrWhiteSpace(os)) { info += $" [Host: {host.Replace(StringComparison.OrdinalIgnoreCase, "/call-host:", "").UrlDecode()} - Platform: {platform.Replace(StringComparison.OrdinalIgnoreCase, "/call-platform:", "").UrlDecode()} @ {os.Replace(StringComparison.OrdinalIgnoreCase, "/call-os:", "").UrlDecode()}]"; } } } else { info += " - Status: Stopped"; } }); Program.Logger.LogInformation(info); } else if (Program.Manager.AvailableControllers.ContainsKey(controllerID)) { var controller = Program.Manager.AvailableControllers[controllerID]; var info = $"Controller:" + "\r\n\t" + $"- ID: {controller.ID}" + "\r\n\t" + $"- Platform: {controller.Platform})" + "\r\n\t" + $"- Working mode: {controller.Mode}" + "\r\n\t" + $"- Host: {controller.Host}" + "\r\n\t" + $"- User: {controller.User}" + "\r\n\t" + $"- Status: {(controller.Available ? "Available" : "Unvailable")}" + "\r\n\t"; info += controller.Available ? $"- Starting time: {controller.Timestamp.ToDTString()} [Served times: {controller.Timestamp.GetElapsedTimes()}]" : $"- Last working time: {controller.Timestamp.ToDTString()}"; var services = Program.Manager.AvailableServices.Values.Select(svc => svc.FirstOrDefault(svcInfo => svcInfo.ControllerID.Equals(controller.ID))).Where(svcInfo => svcInfo != null).OrderBy(svcInfo => svcInfo.Name).ToList(); info += "\r\n" + $"Services - Available: {services.Where(svc => svc.Available).Count():#,##0} - Running: {services.Where(svc => svc.Running).Count():#,##0}"; services.ForEach(svc => { info += "\r\n\t" + $"- URI: services.{svc.Name} ({svc.UniqueName}) - Status: {(svc.Running ? "Running" : "Stopped")}"; info += svc.Running ? $" - Starting time: {svc.Timestamp.ToDTString()} [Served times: {svc.Timestamp.GetElapsedTimes()}] - Invoked by: {svc.InvokeInfo}" : $" - Last working time: {svc.Timestamp.ToDTString()}"; }); Program.Logger.LogInformation(info); } else { Program.Logger.LogWarning($"Controller with identity \"{controllerID}\" is not found"); } } else if (commands[0].IsEquals("start")) { if (commands.Length > 1) { var controllerID = commands.Length > 2 ? commands[2].ToLower() : Program.Controller.Info.ID; if (!Program.Manager.AvailableControllers.ContainsKey(controllerID)) { Program.Logger.LogWarning($"Controller with identity \"{controllerID}\" is not found"); } else { Program.Manager.StartBusinessService(controllerID, commands[1].ToLower(), Program.Controller.GetServiceArguments().Replace("/", "/call-")); } } else { Program.Logger.LogInformation($"Invalid {command} command"); } } else if (commands[0].IsEquals("stop")) { if (commands.Length > 1) { var controllerID = commands.Length > 2 ? commands[2].ToLower() : Program.Controller.Info.ID; if (!Program.Manager.AvailableControllers.ContainsKey(controllerID)) { Program.Logger.LogWarning($"Controller with identity \"{controllerID}\" is not found"); } else { Program.Manager.StopBusinessService(controllerID, commands[1].ToLower()); } } else { Program.Logger.LogInformation($"Invalid {command} command"); } } else if (commands[0].IsEquals("refresh")) { Task.Run(async() => { await Program.Manager.SendInterCommunicateMessageAsync("Controller#RequestInfo").ConfigureAwait(false); await Program.Manager.SendInterCommunicateMessageAsync("Service#RequestInfo").ConfigureAwait(false); }).ConfigureAwait(false); } else if (!commands[0].IsEquals("exit")) { Program.Logger.LogInformation( "Commands:" + "\r\n\t" + "info [global | controller-id]: show the information of controllers & services" + "\r\n\t" + "start <name> [controller-id]: start a business service" + "\r\n\t" + "stop <name> [controller-id]: stop a business service" + "\r\n\t" + "refresh: refresh the information of controllers & services" + "\r\n\t" + "help: show available commands" + "\r\n\t" + "exit: shutdown & terminate" ); } command = commands[0].IsEquals("exit") ? null : Console.ReadLine(); } } // wait until be killed else { while (true) { Task.Delay(54321).GetAwaiter().GetResult(); } } // stop and do clean up Program.Stop(); }