change - 多实例运行

master
WenJY 2 months ago
parent 2543f7e825
commit a68596df56

@ -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<SerilogHelper>();
var appConfig = services.GetRequiredService<AppConfig>();
// 强制设置实例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<AppConfig>();
//var logPath = $"{appConfig.logPath}/Logs/{DateTime.UtcNow:yyyy-MM-dd}/";
//Log.Information($"系统初始化完成,日志存放路径:{appConfig.logPath}");
serilogHelper = host.Services.GetRequiredService<SerilogHelper>();
// 等待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<Startup>();
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";
}
}

@ -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");
}
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
@ -43,6 +59,8 @@ namespace ConsoleApp
[Obsolete]
public void ConfigureServices(IServiceCollection services)
{
Console.WriteLine($"Configuring services for Instance {_instanceId}");
// 注册 SerilogHelper 服务
services.AddSingleton<SerilogHelper>();
services.AddControllers();
@ -73,6 +91,7 @@ namespace ConsoleApp
//注册任务调度
//services.AddQuartzSetUp();
Console.WriteLine($"Services for Instance {_instanceId} configured successfully");
}
/// <summary>
@ -81,7 +100,15 @@ namespace ConsoleApp
/// <param name="builder"></param>
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");
}
/// <summary>
@ -91,6 +118,8 @@ namespace ConsoleApp
/// <param name="env"></param>
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");
}
}

@ -36,44 +36,34 @@ namespace SlnMesnac.Ioc
/// Configures dependency injection for the application.
/// </summary>
/// <param name="builder">The Autofac container builder.</param>
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<string>("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");
}
/// <summary>
/// 自动注入接口实现
/// </summary>
/// <param name="builder"></param>
/// <param name="assembly"></param>
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;
}
}
}
}
/// <summary>
/// 自动注入自定义类、抽象类,设置为单例
/// </summary>
/// <param name="builder"></param>
/// <param name="assembly"></param>
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();
}
}
}
/// <summary>
/// 自动注入自定义类,设置生命周期为每次解析返回新实例
/// </summary>
/// <param name="builder"></param>
/// <param name="assembly"></param>
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;
}
}
}
}

Loading…
Cancel
Save