MES-ETL/ConsoleApp2/HostedServices/MainHostedService.cs

177 lines
6.8 KiB
C#
Raw Normal View History

2024-01-29 09:29:16 +08:00
using System.Diagnostics;
using System.Text;
using ConsoleApp2.Helpers;
using ConsoleApp2.Helpers.Database;
using ConsoleApp2.HostedServices.Abstractions;
2024-01-18 14:36:36 +08:00
using ConsoleApp2.Options;
2024-01-12 16:50:37 +08:00
using ConsoleApp2.Services;
2024-01-29 09:29:16 +08:00
using ConsoleApp2.Services.ErrorRecorder;
using Microsoft.Extensions.Configuration;
2024-01-04 09:00:44 +08:00
using Microsoft.Extensions.Hosting;
2024-01-12 16:50:37 +08:00
using Microsoft.Extensions.Logging;
2024-01-29 09:29:16 +08:00
using Microsoft.Extensions.Options;
2024-01-04 09:00:44 +08:00
namespace ConsoleApp2.HostedServices;
public class MainHostedService : BackgroundService
{
2024-01-29 09:29:16 +08:00
private Stopwatch? _stopwatch;
2024-01-04 09:00:44 +08:00
private readonly IInputService _input;
private readonly ITransformService _transform;
private readonly IOutputService _output;
2024-01-29 09:29:16 +08:00
private readonly ILogger _logger;
2024-01-12 16:50:37 +08:00
private readonly ProcessContext _context;
2024-01-29 09:29:16 +08:00
private readonly IOptions<DatabaseOutputOptions> _databaseOptions;
private readonly IOptions<TenantDbOptions> _tenantDbOptions;
private readonly IConfiguration _config;
2024-01-04 09:00:44 +08:00
2024-01-29 09:29:16 +08:00
public MainHostedService(IInputService input,
ITransformService transform,
IOutputService output,
ILogger<MainHostedService> logger,
IOptions<TenantDbOptions> tenantDbOptions,
IOptions<DatabaseOutputOptions> databaseOptions,
IConfiguration config,
ProcessContext context)
2024-01-04 09:00:44 +08:00
{
_input = input;
_transform = transform;
_output = output;
2024-01-29 09:29:16 +08:00
_logger = logger;
_tenantDbOptions = tenantDbOptions;
_databaseOptions = databaseOptions;
_config = config;
2024-01-12 16:50:37 +08:00
_context = context;
2024-01-04 09:00:44 +08:00
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
2024-01-29 09:29:16 +08:00
_stopwatch = Stopwatch.StartNew();
2024-02-01 13:41:59 +08:00
await SetVariableAsync(); // 开启延迟写入,禁用重做日志 >>> 重做日志处于禁用状态时绝对不要关闭数据库服务!
2024-01-29 09:29:16 +08:00
var inputTask = ExecuteAndCatch(
async () => await _input.ExecuteAsync(stoppingToken), "文件输入程序出现异常", stoppingToken);
var transformTask = ExecuteAndCatch(
async () => await _transform.ExecuteAsync(stoppingToken), "转换程序出现异常", stoppingToken);
var outputTask = ExecuteAndCatch(
async () => await _output.ExecuteAsync(stoppingToken), "输出程序出现异常", stoppingToken);
await Task.WhenAll(inputTask, transformTask, outputTask);
_stopwatch.Stop();
_logger.LogInformation("***** All tasks completed *****");
_logger.LogInformation("***** ElapseTime: {Time}", (_stopwatch.ElapsedMilliseconds / 1000f).ToString("F3"));
await Task.Delay(5000, stoppingToken);
2024-02-01 13:41:59 +08:00
await SetVariableAsync(false); // 关闭延迟写入,开启重做日志
2024-01-29 09:29:16 +08:00
if (!stoppingToken.IsCancellationRequested)
2024-01-12 16:50:37 +08:00
{
2024-01-29 09:29:16 +08:00
await ExportResultAsync();
_logger.LogInformation("The execution result export to {Path}",
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Result-{ErrorRecorder.UID}.md"));
if (_config["RestoreIndex"] is not null)
await RestoreIndexAsync();
Environment.Exit(0);
}
else Environment.Exit(1);
}
private Task ExecuteAndCatch(Func<Task> func, string message, CancellationToken ct)
{
return Task.Run(async () =>
{
try
2024-01-12 16:50:37 +08:00
{
2024-01-29 09:29:16 +08:00
await func();
}
catch (Exception e)
2024-01-12 16:50:37 +08:00
{
2024-01-29 09:29:16 +08:00
_logger.LogCritical(e, "{Msg}\t{ErrMsg}", message, e.Message);
_context.AddException(e);
Environment.Exit(1);
}
}, ct);
}
2024-02-01 13:41:59 +08:00
private async Task SetVariableAsync(bool enable = true)
{
var connStr = _databaseOptions.Value.ConnectionString
?? throw new ApplicationException("无法还原索引,因为分库配置中没有配置数据库");
if (enable)
{
await DatabaseHelper.NonQueryAsync(connStr,
"""
SET GLOBAL innodb_flush_log_at_trx_commit = 0;
ALTER INSTANCE DISABLE INNODB REDO_LOG;
""");
}
else
{
await DatabaseHelper.NonQueryAsync(connStr,
"""
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
ALTER INSTANCE ENABLE INNODB REDO_LOG;
""");
}
}
2024-01-29 09:29:16 +08:00
/// <summary>
/// 还原所有数据库的索引...
/// </summary>
/// <returns></returns>
/// <exception cref="ApplicationException"></exception>
2024-02-01 13:41:59 +08:00
private async Task RestoreIndexAsync()
2024-01-29 09:29:16 +08:00
{
var databases = _tenantDbOptions.Value.DbList?.Keys
?? throw new ApplicationException("无法还原索引,因为分库配置中没有配置数据库");
var connStr = _databaseOptions.Value.ConnectionString
?? throw new ApplicationException("无法还原索引,因为没有配置数据库连接字符串");
var list = new List<Task>();
foreach(var db in databases)
{
var task = DatabaseHelper.NonQueryAsync(connStr + $";Database={db};",
2024-02-01 13:41:59 +08:00
await File.ReadAllTextAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RestoreIndex.sql"))
);
2024-01-29 09:29:16 +08:00
list.Add(task);
}
2024-02-01 13:41:59 +08:00
await Task.WhenAll(list);
2024-01-29 09:29:16 +08:00
}
private async Task ExportResultAsync()
{
var sb = new StringBuilder();
if (_context.HasException)
sb.AppendLine("# Program Completed With Error");
else sb.AppendLine("# Program Completed Successfully");
sb.AppendLine("## Process Count");
var processCount = new[]
2024-01-18 14:36:36 +08:00
{
2024-01-29 09:29:16 +08:00
new { State = "Input", Count = _context.InputCount },
new { State = "Transform", Count = _context.TransformCount },
new { State = "Output", Count = _context.OutputCount }
2024-01-18 14:36:36 +08:00
};
2024-01-29 09:29:16 +08:00
sb.AppendLine(processCount.ToMarkdownTable());
sb.AppendLine("\n---\n");
sb.AppendLine("## Table Output Progress");
var tableOutputProgress = _context.TableProgress.Select(pair =>
new { Table = pair.Key, Count = pair.Value });
sb.AppendLine(tableOutputProgress.ToMarkdownTable());
sb.AppendLine("\n---\n");
sb.AppendLine("## Result");
var elapsedTime = (_stopwatch!.ElapsedMilliseconds / 1000f);
var result = new[]
2024-01-18 14:36:36 +08:00
{
2024-01-29 09:29:16 +08:00
new { Field = "ElapsedTime", Value = elapsedTime.ToString("F2") },
new
{
Field = "Average Output Speed",
Value = (_context.OutputCount / elapsedTime).ToString("F2") + "records/s"
}
2024-01-18 14:36:36 +08:00
};
2024-01-29 09:29:16 +08:00
sb.AppendLine(result.ToMarkdownTable());
await File.WriteAllTextAsync(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Result-{ErrorRecorder.UID}.md"),
sb.ToString());
2024-01-04 09:00:44 +08:00
}
}