using NewLife;
using NewLife.Log;
using NewLife.Remoting.Models;
using NewLife.Threading;
using Stardust.Data;
using Stardust.Data.Nodes;
namespace Stardust.Server.Services;
/// <summary>节点在线服务</summary>
public class NodeOnlineService(IServiceProvider serviceProvider, StarServerSetting setting, ITracer tracer) : IHostedService
{
#region 属性
private TimerX _timer;
private NodeService _nodeService;
#endregion
#region 方法
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new TimerX(CheckNodeOnline, null, 30_000, 30_000) { Async = true };
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer.TryDispose();
return Task.CompletedTask;
}
private void CheckNodeOnline(Object state)
{
// 节点超时
var sessionTimeout = setting.SessionTimeout;
if (sessionTimeout > 0)
{
using var span = tracer?.NewSpan(nameof(CheckNodeOnline));
_nodeService ??= serviceProvider.GetService<NodeService>();
var rs = NodeOnline.ClearExpire(TimeSpan.FromSeconds(sessionTimeout));
if (rs != null)
{
foreach (var online in rs)
{
var node = online?.Node;
if (node != null)
{
var msg = L.T("[{0}/{1}] logged in at {2}, last active at {3}", node, online?.SessionID, online.CreateTime, online.UpdateTime);
node.WriteHistory(L.T("Timed out offline"), true, msg, online.CreateIP);
// 结算在线时长统一由 RemoveOnline 负责
_nodeService.RemoveOnline(new DeviceContext { Device = node, Online = online });
CheckOffline(node, L.T("Timed out offline"));
}
}
}
}
}
public static void CheckOffline(Node node, String reason)
{
// 下线告警
if (node.AlarmOnOffline)
{
var webhook = RobotHelper.GetAlarm(node.Project, node.Category, node.WebHook);
if (webhook.IsNullOrEmpty()) return;
// 查找该节点还有没有其它实例在线
var olts = NodeOnline.FindAllByNodeId(node.ID);
if (olts.Count == 0)
{
var msg = $"节点[{node.Name}]已下线!{reason} IP={node.IP}";
RobotHelper.SendAlarmWithRecord(node.Category, node.WebHook, "节点下线告警", msg, "节点下线", node.Name);
}
}
}
/// <summary>检查节点上线恢复。在节点登录或心跳时调用</summary>
/// <param name="node">节点</param>
public static void CheckOnline(Node node)
{
if (node == null || !node.AlarmOnOffline) return;
var webhook = RobotHelper.GetAlarm(node.Project, node.Category, node.WebHook);
if (webhook.IsNullOrEmpty()) return;
RobotHelper.RecoverAlarm("节点下线", node.Name, node.Category, webhook);
}
#endregion
}
|