节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
3.08 KiB
Stardust
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
}