171 lines
4.9 KiB
C#
171 lines
4.9 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using Prism.Events;
|
|
using System.Net.Http;
|
|
using YY.Admin.Core.Events;
|
|
using YY.Admin.Core.Services;
|
|
using YY.Admin.Helper;
|
|
|
|
namespace YY.Admin.Infrastructure.Network;
|
|
|
|
public class NetworkMonitor : INetworkMonitor
|
|
{
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly IEventAggregator _eventAggregator;
|
|
private readonly SemaphoreSlim _startLock = new(1, 1);
|
|
private readonly object _aggregateLock = new();
|
|
private Task? _loopTask;
|
|
private CancellationTokenSource? _cts;
|
|
private volatile bool _httpProbeOnline;
|
|
private volatile bool _stompTransportOnline;
|
|
private volatile bool _isOnline;
|
|
|
|
public NetworkMonitor(
|
|
IHttpClientFactory httpClientFactory,
|
|
IConfiguration configuration,
|
|
IEventAggregator eventAggregator)
|
|
{
|
|
_httpClientFactory = httpClientFactory;
|
|
_configuration = configuration;
|
|
_eventAggregator = eventAggregator;
|
|
}
|
|
|
|
public bool IsOnline => _isOnline;
|
|
|
|
public event Action<bool>? StatusChanged;
|
|
|
|
/// <inheritdoc />
|
|
public void SetStompTransportOnline(bool online)
|
|
{
|
|
_stompTransportOnline = online;
|
|
RecomputeAggregatedOnline();
|
|
}
|
|
|
|
public async Task StartAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
await _startLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
try
|
|
{
|
|
if (_cts != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
_loopTask = Task.Run(() => MonitorLoopAsync(_cts.Token), _cts.Token);
|
|
}
|
|
finally
|
|
{
|
|
_startLock.Release();
|
|
}
|
|
}
|
|
|
|
private async Task MonitorLoopAsync(CancellationToken cancellationToken)
|
|
{
|
|
// 启动后立即探活一次,避免首屏 10 秒内 IsOnline 恒为 false
|
|
await RunHttpProbeAndRecomputeAsync(cancellationToken).ConfigureAwait(false);
|
|
|
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
|
|
while (await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false))
|
|
{
|
|
await RunHttpProbeAndRecomputeAsync(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
private async Task RunHttpProbeAndRecomputeAsync(CancellationToken cancellationToken)
|
|
{
|
|
var httpOk = await ProbeAsync(cancellationToken).ConfigureAwait(false);
|
|
_httpProbeOnline = httpOk;
|
|
RecomputeAggregatedOnline();
|
|
}
|
|
|
|
private void RecomputeAggregatedOnline()
|
|
{
|
|
var combined = ComputeCombinedOnline();
|
|
bool newValue;
|
|
lock (_aggregateLock)
|
|
{
|
|
if (combined == _isOnline)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_isOnline = combined;
|
|
newValue = combined;
|
|
}
|
|
|
|
StatusChanged?.Invoke(newValue);
|
|
_eventAggregator.GetEvent<NetworkStatusChangedEvent>().Publish(new NetworkStatusChangedPayload
|
|
{
|
|
IsOnline = newValue,
|
|
ChangedAt = DateTime.UtcNow
|
|
});
|
|
}
|
|
|
|
private bool ComputeCombinedOnline()
|
|
{
|
|
if (IsDisconnectedByUser())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return _httpProbeOnline || _stompTransportOnline;
|
|
}
|
|
|
|
private async Task<bool> ProbeAsync(CancellationToken cancellationToken)
|
|
{
|
|
if (IsDisconnectedByUser())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var baseUrl = _configuration.GetValue<string>("JeecgIntegration:BaseUrl")?.TrimEnd('/');
|
|
if (string.IsNullOrWhiteSpace(baseUrl))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 探活策略:优先调用免登录接口,失败后再降级到健康检查接口
|
|
var probeUrls = new[]
|
|
{
|
|
$"{baseUrl}/sys/user/scada/queryUser?current=1&pageSize=1",
|
|
$"{baseUrl}/sys/dict/scada/queryDictItem?pageNo=1&pageSize=1",
|
|
$"{baseUrl}/actuator/health"
|
|
};
|
|
|
|
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
timeoutCts.CancelAfter(TimeSpan.FromSeconds(3));
|
|
var client = _httpClientFactory.CreateClient("JeecgApi");
|
|
foreach (var url in probeUrls)
|
|
{
|
|
try
|
|
{
|
|
using var req = new HttpRequestMessage(HttpMethod.Get, url);
|
|
using var resp = await client.SendAsync(req, timeoutCts.Token).ConfigureAwait(false);
|
|
if (resp.IsSuccessStatusCode)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// 当前探活地址失败后继续尝试下一个降级地址
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsDisconnectedByUser()
|
|
{
|
|
try
|
|
{
|
|
return ServerSettingsStore.Load().DisconnectConnection;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|