✨ 使用LH-COS代替COS 💩 未经测试
This commit is contained in:
parent
f2001e2338
commit
5f3f3fd43e
|
|
@ -6,14 +6,101 @@ using System.Text;
|
||||||
|
|
||||||
namespace SQLBackupToCOS;
|
namespace SQLBackupToCOS;
|
||||||
|
|
||||||
public class BackupService(ILogger<BackupService> logger, IConfiguration config, COSService cosService) : BackgroundService
|
public class BackupService(ILogger<BackupService> logger, IConfiguration config, OutputService outputService) : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly ILogger<BackupService> _logger = logger;
|
private readonly ILogger<BackupService> _logger = logger;
|
||||||
private readonly IConfiguration _config = config;
|
private readonly IConfiguration _config = config;
|
||||||
private readonly COSService _cosService = cosService;
|
//private readonly COSService _cosService = cosService;
|
||||||
|
private readonly OutputService _outputService = outputService;
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
|
var startTime = _config.GetValue<string>("startedAt");
|
||||||
|
if (!string.IsNullOrWhiteSpace(startTime))
|
||||||
|
{
|
||||||
|
DateTime scheduledTime;
|
||||||
|
|
||||||
|
if (TimeOnly.TryParse(startTime, out var timeOnly))
|
||||||
|
{
|
||||||
|
// 只有时间(如 "08:00:00" 或 "08:00")
|
||||||
|
// 计算今天的目标时间(UTC+8)
|
||||||
|
TimeZoneInfo timeZone;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 尝试使用跨平台的时区 ID
|
||||||
|
timeZone = TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai");
|
||||||
|
}
|
||||||
|
catch (TimeZoneNotFoundException)
|
||||||
|
{
|
||||||
|
// 回退到 Windows 时区 ID
|
||||||
|
timeZone = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
|
||||||
|
}
|
||||||
|
|
||||||
|
var nowUtc8 = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone);
|
||||||
|
|
||||||
|
// 构建今天的目标时间
|
||||||
|
scheduledTime = new DateTime(
|
||||||
|
nowUtc8.Year,
|
||||||
|
nowUtc8.Month,
|
||||||
|
nowUtc8.Day,
|
||||||
|
timeOnly.Hour,
|
||||||
|
timeOnly.Minute,
|
||||||
|
timeOnly.Second);
|
||||||
|
|
||||||
|
// 如果已经过了今天的时间,则安排到明天
|
||||||
|
if (scheduledTime <= nowUtc8)
|
||||||
|
{
|
||||||
|
scheduledTime = scheduledTime.AddDays(1);
|
||||||
|
_logger.LogInformation("Scheduled time already passed today, scheduling for tomorrow: {Time}", scheduledTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换回 UTC 以便计算延迟
|
||||||
|
var scheduledTimeUtc = TimeZoneInfo.ConvertTimeToUtc(scheduledTime, timeZone);
|
||||||
|
var delay = scheduledTimeUtc - DateTime.UtcNow;
|
||||||
|
|
||||||
|
if (delay > TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Waiting until scheduled start time: {Time} UTC+8 (in {Delay})", scheduledTime, delay);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(delay, stoppingToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Cancellation requested before start.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (DateTime.TryParse(startTime, out scheduledTime))
|
||||||
|
{
|
||||||
|
// 完整日期时间(如 "2024-01-15 08:00:00")
|
||||||
|
var delay = scheduledTime - DateTime.Now;
|
||||||
|
|
||||||
|
if (delay > TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Waiting until scheduled start time: {Time} (in {Delay})", scheduledTime, delay);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(delay, stoppingToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Cancellation requested before start.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invalid scheduled start time format: {Time}, starting immediately.", startTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("No valid scheduled start time provided, starting immediately.");
|
||||||
|
}
|
||||||
|
|
||||||
var interval = TimeSpan.FromMinutes(_config.GetValue("BackupIntervalMinutes", 60));
|
var interval = TimeSpan.FromMinutes(_config.GetValue("BackupIntervalMinutes", 60));
|
||||||
using var timer = new PeriodicTimer(interval);
|
using var timer = new PeriodicTimer(interval);
|
||||||
|
|
||||||
|
|
@ -98,7 +185,8 @@ public class BackupService(ILogger<BackupService> logger, IConfiguration config,
|
||||||
Directory.Delete(dumpDir, recursive: true);
|
Directory.Delete(dumpDir, recursive: true);
|
||||||
_logger.LogInformation("Backup completed: {File}", finalDump);
|
_logger.LogInformation("Backup completed: {File}", finalDump);
|
||||||
|
|
||||||
await _cosService.AddFileToCOS(finalDump);
|
await _outputService.AddFileToOutput(finalDump);
|
||||||
|
//await _cosService.AddFileToCOS(finalDump);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,12 @@ using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace SQLBackupToCOS
|
namespace SQLBackupToCOS
|
||||||
{
|
{
|
||||||
public class COSService
|
[Obsolete("This class uses COSXML SDK, now we migrated to use LH-COS")]
|
||||||
|
public class COSService(IConfiguration config, ILogger<COSService> logger)
|
||||||
{
|
{
|
||||||
private readonly IConfiguration _config;
|
private readonly IConfiguration _config = config;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger = logger;
|
||||||
|
|
||||||
public COSService(IConfiguration config, ILogger<COSService> logger)
|
|
||||||
{
|
|
||||||
_config = config;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
public async Task AddFileToCOS(string srcPath)
|
public async Task AddFileToCOS(string srcPath)
|
||||||
{
|
{
|
||||||
var bucketName = _config.GetValue<string>("COS:BucketName") ?? "my-bucket";
|
var bucketName = _config.GetValue<string>("COS:BucketName") ?? "my-bucket";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace SQLBackupToCOS
|
||||||
|
{
|
||||||
|
public class OutputService(IConfiguration config, ILogger<OutputService> logger)
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _config = config;
|
||||||
|
private readonly ILogger _logger = logger;
|
||||||
|
|
||||||
|
public async Task AddFileToOutput(string srcPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string outputPath = _config.GetValue<string>("outputDir") ?? "/output";
|
||||||
|
Directory.CreateDirectory(outputPath);
|
||||||
|
|
||||||
|
// 构建目标文件完整路径
|
||||||
|
var fileName = Path.GetFileName(srcPath);
|
||||||
|
var destFilePath = Path.Combine(outputPath, fileName);
|
||||||
|
|
||||||
|
_logger.LogInformation("Copying backup file from {Source} to {Destination}", srcPath, destFilePath);
|
||||||
|
|
||||||
|
// 异步复制文件
|
||||||
|
await using var sourceStream = new FileStream(
|
||||||
|
srcPath,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.Read,
|
||||||
|
81920,
|
||||||
|
FileOptions.Asynchronous);
|
||||||
|
|
||||||
|
await using var destStream = new FileStream(
|
||||||
|
destFilePath,
|
||||||
|
FileMode.Create,
|
||||||
|
FileAccess.Write,
|
||||||
|
FileShare.None,
|
||||||
|
81920,
|
||||||
|
FileOptions.Asynchronous);
|
||||||
|
|
||||||
|
await sourceStream.CopyToAsync(destStream);
|
||||||
|
|
||||||
|
_logger.LogInformation("Backup file copied successfully to {Destination}", destFilePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to copy backup file to output directory");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,8 @@ var builder = Host.CreateDefaultBuilder(args)
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
services.AddSingleton<COSService>();
|
//services.AddSingleton<COSService>();
|
||||||
|
services.AddSingleton<OutputService>();
|
||||||
services.AddHostedService<BackupService>();
|
services.AddHostedService<BackupService>();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BackupIntervalMinutes": 60,
|
"BackupIntervalMinutes": 60,
|
||||||
|
"extraDir": "",
|
||||||
|
"outputDir": "",
|
||||||
|
"startedAt": "",
|
||||||
"Database": {
|
"Database": {
|
||||||
"Host": "",
|
"Host": "",
|
||||||
"User": "",
|
"User": "",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
},
|
},
|
||||||
"BackupIntervalMinutes": 60,
|
"BackupIntervalMinutes": 60,
|
||||||
"extraDir": "EXTRAPATH",
|
"extraDir": "EXTRAPATH",
|
||||||
|
"outputDir": "OUTPUTPATH",
|
||||||
|
"startedAt": "STARTTIME",
|
||||||
"Database": {
|
"Database": {
|
||||||
"Host": "172.17.0.1",
|
"Host": "172.17.0.1",
|
||||||
"User": "SQLUSER",
|
"User": "SQLUSER",
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,10 @@ services:
|
||||||
COS__SecretId: SECRETID
|
COS__SecretId: SECRETID
|
||||||
COS__SecretKey: SECRETKEY
|
COS__SecretKey: SECRETKEY
|
||||||
COS__FilePath: PATH
|
COS__FilePath: PATH
|
||||||
|
startedAt: '03:00:00'
|
||||||
BackupIntervalMinutes: 1440
|
BackupIntervalMinutes: 1440
|
||||||
extraDir: EXTRAPATH
|
extraDir: EXTRAPATH
|
||||||
|
outputDir: OUTPUTPATH
|
||||||
volumes:
|
volumes:
|
||||||
- srcDir:EXTRAPATH/srcDir:ro
|
- srcDir:EXTRAPATH/srcDir:ro
|
||||||
|
- outputDir:OUTPUTPAPH:rw
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue