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.

294 lines
12 KiB
C#

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;
2 months ago
using SlnMesnac.Serilog;
namespace ConsoleApp
{
internal class Program
{
2 months ago
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}");
2 months ago
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
{
2 months ago
serilogHelper = services.GetRequiredService<SerilogHelper>();
2 months ago
serilogHelper.Info($"启动服务");
var appConfig = services.GetRequiredService<AppConfig>();
// 强制设置实例ID到配置对象
SetInstanceSpecificConfiguration(appConfig, instanceId);
serilogHelper.Info($"实例 {instanceId} 服务初始化完成");
Console.WriteLine($"实例 {instanceId} 服务初始化完成");
}
catch (Exception ex)
{
Console.WriteLine($"实例 {instanceId} 服务初始化失败: {ex.Message}");
2 months ago
serilogHelper.Info($"详细错误: {ex}");
}
}
2 months ago
// 启动host
2 months ago
serilogHelper.Info($"正在启动实例 {instanceId} 的Host...");
Console.WriteLine($"正在启动实例 {instanceId} 的Host...");
var hostTask = host.RunAsync();
// 等待host完全启动
await Task.Delay(2000);
// 启动主控制逻辑
Console.WriteLine($"正在启动实例 {instanceId} 的主控制逻辑...");
2 months ago
MainCentralControl mainCentralControl = new MainCentralControl(host);
mainCentralControl.Start();
serilogHelper?.Info($"iMES系统实例 {instanceId} 启动成功");
Console.WriteLine($"实例 {instanceId} 已成功启动!");
Console.WriteLine("按 Q 退出程序...");
// 保持程序运行
2 months ago
while (true)
{
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Q)
{
Console.WriteLine($"正在停止实例 {instanceId}...");
break;
}
2 months ago
}
}
catch (Exception ex)
{
Console.WriteLine($"实例 {instanceId} 启动失败: {ex.Message}");
Console.WriteLine($"堆栈跟踪: {ex.StackTrace}");
2 months ago
serilogHelper?.Info($"实例 {instanceId} 启动失败: {ex.Message}\n{ex.StackTrace}");
if (ex.InnerException != null)
2 months ago
{
Console.WriteLine($"内部异常: {ex.InnerException.Message}");
2 months ago
serilogHelper?.Info($"内部异常: {ex.InnerException.Message}");
2 months ago
}
}
finally
{
Console.WriteLine($"实例 {instanceId} 清理资源...");
if (host is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
host?.Dispose();
}
Console.WriteLine($"实例 {instanceId} 已退出");
}
2 months ago
}
private static string GetInstanceId(string[] args)
2 months ago
{
// 支持多种参数格式
foreach (var arg in args)
2 months ago
{
if (arg.StartsWith("--instance"))
2 months ago
{
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);
}
2 months ago
}
}
2 months ago
// 如果没有指定实例ID使用随机ID避免冲突
2 months ago
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
2 months ago
{
Console.WriteLine($"为实例 {instanceId} 设置特定配置...");
// 使用反射设置所有可能的配置属性
var configType = appConfig.GetType();
var properties = configType.GetProperties();
foreach (var property in properties)
2 months ago
{
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}");
}
}
2 months ago
}
}
catch (Exception ex)
{
Console.WriteLine($"设置实例配置时出错: {ex.Message}");
2 months ago
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);
2 months ago
}
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";
}
}