112 lines
3.5 KiB
C#
112 lines
3.5 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using Prism.Events;
|
|
using System.Net.Http;
|
|
using YY.Admin.Core.Events;
|
|
using YY.Admin.Core.Services;
|
|
|
|
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 Task? _loopTask;
|
|
private CancellationTokenSource? _cts;
|
|
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;
|
|
|
|
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)
|
|
{
|
|
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
|
|
while (await timer.WaitForNextTickAsync(cancellationToken).ConfigureAwait(false))
|
|
{
|
|
var online = await ProbeAsync(cancellationToken).ConfigureAwait(false);
|
|
if (online == _isOnline)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_isOnline = online;
|
|
StatusChanged?.Invoke(online);
|
|
_eventAggregator.GetEvent<NetworkStatusChangedEvent>().Publish(new NetworkStatusChangedPayload
|
|
{
|
|
IsOnline = online,
|
|
ChangedAt = DateTime.UtcNow
|
|
});
|
|
}
|
|
}
|
|
|
|
private async Task<bool> ProbeAsync(CancellationToken cancellationToken)
|
|
{
|
|
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;
|
|
}
|
|
}
|