using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Linq; using System.Net; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc; using System.Runtime.Serialization.Formatters; using System.Text; using System.Threading; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Logging; using SuperSocket.SocketBase.Metadata; using SuperSocket.SocketBase.Provider; using SuperSocket.SocketEngine.Configuration; namespace SuperSocket.SocketEngine { /// /// SuperSocket default bootstrap /// public partial class DefaultBootstrap : IBootstrap, ILoggerProvider, IDisposable { private List m_AppServers; private IWorkItem m_ServerManager; /// /// Indicates whether the bootstrap is initialized /// private bool m_Initialized = false; /// /// Global configuration /// private IConfigurationSource m_Config; /// /// Global log /// private ILog m_GlobalLog; /// /// Gets the bootstrap logger. /// ILog ILoggerProvider.Logger { get { return m_GlobalLog; } } /// /// Gets the log factory. /// protected ILogFactory LogFactory { get; private set; } /// /// Gets all the app servers running in this bootstrap /// public IEnumerable AppServers { get { return m_AppServers; } } private readonly IRootConfig m_RootConfig; /// /// Gets the config. /// public IRootConfig Config { get { if (m_Config != null) return m_Config; return m_RootConfig; } } /// /// Gets the startup config file. /// public string StartupConfigFile { get; private set; } /// /// Gets the class. /// public IPerformanceMonitor PerfMonitor { get { return m_PerfMonitor; } } private PerformanceMonitor m_PerfMonitor; private readonly string m_BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; /// /// Gets the base directory. /// /// /// The base directory. /// public string BaseDirectory { get { return m_BaseDirectory; } } partial void SetDefaultCulture(IRootConfig rootConfig); /// /// Initializes a new instance of the class. /// /// The app servers. public DefaultBootstrap(IEnumerable appServers) : this(new RootConfig(), appServers, new Log4NetLogFactory()) { } /// /// Initializes a new instance of the class. /// /// The root config. /// The app servers. public DefaultBootstrap(IRootConfig rootConfig, IEnumerable appServers) : this(rootConfig, appServers, new Log4NetLogFactory()) { } /// /// Initializes a new instance of the class. /// /// The root config. /// The app servers. /// The log factory. public DefaultBootstrap(IRootConfig rootConfig, IEnumerable appServers, ILogFactory logFactory) { if (rootConfig == null) throw new ArgumentNullException("rootConfig"); if (appServers == null) throw new ArgumentNullException("appServers"); if(!appServers.Any()) throw new ArgumentException("appServers must have one item at least", "appServers"); if (logFactory == null) throw new ArgumentNullException("logFactory"); m_RootConfig = rootConfig; SetDefaultCulture(rootConfig); m_AppServers = appServers.ToList(); m_GlobalLog = logFactory.GetLog(this.GetType().Name); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); if (!rootConfig.DisablePerformanceDataCollector) { m_PerfMonitor = new PerformanceMonitor(rootConfig, m_AppServers, null, logFactory); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The PerformanceMonitor has been initialized!"); } if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The Bootstrap has been initialized!"); m_Initialized = true; } /// /// Initializes a new instance of the class. /// /// The config. public DefaultBootstrap(IConfigurationSource config) { if (config == null) throw new ArgumentNullException("config"); SetDefaultCulture(config); var fileConfigSource = config as ConfigurationSection; if (fileConfigSource != null) StartupConfigFile = fileConfigSource.GetConfigSource(); m_Config = config; AppDomain.CurrentDomain.SetData("Bootstrap", this); } /// /// Initializes a new instance of the class. /// /// The config. /// The startup config file. public DefaultBootstrap(IConfigurationSource config, string startupConfigFile) { if (config == null) throw new ArgumentNullException("config"); SetDefaultCulture(config); if (!string.IsNullOrEmpty(startupConfigFile)) StartupConfigFile = startupConfigFile; m_Config = config; AppDomain.CurrentDomain.SetData("Bootstrap", this); } /// /// Creates the work item instance. /// /// Name of the service type. /// The server status metadata. /// protected virtual IWorkItem CreateWorkItemInstance(string serviceTypeName, StatusInfoAttribute[] serverStatusMetadata) { var serviceType = Type.GetType(serviceTypeName, true); return Activator.CreateInstance(serviceType) as IWorkItem; } internal virtual bool SetupWorkItemInstance(IWorkItem workItem, WorkItemFactoryInfo factoryInfo) { try { //Share AppDomain AppServers also share same socket server factory and log factory instances factoryInfo.SocketServerFactory.ExportFactory.EnsureInstance(); factoryInfo.LogFactory.ExportFactory.EnsureInstance(); } catch (Exception e) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.Error(e); return false; } return workItem.Setup(this, factoryInfo.Config, factoryInfo.ProviderFactories.ToArray()); } /// /// Gets the work item factory info loader. /// /// The config. /// The log factory. /// internal virtual WorkItemFactoryInfoLoader GetWorkItemFactoryInfoLoader(IConfigurationSource config, ILogFactory logFactory) { return new WorkItemFactoryInfoLoader(config, logFactory); } /// /// Initializes the bootstrap with a listen endpoint replacement dictionary /// /// The listen end point replacement. /// public virtual bool Initialize(IDictionary listenEndPointReplacement) { return Initialize((c) => ReplaceListenEndPoint(c, listenEndPointReplacement)); } private IServerConfig ReplaceListenEndPoint(IServerConfig serverConfig, IDictionary listenEndPointReplacement) { var config = new ServerConfig(serverConfig); if (serverConfig.Port > 0) { var endPointKey = serverConfig.Name + "_" + serverConfig.Port; IPEndPoint instanceEndpoint; if(!listenEndPointReplacement.TryGetValue(endPointKey, out instanceEndpoint)) { throw new Exception(string.Format("Failed to find Input Endpoint configuration {0}!", endPointKey)); } config.Ip = instanceEndpoint.Address.ToString(); config.Port = instanceEndpoint.Port; } if (config.Listeners != null && config.Listeners.Any()) { var listeners = config.Listeners.ToArray(); for (var i = 0; i < listeners.Length; i++) { var listener = (ListenerConfig)listeners[i]; var endPointKey = serverConfig.Name + "_" + listener.Port; IPEndPoint instanceEndpoint; if (!listenEndPointReplacement.TryGetValue(endPointKey, out instanceEndpoint)) { throw new Exception(string.Format("Failed to find Input Endpoint configuration {0}!", endPointKey)); } listener.Ip = instanceEndpoint.Address.ToString(); listener.Port = instanceEndpoint.Port; } config.Listeners = listeners; } return config; } private IWorkItem InitializeAndSetupWorkItem(WorkItemFactoryInfo factoryInfo) { IWorkItem appServer; try { appServer = CreateWorkItemInstance(factoryInfo.ServerType, factoryInfo.StatusInfoMetadata); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.DebugFormat("The server instance {0} has been created!", factoryInfo.Config.Name); } catch (Exception e) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.Error(string.Format("Failed to create server instance {0}!", factoryInfo.Config.Name), e); return null; } var exceptionSource = appServer as IExceptionSource; if (exceptionSource != null) exceptionSource.ExceptionThrown += new EventHandler(exceptionSource_ExceptionThrown); var setupResult = false; try { setupResult = SetupWorkItemInstance(appServer, factoryInfo); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.DebugFormat("The server instance {0} has been initialized!", appServer.Name); } catch (Exception e) { m_GlobalLog.Error(e); setupResult = false; } if (!setupResult) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.Error("Failed to setup server instance!"); return null; } return appServer; } /// /// Initializes the bootstrap with the configuration, config resolver and log factory. /// /// The server config resolver. /// The log factory. /// public virtual bool Initialize(Func serverConfigResolver, ILogFactory logFactory) { if (m_Initialized) throw new Exception("The server had been initialized already, you cannot initialize it again!"); if (logFactory != null && !string.IsNullOrEmpty(m_Config.LogFactory)) { throw new ArgumentException("You cannot pass in a logFactory parameter, if you have configured a root log factory.", "logFactory"); } IEnumerable workItemFactories; using (var factoryInfoLoader = GetWorkItemFactoryInfoLoader(m_Config, logFactory)) { var bootstrapLogFactory = factoryInfoLoader.GetBootstrapLogFactory(); logFactory = bootstrapLogFactory.ExportFactory.CreateExport(); LogFactory = logFactory; m_GlobalLog = logFactory.GetLog(this.GetType().Name); AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); try { workItemFactories = factoryInfoLoader.LoadResult(serverConfigResolver); } catch (Exception e) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.Error(e); return false; } } m_AppServers = new List(m_Config.Servers.Count()); IWorkItem serverManager = null; //Initialize servers foreach (var factoryInfo in workItemFactories) { IWorkItem appServer = InitializeAndSetupWorkItem(factoryInfo); if (appServer == null) return false; if (factoryInfo.IsServerManager) serverManager = appServer; else if (!(appServer is IsolationAppServer))//No isolation { //In isolation mode, cannot check whether is server manager in the factory info loader if (TypeValidator.IsServerManagerType(appServer.GetType())) serverManager = appServer; } m_AppServers.Add(appServer); } if (serverManager != null) m_ServerManager = serverManager; if (!m_Config.DisablePerformanceDataCollector) { m_PerfMonitor = new PerformanceMonitor(m_Config, m_AppServers, serverManager, logFactory); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The PerformanceMonitor has been initialized!"); } if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The Bootstrap has been initialized!"); try { RegisterRemotingService(); } catch (Exception e) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.Error("Failed to register remoting access service!", e); return false; } m_Initialized = true; return true; } void exceptionSource_ExceptionThrown(object sender, ErrorEventArgs e) { m_GlobalLog.Error(string.Format("The server {0} threw an exception.", ((IWorkItemBase)sender).Name), e.Exception); } void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { m_GlobalLog.Error("The process crashed for an unhandled exception!", (Exception)e.ExceptionObject); } /// /// Initializes the bootstrap with the configuration and config resolver. /// /// The server config resolver. /// public virtual bool Initialize(Func serverConfigResolver) { return Initialize(serverConfigResolver, null); } /// /// Initializes the bootstrap with the configuration /// /// The log factory. /// public virtual bool Initialize(ILogFactory logFactory) { return Initialize(c => c, logFactory); } /// /// Initializes the bootstrap with the configuration /// /// public virtual bool Initialize() { return Initialize(c => c); } /// /// Starts this bootstrap. /// /// public StartResult Start() { if (!m_Initialized) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.Error("You cannot invoke method Start() before initializing!"); return StartResult.Failed; } var result = StartResult.None; var succeeded = 0; foreach (var server in m_AppServers) { if (!server.Start()) { if (m_GlobalLog.IsErrorEnabled) m_GlobalLog.InfoFormat("The server instance {0} has failed to be started!", server.Name); } else { succeeded++; if (Config.Isolation != IsolationMode.None) { if (m_GlobalLog.IsInfoEnabled) m_GlobalLog.InfoFormat("The server instance {0} has been started!", server.Name); } } } if (m_AppServers.Any()) { if (m_AppServers.Count == succeeded) result = StartResult.Success; else if (succeeded == 0) result = StartResult.Failed; else result = StartResult.PartialSuccess; } if (m_PerfMonitor != null) { m_PerfMonitor.Start(); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The PerformanceMonitor has been started!"); } return result; } /// /// Stops this bootstrap. /// public void Stop() { var servers = m_AppServers.ToArray(); if (servers.Any(s => s.Config != null && s.Config.StartupOrder != 0)) { Array.Reverse(servers); } foreach (var server in servers) { if (server.State == ServerState.Running) { server.Stop(); if (Config.Isolation != IsolationMode.None) { if (m_GlobalLog.IsInfoEnabled) m_GlobalLog.InfoFormat("The server instance {0} has been stopped!", server.Name); } } } if (m_PerfMonitor != null) { m_PerfMonitor.Stop(); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The PerformanceMonitor has been stoppped!"); } } /// /// Registers the bootstrap remoting access service. /// protected virtual void RegisterRemotingService() { var bootstrapIpcPort = string.Format("SuperSocket.Bootstrap[{0}]", Math.Abs(AppDomain.CurrentDomain.BaseDirectory.TrimEnd(System.IO.Path.DirectorySeparatorChar).GetHashCode())); var serverChannelName = "Bootstrap"; var serverChannel = ChannelServices.RegisteredChannels.FirstOrDefault(c => c.ChannelName == serverChannelName); if (serverChannel != null) ChannelServices.UnregisterChannel(serverChannel); serverChannel = new IpcServerChannel(serverChannelName, bootstrapIpcPort, new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full }); ChannelServices.RegisterChannel(serverChannel, false); AppDomain.CurrentDomain.SetData("BootstrapIpcPort", bootstrapIpcPort); var bootstrapProxyType = typeof(RemoteBootstrapProxy); if (!RemotingConfiguration.GetRegisteredWellKnownServiceTypes().Any(s => s.ObjectType == bootstrapProxyType)) RemotingConfiguration.RegisterWellKnownServiceType(bootstrapProxyType, "Bootstrap.rem", WellKnownObjectMode.Singleton); } /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) { AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); } } /// /// Releases unmanaged and - optionally - managed resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void ResetPerfMoniter() { if (m_PerfMonitor != null) { m_PerfMonitor.Stop(); m_PerfMonitor.Dispose(); m_PerfMonitor = null; } m_PerfMonitor = new PerformanceMonitor(m_Config, m_AppServers, m_ServerManager, LogFactory); m_PerfMonitor.Start(); if (m_GlobalLog.IsDebugEnabled) m_GlobalLog.Debug("The PerformanceMonitor has been reset for new server has been added!"); } } }