2025-07-04 14:10:12 +08:00

502 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Serilog;
using System;
using System.Threading;
using System.Threading.Tasks;
using RunRedis.Services;
using RunRedis.Models;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
namespace RunRedis
{
public class EnhancedWorker
{
private readonly RedisSetting _settings;
private readonly RedisProcessManager _processManager;
private readonly CancellationTokenSource _cancellationTokenSource;
private bool _isRunning = false;
private DateTime _lastHealthCheck = DateTime.MinValue;
public EnhancedWorker(RedisSetting settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_processManager = new RedisProcessManager(_settings);
_cancellationTokenSource = new CancellationTokenSource();
}
public async Task ExecuteAsync()
{
_isRunning = true;
Log.Information("🚀 启动简化 Redis 监控服务");
try
{
// Simplified initial setup
await EnsureRedisRunningAsync();
// Start simplified monitoring loop
await SimplifiedMonitoringLoopAsync(_cancellationTokenSource.Token);
}
catch (Exception ex)
{
Log.Fatal("❌ Redis 监控服务发生严重错误: {Error}", ex.Message);
throw;
}
finally
{
_isRunning = false;
Log.Information("⏹️ Redis 监控服务已停止");
}
}
public void Stop()
{
Log.Information("🛑 正在停止 Redis 监控服务");
_cancellationTokenSource.Cancel();
}
private async Task EnsureRedisRunningAsync()
{
Log.Information("🔧 确保 Redis 正在运行");
try
{
if (!_processManager.IsRedisRunning())
{
Log.Information("⚠️ Redis 未运行,正在启动...");
var startResult = await _processManager.StartRedisAsync();
if (!startResult)
{
Log.Error("❌ 启动 Redis 失败,执行强制重启");
await ForceKillAndRestartRedisAsync();
}
else
{
Log.Information("✅ Redis 启动成功");
}
}
else
{
Log.Information("✅ Redis 已在运行");
}
}
catch (Exception ex)
{
Log.Error("❌ 确保 Redis 运行时发生错误: {Error}", ex.Message);
await ForceKillAndRestartRedisAsync();
}
}
/// <summary>
/// 强制终止所有 Redis 进程并快速重启
/// </summary>
private async Task ForceKillAndRestartRedisAsync()
{
try
{
Log.Warning("<22> 执行强制 Redis 进程终止和重启");
// 1. 立即强制终止所有 Redis 进程
await ForceKillAllRedisProcessesAsync();
// 2. 短暂等待系统清理
await Task.Delay(2000);
// 3. 快速重启
Log.Information("🚀 快速重启 Redis");
var startResult = await _processManager.StartRedisAsync();
if (!startResult)
{
Log.Error("❌ 快速重启失败,再次尝试");
await Task.Delay(1000);
await _processManager.StartRedisAsync();
}
else
{
Log.Information("✅ Redis 强制重启成功");
}
}
catch (Exception ex)
{
Log.Error("❌ 强制重启过程中发生错误: {Error}", ex.Message);
}
}
/// <summary>
/// 强制终止所有 Redis 相关进程
/// </summary>
private async Task ForceKillAllRedisProcessesAsync()
{
try
{
Log.Warning("<22> 强制终止所有 Redis 进程");
// 获取所有 Redis 相关进程
var redisProcessNames = new[] { "redis-server", "redis-cli", "redis-sentinel" };
foreach (var processName in redisProcessNames)
{
var processes = Process.GetProcessesByName(processName);
foreach (var process in processes)
{
try
{
if (!process.HasExited)
{
Log.Information("🔪 强制终止进程: {ProcessName} (PID: {ProcessId})", processName, process.Id);
process.Kill(entireProcessTree: true); // 终止整个进程树
// 等待进程完全终止
if (!process.WaitForExit(5000))
{
Log.Warning("⚠️ 进程 {ProcessId} 未在5秒内终止", process.Id);
}
else
{
Log.Information("✅ 进程 {ProcessId} 已终止", process.Id);
}
}
}
catch (Exception ex)
{
Log.Warning("⚠️ 终止进程 {ProcessId} 时发生错误: {Error}", process.Id, ex.Message);
}
finally
{
process.Dispose();
}
}
}
// 额外清理:使用系统命令强制终止
try
{
await ExecuteSystemCommandAsync("taskkill /f /im redis-server.exe");
await ExecuteSystemCommandAsync("taskkill /f /im redis-cli.exe");
}
catch (Exception ex)
{
Log.Debug("系统命令清理失败: {Error}", ex.Message);
}
Log.Information("✅ Redis 进程强制终止完成");
}
catch (Exception ex)
{
Log.Error("❌ 强制终止 Redis 进程时发生错误: {Error}", ex.Message);
}
}
/// <summary>
/// 执行系统命令
/// </summary>
private async Task ExecuteSystemCommandAsync(string command)
{
try
{
var processInfo = new ProcessStartInfo("cmd.exe", $"/c {command}")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using var process = Process.Start(processInfo);
if (process != null)
{
await process.WaitForExitAsync();
}
}
catch (Exception ex)
{
Log.Debug("执行系统命令失败: {Command}, 错误: {Error}", command, ex.Message);
}
}
/// <summary>
/// 简化的监控循环
/// </summary>
private async Task SimplifiedMonitoringLoopAsync(CancellationToken cancellationToken)
{
Log.Information("🔄 启动简化监控循环 (检查间隔: {Interval}秒)", _settings.ProcessCheckIntervalSeconds);
while (!cancellationToken.IsCancellationRequested)
{
try
{
// 简单检查Redis 是否运行
if (!_processManager.IsRedisRunning())
{
Log.Warning("⚠️ Redis 未运行,执行强制重启");
await ForceKillAndRestartRedisAsync();
}
else
{
// 简化的健康检查
await PerformSimplifiedHealthCheckAsync();
}
// 等待下次检查
await Task.Delay(_settings.ProcessCheckIntervalSeconds * 1000, cancellationToken);
}
catch (OperationCanceledException)
{
Log.Information("🛑 监控循环已取消");
break;
}
catch (Exception ex)
{
Log.Error("❌ 监控循环错误: {Error}", ex.Message);
// 发生任何错误都执行强制重启
await ForceKillAndRestartRedisAsync();
await Task.Delay(3000, cancellationToken);
}
}
Log.Information("⏹️ 监控循环已停止");
}
/// <summary>
/// 简化的健康检查 - 检查连接、数据读写功能和性能
/// </summary>
private async Task PerformSimplifiedHealthCheckAsync()
{
try
{
var timeSinceLastCheck = DateTime.Now - _lastHealthCheck;
if (timeSinceLastCheck.TotalSeconds < _settings.HealthCheckIntervalSeconds)
{
return; // 还没到检查时间
}
_lastHealthCheck = DateTime.Now;
// 执行增强的健康检查,包含超时机制
var healthCheckPassed = await PerformEnhancedHealthCheckWithTimeoutAsync();
if (!healthCheckPassed)
{
Log.Warning("⚠️ Redis 健康检查失败,执行强制重启");
await ForceKillAndRestartRedisAsync();
return;
}
Log.Information("✅ Redis 健康检查通过");
}
catch (Exception ex)
{
Log.Error("❌ 健康检查失败: {Error}", ex.Message);
// 健康检查失败就强制重启
await ForceKillAndRestartRedisAsync();
}
}
/// <summary>
/// 执行增强的健康检查,包含连接测试、数据读写测试和超时机制
/// </summary>
private async Task<bool> PerformEnhancedHealthCheckWithTimeoutAsync()
{
const int timeoutSeconds = 5;
var testKey = $"health_check_{DateTime.Now:yyyyMMddHHmmss}_{Guid.NewGuid():N}";
var testValue = $"test_value_{DateTime.Now.Ticks}";
try
{
Log.Debug("🔍 开始增强健康检查 - 测试键: {TestKey}", testKey);
// 使用CancellationToken实现5秒超时
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
try
{
// 1. 基本连接测试
var pingTask = TestRedisConnectionAsync();
var pingResult = await ExecuteWithTimeoutAsync(pingTask, cts.Token);
if (!pingResult)
{
Log.Warning("⚠️ Redis PING 测试失败");
return false;
}
Log.Debug("✅ Redis PING 测试通过");
// 2. 数据写入测试
var writeTask = TestRedisWriteAsync(testKey, testValue);
var writeResult = await ExecuteWithTimeoutAsync(writeTask, cts.Token);
if (!writeResult)
{
Log.Warning("⚠️ Redis 数据写入测试失败");
return false;
}
Log.Debug("✅ Redis 数据写入测试通过");
// 3. 数据读取测试
var readTask = TestRedisReadAsync(testKey, testValue);
var readResult = await ExecuteWithTimeoutAsync(readTask, cts.Token);
if (!readResult)
{
Log.Warning("⚠️ Redis 数据读取测试失败");
return false;
}
Log.Debug("✅ Redis 数据读取测试通过");
return true;
}
catch (OperationCanceledException) when (cts.Token.IsCancellationRequested)
{
Log.Warning("⚠️ Redis 健康检查超时 ({TimeoutSeconds}秒),操作响应过慢", timeoutSeconds);
return false;
}
}
catch (Exception ex)
{
Log.Error("❌ 增强健康检查过程中发生错误: {Error}", ex.Message);
return false;
}
finally
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await CleanupTestDataAsync(testKey, cts.Token); // 需修改方法支持CancellationToken
// 清理测试数据
}
}
/// <summary>
/// 执行带超时的任务 (.NET Core 3.1 兼容版本)
/// </summary>
private async Task<T> ExecuteWithTimeoutAsync<T>(Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<T>();
// 注册取消回调
using var registration = cancellationToken.Register(() =>
tcs.TrySetCanceled(cancellationToken));
// 等待原始任务完成
_ = task.ContinueWith(t =>
{
if (t.IsFaulted)
tcs.TrySetException(t.Exception.InnerException ?? t.Exception);
else if (t.IsCanceled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(t.Result);
}, TaskContinuationOptions.ExecuteSynchronously);
return await tcs.Task;
}
/// <summary>
/// 测试 Redis 数据写入功能
/// </summary>
private async Task<bool> TestRedisWriteAsync(string key, string value)
{
try
{
var command = $"SET {key} \"{value}\"";
var result = await _processManager.ExecuteRedisCliCommandAsync(command);
var success = !string.IsNullOrEmpty(result) && result.Contains("OK");
if (success)
{
Log.Debug("✅ Redis 写入操作成功: {Key} = {Value}", key, value);
}
else
{
Log.Warning("⚠️ Redis 写入操作失败: {Result}", result);
}
return success;
}
catch (Exception ex)
{
Log.Error("❌ Redis 写入测试异常: {Error}", ex.Message);
return false;
}
}
/// <summary>
/// 测试 Redis 数据读取功能
/// </summary>
private async Task<bool> TestRedisReadAsync(string key, string expectedValue)
{
try
{
var command = $"GET {key}";
var result = await _processManager.ExecuteRedisCliCommandAsync(command);
var success = !string.IsNullOrEmpty(result) && result.Trim().Trim('"') == expectedValue;
if (success)
{
Log.Debug("✅ Redis 读取操作成功: {Key} = {Value}", key, result);
}
else
{
Log.Warning("⚠️ Redis 读取操作失败: 期望值 '{ExpectedValue}', 实际值 '{ActualValue}'", expectedValue, result);
}
return success;
}
catch (Exception ex)
{
Log.Error("❌ Redis 读取测试异常: {Error}", ex.Message);
return false;
}
}
/// <summary>
/// 清理测试数据
/// </summary>
private async Task CleanupTestDataAsync(string testKey, CancellationToken token)
{
try
{
if (!string.IsNullOrEmpty(testKey))
{
var command = $"DEL {testKey}";
var result = await _processManager.ExecuteRedisCliCommandAsync(command, token);
Log.Debug("🧹 清理测试数据: {Key}, 结果: {Result}", testKey, result);
}
}
catch (Exception ex)
{
Log.Debug("⚠️ 清理测试数据时发生错误: {Error}", ex.Message);
// 清理失败不影响主流程,只记录日志
}
}
/// <summary>
/// 测试 Redis 连接
/// </summary>
private async Task<bool> TestRedisConnectionAsync()
{
try
{
// 使用 redis-cli 执行简单的 PING 命令
var result = await _processManager.ExecuteRedisCliCommandAsync("PING");
return !string.IsNullOrEmpty(result) && result.Contains("PONG");
}
catch (Exception ex)
{
Log.Debug("Redis 连接测试失败: {Error}", ex.Message);
return false;
}
}
public bool IsRunning => _isRunning;
public void Dispose()
{
_cancellationTokenSource?.Cancel();
_processManager?.Dispose();
_cancellationTokenSource?.Dispose();
}
}
}