You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
12 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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>();
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();
// 等待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?.Error($"实例 {instanceId} 启动失败: {ex.Message}\n{ex.StackTrace}");
if (ex.InnerException != null)
{
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 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}");
}
}
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<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";
}
}