using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using SlnMesnac.Config; using SlnMesnac.Quartz; using TouchSocket.Sockets; using SlnMesnac.Extensions; using Microsoft.AspNetCore.Hosting; using Serilog; using Autofac.Extensions.DependencyInjection; using SlnMesnac.Serilog; namespace ConsoleApp { internal class Program { private static SerilogHelper? serilogHelper; private static IHost? host; static async Task Main(string[] args) { // 解析实例ID - 支持多种参数格式 var instanceId = GetInstanceId(args); Console.Title = $"iMES System - Instance {instanceId}"; // 设置环境变量,确保实例完全隔离 Environment.SetEnvironmentVariable("INSTANCE_ID", instanceId); Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", $"Instance{instanceId}"); try { Thread.CurrentThread.Name = $"Main-{instanceId}"; Console.ForegroundColor = GetConsoleColor(instanceId); AppDomain.CurrentDomain.UnhandledException += (sender, e) => GlobalExceptionHandler(sender, e, instanceId); // 创建完全独立的Host构建器 host = CreateHostBuilder(args, instanceId).Build(); // 预先验证服务 using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; try { serilogHelper = services.GetRequiredService(); serilogHelper.Info($"启动服务"); var appConfig = services.GetRequiredService(); // 强制设置实例ID到配置对象 SetInstanceSpecificConfiguration(appConfig, instanceId); serilogHelper.Info($"实例 {instanceId} 服务初始化完成"); Console.WriteLine($"实例 {instanceId} 服务初始化完成"); } catch (Exception ex) { Console.WriteLine($"实例 {instanceId} 服务初始化失败: {ex.Message}"); serilogHelper.Info($"详细错误: {ex}"); } } // 启动host serilogHelper.Info($"正在启动实例 {instanceId} 的Host..."); Console.WriteLine($"正在启动实例 {instanceId} 的Host..."); var hostTask = host.RunAsync(); // 等待host完全启动 await Task.Delay(2000); // 启动主控制逻辑 Console.WriteLine($"正在启动实例 {instanceId} 的主控制逻辑..."); MainCentralControl mainCentralControl = new MainCentralControl(host); mainCentralControl.Start(); serilogHelper?.Info($"iMES系统实例 {instanceId} 启动成功"); Console.WriteLine($"实例 {instanceId} 已成功启动!"); Console.WriteLine("按 Q 退出程序..."); // 保持程序运行 while (true) { var key = Console.ReadKey(true); if (key.Key == ConsoleKey.Q) { Console.WriteLine($"正在停止实例 {instanceId}..."); break; } } } catch (Exception ex) { Console.WriteLine($"实例 {instanceId} 启动失败: {ex.Message}"); Console.WriteLine($"堆栈跟踪: {ex.StackTrace}"); serilogHelper?.Info($"实例 {instanceId} 启动失败: {ex.Message}\n{ex.StackTrace}"); if (ex.InnerException != null) { Console.WriteLine($"内部异常: {ex.InnerException.Message}"); serilogHelper?.Info($"内部异常: {ex.InnerException.Message}"); } } finally { Console.WriteLine($"实例 {instanceId} 清理资源..."); if (host is IAsyncDisposable asyncDisposable) { await asyncDisposable.DisposeAsync(); } else { host?.Dispose(); } Console.WriteLine($"实例 {instanceId} 已退出"); } } private static string GetInstanceId(string[] args) { // 支持多种参数格式 foreach (var arg in args) { if (arg.StartsWith("--instance")) { if (arg == "--instance") { // 查找下一个参数作为实例ID var index = Array.IndexOf(args, arg); if (index + 1 < args.Length) { return args[index + 1]; } } else if (arg.StartsWith("--instance=")) { return arg.Substring("--instance=".Length); } else { // 格式: --instance1, --instance2 return arg.Substring("--instance".Length); } } } // 如果没有指定实例ID,使用随机ID避免冲突 Random random = new Random(); return random.Next(0000, 9999).ToString(); } private static ConsoleColor GetConsoleColor(string instanceId) { // 为不同实例分配不同颜色,便于区分 return instanceId switch { "1" => ConsoleColor.Green, "2" => ConsoleColor.Cyan, "3" => ConsoleColor.Magenta, "4" => ConsoleColor.Yellow, _ => ConsoleColor.White }; } private static void SetInstanceSpecificConfiguration(AppConfig appConfig, string instanceId) { try { Console.WriteLine($"为实例 {instanceId} 设置特定配置..."); // 使用反射设置所有可能的配置属性 var configType = appConfig.GetType(); var properties = configType.GetProperties(); foreach (var property in properties) { if (property.CanWrite) { var value = property.GetValue(appConfig); if (value is string stringValue) { // 替换路径中的占位符 if (stringValue.Contains("{Instance}") || stringValue.Contains("{instance}")) { var newValue = stringValue .Replace("{Instance}", instanceId) .Replace("{instance}", instanceId); property.SetValue(appConfig, newValue); Console.WriteLine($" 更新 {property.Name}: {newValue}"); } else if (IsPathProperty(property.Name) && !stringValue.Contains(instanceId)) { // 为路径属性添加实例ID var newValue = $"{stringValue}_Instance{instanceId}"; property.SetValue(appConfig, newValue); Console.WriteLine($" 更新 {property.Name}: {newValue}"); } } else if (property.PropertyType == typeof(int) && IsPortProperty(property.Name)) { // 调整端口号 var basePort = (int)value; var newPort = basePort + (int.Parse(instanceId) - 1) * 10; property.SetValue(appConfig, newPort); Console.WriteLine($" 更新 {property.Name}: {newPort}"); } } } } catch (Exception ex) { Console.WriteLine($"设置实例配置时出错: {ex.Message}"); serilogHelper?.Info($"设置实例配置时出错: {ex.Message}"); } } private static bool IsPathProperty(string propertyName) { return propertyName.ToLower().Contains("path") || propertyName.ToLower().Contains("dir") || propertyName.ToLower().Contains("file"); } private static bool IsPortProperty(string propertyName) { return propertyName.ToLower().Contains("port"); } private static void GlobalExceptionHandler(object sender, UnhandledExceptionEventArgs e, string instanceId) { Thread.CurrentThread.Name = $"Error-{instanceId}"; Exception? exception = e.ExceptionObject as Exception; string errorMessage = exception != null ? $"实例 {instanceId} 全局异常捕获:\n{exception.Message}\n{exception.StackTrace}" : $"实例 {instanceId} 未知异常捕获:\n{e.ExceptionObject}"; Console.WriteLine(errorMessage); serilogHelper?.Error(errorMessage); } public static IHostBuilder CreateHostBuilder(string[] args, string instanceId) => Host.CreateDefaultBuilder(args) .UseSerilog((context, config) => { // 为每个实例配置独立的Serilog config.WriteTo.Console( outputTemplate: $"[{{Timestamp:HH:mm:ss}} {{Level:u3}}] Instance{instanceId}: {{Message:lj}}{{NewLine}}{{Exception}}") .WriteTo.File( path: $"./Logs/Instance{instanceId}/log-.txt", rollingInterval: RollingInterval.Day, outputTemplate: $"[{{Timestamp:yyyy-MM-dd HH:mm:ss}} {{Level:u3}}] Instance{instanceId}: {{Message:lj}}{{NewLine}}{{Exception}}"); }) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureAppConfiguration((context, config) => { // 清除默认配置,从头开始构建 config.Sources.Clear(); // 按优先级添加配置源 config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.instance{instanceId}.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables("INSTANCE_") .AddCommandLine(args); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); webBuilder.ConfigureKestrel(serverOptions => { Console.WriteLine(int.Parse(instanceId)); serverOptions.ListenAnyIP(1 + (int.Parse(instanceId) - 1)); }); }) .ConfigureServices((context, services) => { services.AddSingleton(new InstanceInfo { Id = instanceId }); }); } public class InstanceInfo { public string Id { get; set; } = "1"; } }