132 lines
5.0 KiB
C#
132 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using FinlyticBackend.Controllers;
|
|
using FinlyticBackend.Hubs;
|
|
using FinlyticBackend.Util;
|
|
using FinlyticCore.Dtos;
|
|
using Microsoft.AspNetCore.SignalR;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace FinlyticBackend.Services;
|
|
|
|
/// <summary>
|
|
/// Background service that continuously executes live MQTT RPC health pings across all microservices every 5 seconds
|
|
/// and broadcasts real-time health diagnostic updates to all connected SignalR clients on SystemHealthHub (AOT-compliant).
|
|
/// </summary>
|
|
public class SystemHealthBackgroundService : BackgroundService
|
|
{
|
|
private readonly IHubContext<SystemHealthHub> _hubContext;
|
|
private readonly WebMqttClient _mqttClient;
|
|
private readonly ILogger<SystemHealthBackgroundService> _logger;
|
|
|
|
public SystemHealthBackgroundService(
|
|
IHubContext<SystemHealthHub> hubContext,
|
|
WebMqttClient mqttClient,
|
|
ILogger<SystemHealthBackgroundService> logger)
|
|
{
|
|
_hubContext = hubContext;
|
|
_mqttClient = mqttClient;
|
|
_logger = logger;
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_logger.LogInformation("[SystemHealthBackgroundService] Started periodic MQTT RPC health pings (Interval: 5s).");
|
|
|
|
// Initial delay to allow MQTT client to establish connection
|
|
await Task.Delay(3000, stoppingToken);
|
|
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
var healthData = await PerformHealthCheckAsync();
|
|
await _hubContext.Clients.All.SendAsync("ReceiveSystemHealth", healthData, cancellationToken: stoppingToken);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "[SystemHealthBackgroundService] Error performing system health check broadcast.");
|
|
}
|
|
|
|
await Task.Delay(5000, stoppingToken);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes health check pings and returns AOT-compliant DTOs.
|
|
/// </summary>
|
|
public async Task<List<ServiceHealthStatusDto>> PerformHealthCheckAsync()
|
|
{
|
|
var servicesToCheck = new (string Name, string Channel, string Type, string Db)[]
|
|
{
|
|
("FinlyticAssets", "health_Ping/FinlyticAssets", "Asset Catalog & Scraper", "PostgreSQL assets"),
|
|
("FinlyticNews", "health_Ping/FinlyticNews", "News RSS Scraper & AI", "PostgreSQL news"),
|
|
("FinlyticTechnicalAnalysis", "health_Ping/FinlyticTechnicalAnalysis", "Technical Indicators (EMA/RSI)", "PostgreSQL ta"),
|
|
("FinlyticSentiment", "health_Ping/FinlyticSentiment", "NLP Sentiment Engine", "PostgreSQL sentiment"),
|
|
("FinlyticAnalyzer", "health_Ping/FinlyticAnalyzer", "Multi-Layer Signal Engine", "PostgreSQL analyzer"),
|
|
("FinlyticTrades", "health_Ping/FinlyticTrades", "Trade Lifecycle Manager", "PostgreSQL trades"),
|
|
("FinlyticFundamentals", "health_Ping/FinlyticFundamentals", "Financial Statements & Estimates", "PostgreSQL fundamentals"),
|
|
};
|
|
|
|
var results = new List<ServiceHealthStatusDto>
|
|
{
|
|
new(
|
|
Name: "FinlyticBackend",
|
|
Type: "REST API & SignalR Gateway",
|
|
Status: "Online",
|
|
Port: "5000",
|
|
Communication: "Kestrel HTTP / WebSocket",
|
|
Db: "PostgreSQL backend",
|
|
LastPing: DateTime.UtcNow
|
|
)
|
|
};
|
|
|
|
var tasks = servicesToCheck.Select(async s =>
|
|
{
|
|
try
|
|
{
|
|
if (_mqttClient.IsConnected)
|
|
{
|
|
var resp = await _mqttClient.SendRpcRequestAsync<ServiceHealthResponse, EmptyRequest>(
|
|
s.Channel,
|
|
new EmptyRequest(),
|
|
TimeSpan.FromMilliseconds(1200)
|
|
);
|
|
|
|
if (resp != null)
|
|
{
|
|
return new ServiceHealthStatusDto(
|
|
Name: s.Name,
|
|
Type: s.Type,
|
|
Status: "Online",
|
|
Port: "MQTT Only (No HTTP Port)",
|
|
Communication: "MQTT RPC & Pub/Sub",
|
|
Db: s.Db,
|
|
LastPing: resp.Timestamp
|
|
);
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
return new ServiceHealthStatusDto(
|
|
Name: s.Name,
|
|
Type: s.Type,
|
|
Status: "Offline",
|
|
Port: "MQTT Only (No HTTP Port)",
|
|
Communication: "MQTT (No Response / Timeout)",
|
|
Db: s.Db,
|
|
LastPing: DateTime.UtcNow
|
|
);
|
|
});
|
|
|
|
var pingResults = await Task.WhenAll(tasks);
|
|
results.AddRange(pingResults);
|
|
|
|
return results;
|
|
}
|
|
} |