完成进程启动器接口的设计
This commit is contained in:
176
SHH.ProcessLaunchers/IProcessManager.cs
Normal file
176
SHH.ProcessLaunchers/IProcessManager.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SHH.ProcessLaunchers
|
||||
{
|
||||
/// <summary>
|
||||
/// 进程管理器核心接口
|
||||
/// </summary>
|
||||
public interface IProcessManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册一个要管理的进程配置
|
||||
/// </summary>
|
||||
/// <param name="config">进程配置对象</param>
|
||||
void Register(ProcessConfig config);
|
||||
|
||||
/// <summary>
|
||||
/// 启动指定名称的进程
|
||||
/// </summary>
|
||||
/// <param name="name">配置中定义的 Name</param>
|
||||
void Start(string name);
|
||||
|
||||
/// <summary>
|
||||
/// 启动所有已注册的进程
|
||||
/// </summary>
|
||||
Task StartAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 停止指定进程 (优雅或强制)
|
||||
/// </summary>
|
||||
/// <param name="name">进程名称</param>
|
||||
void Stop(string name);
|
||||
|
||||
/// <summary>
|
||||
/// 停止所有进程
|
||||
/// </summary>
|
||||
void StopAll();
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定进程的资源监控复位接口
|
||||
/// </summary>
|
||||
/// <param name="processName">进程名称</param>
|
||||
void ResetGuard(string processName);
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有进程的实时状态快照
|
||||
/// </summary>
|
||||
List<ProcessInfoSnapshot> GetSnapshot();
|
||||
|
||||
// --- 事件定义 ---
|
||||
|
||||
/// <summary>
|
||||
/// 当接收到子进程的标准输出或错误流时触发
|
||||
/// </summary>
|
||||
event EventHandler<ProcessOutputEventArgs> OnOutputReceived;
|
||||
|
||||
/// <summary>
|
||||
/// 当进程生命周期状态发生变化时触发
|
||||
/// </summary>
|
||||
event EventHandler<ProcessStateEventArgs> OnStateChanged;
|
||||
}
|
||||
|
||||
public class MemoryGuard : IResourceGuard
|
||||
{
|
||||
public string Name => "MemoryGuard";
|
||||
|
||||
private readonly long _warningBytes;
|
||||
private readonly long _criticalBytes;
|
||||
private readonly TimeSpan _alertDuration; // 持续时间阈值 (如 3分钟)
|
||||
|
||||
// --- 内部状态 ---
|
||||
private DateTime? _firstOverLimitTime; // 第一次检测到超限的时间
|
||||
private bool _isAlertLatched = false; // 是否已经报过警 (自锁)
|
||||
|
||||
/// <summary>
|
||||
/// 智能内存哨兵
|
||||
/// </summary>
|
||||
/// <param name="warningMb">警告阈值</param>
|
||||
/// <param name="criticalMb">熔断阈值</param>
|
||||
/// <param name="durationMinutes">必须持续超限多少分钟才报警</param>
|
||||
public MemoryGuard(int warningMb, int criticalMb, int durationMinutes = 3)
|
||||
{
|
||||
_warningBytes = (long)warningMb * 1024 * 1024;
|
||||
_criticalBytes = (long)criticalMb * 1024 * 1024;
|
||||
_alertDuration = TimeSpan.FromMinutes(durationMinutes);
|
||||
}
|
||||
|
||||
public GuardResult Check(Process process, out string reason)
|
||||
{
|
||||
reason = null;
|
||||
try
|
||||
{
|
||||
process.Refresh();
|
||||
long currentUsage = process.WorkingSet64;
|
||||
|
||||
// 1. 优先检查 Critical (熔断线)
|
||||
// 逻辑:熔断涉及生死,不需要防抖,也不受“已报警”锁定的限制。
|
||||
// 哪怕用户标记了已处置,只要内存爆了,必须重启。
|
||||
if (currentUsage > _criticalBytes)
|
||||
{
|
||||
reason = $"[严重] 内存 {FormatSize(currentUsage)} > 熔断线 {FormatSize(_criticalBytes)} (立即执行管控)";
|
||||
// 重启后,物理进程会变,下一次 Check 会是新进程,状态建议在重启时由外部重置,
|
||||
// 或者这里不重置,依靠 ProcessManager 重启后重新创建 Guard 实例。
|
||||
return GuardResult.Critical;
|
||||
}
|
||||
|
||||
// 2. 检查 Warning (警告线) - 包含防抖和自锁逻辑
|
||||
if (currentUsage > _warningBytes)
|
||||
{
|
||||
// A. 如果已经报过警 (已锁定),则不再报,保持沉默
|
||||
if (_isAlertLatched)
|
||||
{
|
||||
return GuardResult.Normal;
|
||||
}
|
||||
|
||||
// B. 如果是刚发现超限,记录时间
|
||||
if (_firstOverLimitTime == null)
|
||||
{
|
||||
_firstOverLimitTime = DateTime.Now;
|
||||
// 这里可以选做:记录一条 Info 日志,告诉用户"正在观察中"
|
||||
// reason = $"内存超限 {FormatSize(currentUsage)},开始计时观察...";
|
||||
return GuardResult.Normal; // 暂时不报 Warning
|
||||
}
|
||||
|
||||
// C. 检查持续时间
|
||||
var duration = DateTime.Now - _firstOverLimitTime.Value;
|
||||
if (duration >= _alertDuration)
|
||||
{
|
||||
// 满足持续时间 -> 触发报警并锁定
|
||||
_isAlertLatched = true;
|
||||
_firstOverLimitTime = null; // 计时归零
|
||||
|
||||
reason = $"[报警] 内存 {FormatSize(currentUsage)} > 阈值 {FormatSize(_warningBytes)} 且持续超过 {_alertDuration.TotalMinutes}分钟";
|
||||
return GuardResult.Warning; // 抛出信号,发邮件!
|
||||
}
|
||||
else
|
||||
{
|
||||
// 还没到时间
|
||||
return GuardResult.Normal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3. 内存正常
|
||||
// 逻辑:如果之前在计时(比如超了1分钟),现在降下来了,则计时器清零。
|
||||
// 但如果已经报过警 (_isAlertLatched=true),则保持锁定,不自动复位。
|
||||
// 除非用户手动点 Reset。
|
||||
|
||||
if (_firstOverLimitTime != null)
|
||||
{
|
||||
_firstOverLimitTime = null; // 波动防抖:由于降下来了,重置观察计时
|
||||
}
|
||||
|
||||
return GuardResult.Normal;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return GuardResult.Normal;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户点击“已处置”时调用
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_isAlertLatched = false;
|
||||
_firstOverLimitTime = null;
|
||||
}
|
||||
|
||||
private string FormatSize(long bytes) => $"{bytes / 1024 / 1024}MB";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user