diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index da75888..47c08cf 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -15,71 +15,275 @@ namespace ConsoleApp internal class Program { private static SerilogHelper? serilogHelper; - static void Main(string[] args) + 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"; - Console.ForegroundColor = ConsoleColor.Yellow; - AppDomain.CurrentDomain.UnhandledException += GlobalExceptionHandler; //全局异常捕获 - var host = CreateHostBuilder(args).Build(); + 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(); + var appConfig = services.GetRequiredService(); + + // 强制设置实例ID到配置对象 + SetInstanceSpecificConfiguration(appConfig, instanceId); + + serilogHelper.Info($"实例 {instanceId} 服务初始化完成"); + Console.WriteLine($"实例 {instanceId} 服务初始化完成"); + } + catch (Exception ex) + { + Console.WriteLine($"实例 {instanceId} 服务初始化失败: {ex.Message}"); + Console.WriteLine($"详细错误: {ex}"); + throw; + } + } + + // 启动host + Console.WriteLine($"正在启动实例 {instanceId} 的Host..."); var hostTask = host.RunAsync(); - var appConfig = host.Services.GetService(); - //var logPath = $"{appConfig.logPath}/Logs/{DateTime.UtcNow:yyyy-MM-dd}/"; - //Log.Information($"系统初始化完成,日志存放路径:{appConfig.logPath}"); - serilogHelper = host.Services.GetRequiredService(); - + + // 等待host完全启动 + await Task.Delay(2000); + + // 启动主控制逻辑 + Console.WriteLine($"正在启动实例 {instanceId} 的主控制逻辑..."); MainCentralControl mainCentralControl = new MainCentralControl(host); mainCentralControl.Start(); - serilogHelper.Info("iMES系统启动成功"); + serilogHelper?.Info($"iMES系统实例 {instanceId} 启动成功"); + Console.WriteLine($"实例 {instanceId} 已成功启动!"); + Console.WriteLine("按 Q 退出程序..."); + + // 保持程序运行 while (true) { - Console.ReadLine(); + var key = Console.ReadKey(true); + if (key.Key == ConsoleKey.Q) + { + Console.WriteLine($"正在停止实例 {instanceId}..."); + break; + } } } catch (Exception ex) { - Console.WriteLine(ex.Message); - if (serilogHelper != null) + Console.WriteLine($"实例 {instanceId} 启动失败: {ex.Message}"); + Console.WriteLine($"堆栈跟踪: {ex.StackTrace}"); + serilogHelper?.Error($"实例 {instanceId} 启动失败: {ex.Message}\n{ex.StackTrace}"); + + if (ex.InnerException != null) { - serilogHelper.Error("系统启动失败,异常:" + ex.Message); + Console.WriteLine($"内部异常: {ex.InnerException.Message}"); + serilogHelper?.Error($"内部异常: {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避免冲突 + return Guid.NewGuid().ToString("N").Substring(0, 4); } - private static void GlobalExceptionHandler(object sender, UnhandledExceptionEventArgs e) + + private static ConsoleColor GetConsoleColor(string instanceId) { - //当前线程名称 - Thread.CurrentThread.Name = "Error"; - if (e.ExceptionObject is Exception exception) + // 为不同实例分配不同颜色,便于区分 + return instanceId switch { - Console.WriteLine("全局异常捕获:"); - Console.WriteLine(exception.Message); - Console.WriteLine(exception.StackTrace); - if (serilogHelper != null) + "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) { - serilogHelper.Error("全局异常捕获:" + exception.Message + "\n" + exception.StackTrace); + 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}"); + } + } } } - else + catch (Exception ex) { - Console.WriteLine("未知异常捕获:"); - Console.WriteLine(e.ExceptionObject); - if (serilogHelper != null) - { - serilogHelper.Error("未知异常捕获:" + e.ExceptionObject); - } + Console.WriteLine($"设置实例配置时出错: {ex.Message}"); } } - public static IHostBuilder CreateHostBuilder(string[] args) => + + 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() + .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(5000 + (int.Parse(instanceId) - 1) * 10); + }); + }) + .ConfigureServices((context, services) => + { + services.AddSingleton(new InstanceInfo { Id = instanceId }); }); } + + public class InstanceInfo + { + public string Id { get; set; } = "1"; + + } } diff --git a/ConsoleApp/Startup.cs b/ConsoleApp/Startup.cs index 53e4687..48c39b1 100644 --- a/ConsoleApp/Startup.cs +++ b/ConsoleApp/Startup.cs @@ -1,6 +1,7 @@ using Autofac; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -36,6 +37,21 @@ namespace ConsoleApp { public class Startup { + public IConfiguration Configuration { get; } + + private readonly IWebHostEnvironment _env; + private readonly string _instanceId; + + public Startup(IConfiguration configuration, IWebHostEnvironment env) + { + Configuration = configuration; + _env = env; + + // 从环境变量获取实例ID + _instanceId = Environment.GetEnvironmentVariable("INSTANCE_ID") ?? "1"; + Console.WriteLine($"Startup for Instance {_instanceId} initialized"); + } + /// /// This method gets called by the runtime. Use this method to add services to the container. /// @@ -43,6 +59,8 @@ namespace ConsoleApp [Obsolete] public void ConfigureServices(IServiceCollection services) { + Console.WriteLine($"Configuring services for Instance {_instanceId}"); + // 注册 SerilogHelper 服务 services.AddSingleton(); services.AddControllers(); @@ -73,6 +91,7 @@ namespace ConsoleApp //注册任务调度 //services.AddQuartzSetUp(); + Console.WriteLine($"Services for Instance {_instanceId} configured successfully"); } /// @@ -81,7 +100,15 @@ namespace ConsoleApp /// public void ConfigureContainer(ContainerBuilder builder) { - DependencyConfigurator.Configure(builder); + Console.WriteLine($"Configuring Autofac container for Instance {_instanceId}"); + + // 为容器添加实例标识 + builder.Properties["InstanceId"] = _instanceId; + + // 确保每个实例有独立的容器配置 + DependencyConfigurator.Configure(builder, _instanceId); + + Console.WriteLine($"Autofac container for Instance {_instanceId} configured successfully"); } /// @@ -91,6 +118,8 @@ namespace ConsoleApp /// public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { + Console.WriteLine($"Configuring application for Instance {_instanceId}"); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -108,7 +137,13 @@ namespace ConsoleApp app.UseEndpoints(endpoints => { endpoints.MapControllers(); + endpoints.MapGet("/instance", async context => + { + await context.Response.WriteAsync($"Running Instance {_instanceId}"); + }); }); + + Console.WriteLine($"Application for Instance {_instanceId} configured successfully"); } } diff --git a/SlnMesnac.Ioc/DependencyConfigurator.cs b/SlnMesnac.Ioc/DependencyConfigurator.cs index 6234bb9..74a9d3f 100644 --- a/SlnMesnac.Ioc/DependencyConfigurator.cs +++ b/SlnMesnac.Ioc/DependencyConfigurator.cs @@ -36,44 +36,34 @@ namespace SlnMesnac.Ioc /// Configures dependency injection for the application. /// /// The Autofac container builder. - public static void Configure(ContainerBuilder builder) + public static void Configure(ContainerBuilder builder, string instanceId) { - //注入Repository - builder.RegisterGeneric(typeof(Repository<>)).As(typeof(Repository<>)); - RegisterImplementations(builder, Assembly.LoadFrom("SlnMesnac.Repository.dll")); - - //注入Plc - RegisterTypeTransient(builder, Assembly.LoadFrom("SlnMesnac.Plc.dll")); - - //注入Rfid - RegisterTypeTransient(builder, Assembly.LoadFrom("SlnMesnac.Rfid.dll")); - - //注入通用类 - RegisterType(builder, Assembly.LoadFrom("SlnMesnac.Common.dll")); - - //注入MQTT - RegisterType(builder, Assembly.LoadFrom("SlnMesnac.Mqtt.dll")); - - //注入TouchSocket - builder.RegisterType(typeof(TcpService)); - RegisterType(builder, Assembly.LoadFrom("SlnMesnac.TouchSocket.dll")); - - //注入业务类 - RegisterType(builder, Assembly.LoadFrom("SlnMesnac.Business.dll")); - - //注入代码生成 - RegisterType(builder, Assembly.LoadFrom("SlnMesnac.Generate.dll")); + Console.WriteLine($"Configuring dependencies for Instance {instanceId}"); + + // 为每个实例创建完全独立的注册 + builder.RegisterInstance(instanceId).Named("InstanceId"); + + // 所有注册都使用 InstancePerDependency,确保完全独立 + builder.RegisterGeneric(typeof(Repository<>)) + .As(typeof(Repository<>)) + .InstancePerDependency(); + + RegisterImplementations(builder, Assembly.LoadFrom("SlnMesnac.Repository.dll"), instanceId); + RegisterTypeTransient(builder, Assembly.LoadFrom("SlnMesnac.Plc.dll"), instanceId); + RegisterTypeTransient(builder, Assembly.LoadFrom("SlnMesnac.Rfid.dll"), instanceId); + RegisterTypeInstancePerDependency(builder, Assembly.LoadFrom("SlnMesnac.Common.dll"), instanceId); + RegisterTypeInstancePerDependency(builder, Assembly.LoadFrom("SlnMesnac.Mqtt.dll"), instanceId); + + builder.RegisterType(typeof(TcpService)).InstancePerDependency(); + RegisterTypeInstancePerDependency(builder, Assembly.LoadFrom("SlnMesnac.TouchSocket.dll"), instanceId); + RegisterTypeInstancePerDependency(builder, Assembly.LoadFrom("SlnMesnac.Business.dll"), instanceId); + RegisterTypeInstancePerDependency(builder, Assembly.LoadFrom("SlnMesnac.Generate.dll"), instanceId); + + Console.WriteLine($"Dependencies for Instance {instanceId} configured successfully"); } - - /// - /// 自动注入接口实现 - /// - /// - /// - private static void RegisterImplementations(ContainerBuilder builder, Assembly assembly) + private static void RegisterImplementations(ContainerBuilder builder, Assembly assembly, string instanceId) { - //自动注入仓储层的接口实现类 var types = assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType) .ToList(); @@ -81,20 +71,20 @@ namespace SlnMesnac.Ioc foreach (var type in types) { var interfaces = type.GetInterfaces(); - foreach (var @interface in interfaces) { - builder.RegisterType(type).As(@interface); + // 为每个实例创建独立的注册键 + var registrationKey = $"{@interface.FullName}_{instanceId}"; + if (!builder.Properties.ContainsKey(registrationKey)) + { + builder.RegisterType(type).As(@interface).InstancePerDependency(); + builder.Properties[registrationKey] = true; + } } } } - /// - /// 自动注入自定义类、抽象类,设置为单例 - /// - /// - /// - private static void RegisterType(ContainerBuilder builder, Assembly assembly) + private static void RegisterTypeInstancePerDependency(ContainerBuilder builder, Assembly assembly, string instanceId) { var types = assembly.GetTypes() .Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType) @@ -102,43 +92,34 @@ namespace SlnMesnac.Ioc foreach (var type in types) { - var interfaces = type.GetInterfaces(); - var baseType = type.BaseType; - - #region 只注入抽象类 Delete By wenjy 2024-03-27 - //if (baseType != null && baseType.IsAbstract && baseType == typeof(PlcAbsractFactory)) - //{ - // builder.RegisterType(type); - //} - #endregion - - - if (!typeof(Delegate).IsAssignableFrom(type)) //不注入委托事件 - { - builder.RegisterType(type).SingleInstance(); - } - } - } - - /// - /// 自动注入自定义类,设置生命周期为每次解析返回新实例 - /// - /// - /// - private static void RegisterTypeTransient(ContainerBuilder builder, Assembly assembly) - { - var types = assembly.GetTypes() - .Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType) - .ToList(); - - foreach (var type in types) - { - var interfaces = type.GetInterfaces(); - var baseType = type.BaseType; - if (!typeof(Delegate).IsAssignableFrom(type)) { - builder.RegisterType(type).AsSelf().InstancePerDependency(); + var registrationKey = $"{type.FullName}_{instanceId}"; + if (!builder.Properties.ContainsKey(registrationKey)) + { + builder.RegisterType(type).InstancePerDependency(); + builder.Properties[registrationKey] = true; + } + } + } + } + + private static void RegisterTypeTransient(ContainerBuilder builder, Assembly assembly, string instanceId) + { + var types = assembly.GetTypes() + .Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType) + .ToList(); + + foreach (var type in types) + { + if (!typeof(Delegate).IsAssignableFrom(type)) + { + var registrationKey = $"{type.FullName}_{instanceId}"; + if (!builder.Properties.ContainsKey(registrationKey)) + { + builder.RegisterType(type).AsSelf().InstancePerDependency(); + builder.Properties[registrationKey] = true; + } } } }