163 lines
6.7 KiB
C#
163 lines
6.7 KiB
C#
using NLog;
|
|
using StackExchange.Redis;
|
|
using System.Globalization;
|
|
|
|
namespace MP.IOC.Services
|
|
{
|
|
public class MetricsCalcService : BackgroundService
|
|
{
|
|
#region Public Constructors
|
|
|
|
/// <summary>
|
|
/// Metodo x calcolo metriche/statistiche di esecuzone realtime
|
|
/// </summary>
|
|
/// <param name="stats"></param>
|
|
/// <param name="config"></param>
|
|
/// <param name="mux"></param>
|
|
public MetricsCalcService(RouteStatsManager stats, IConfiguration config, IConnectionMultiplexer mux)
|
|
{
|
|
_stats = stats;
|
|
_config = config;
|
|
_db = mux.GetDatabase();
|
|
_redisBaseKey = _config.GetValue<string>("ServerConf:RedisBaseKey") ?? "MP_IOC";
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Protected Methods
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
var interval = _config.GetValue<int>("RouteMan:MetricCalcIntervalSeconds", 20);
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay(TimeSpan.FromSeconds(interval), stoppingToken);
|
|
var snapshot = _stats.Snapshot();
|
|
|
|
if (snapshot.Count == 0) continue;
|
|
|
|
var adesso = DateTime.Now;
|
|
var hourStart = new DateTime(adesso.Year, adesso.Month, adesso.Day, adesso.Hour, 0, 0);
|
|
var dayStart = new DateTime(adesso.Year, adesso.Month, adesso.Day, 0, 0, 0);
|
|
|
|
foreach (var kv in snapshot)
|
|
{
|
|
string dest = "IO";
|
|
string method = "NA";
|
|
// verifico se ho chiave completo (dest+method) o parziale
|
|
string rawKey = kv.Key;
|
|
if (rawKey.Contains("|"))
|
|
{
|
|
var splitVal = rawKey.Split("|");
|
|
dest = splitVal[0];
|
|
method = splitVal[1];
|
|
}
|
|
else
|
|
{
|
|
method = rawKey;
|
|
}
|
|
var stat = kv.Value;
|
|
var count = Interlocked.Read(ref stat.Count);
|
|
var totalMs = stat.TotalDuration.TotalMilliseconds;
|
|
var maxMs = stat.MaxDuration.TotalMilliseconds;
|
|
var minMs = (stat.MinDuration == TimeSpan.MaxValue) ? 0 : stat.MinDuration.TotalMilliseconds;
|
|
|
|
Log.Info($"Dest {dest} | Method {method} | Count {count} | TotalDurationMs {totalMs} | MaxDurationMs {maxMs} | MinDurationMs {minMs}");
|
|
|
|
if (_db == null) continue;
|
|
|
|
// Keys
|
|
var hourKey = HourBucketKey(dest, method, hourStart);
|
|
var hoursIndex = HoursIndexKey(dest, method);
|
|
var dayKey = DayBucketKey(dest, dayStart);
|
|
var daysIndex = DaysIndexKey(dest);
|
|
|
|
// Use batch to reduce roundtrips
|
|
var batch = _db.CreateBatch();
|
|
|
|
// Increment hash fields (count and totalMs)
|
|
var taskHourCount = batch.HashIncrementAsync(hourKey, "count", count);
|
|
var taskHourTotal = batch.HashIncrementAsync(hourKey, "totalMs", totalMs);
|
|
var taskHourMax = batch.HashSetAsync(hourKey, "maxMs", maxMs.ToString());
|
|
var taskHourMin = batch.HashSetAsync(hourKey, "minMs", minMs.ToString());
|
|
|
|
var taskDayCount = batch.HashIncrementAsync(dayKey, "count", count);
|
|
var taskDayTotal = batch.HashIncrementAsync(dayKey, "totalMs", totalMs);
|
|
var taskDayMax = batch.HashSetAsync(dayKey, "maxMs", maxMs.ToString());
|
|
var taskDayMin = batch.HashSetAsync(dayKey, "minMs", minMs.ToString());
|
|
|
|
// Add to sorted set indices with score = epoch seconds of bucket start
|
|
var hourScore = ToEpochSeconds(hourStart);
|
|
var dayScore = ToEpochSeconds(dayStart);
|
|
|
|
var taskZAddHour = batch.SortedSetAddAsync(hoursIndex, hourKey, hourScore);
|
|
var taskZAddDay = batch.SortedSetAddAsync(daysIndex, dayKey, dayScore);
|
|
|
|
// Execute batch
|
|
batch.Execute();
|
|
|
|
// Await tasks to ensure completion
|
|
await Task.WhenAll(taskHourCount, taskHourTotal, taskHourMax, taskHourMin,
|
|
taskDayCount, taskDayTotal, taskDayMax, taskDayMin,
|
|
taskZAddHour, taskZAddDay);
|
|
}
|
|
|
|
_stats.Clear();
|
|
}
|
|
catch (TaskCanceledException) { }
|
|
catch (Exception ex)
|
|
{
|
|
Log.Error(ex, "Error flushing metrics");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Private Fields
|
|
|
|
private static string _redisBaseKey = "";
|
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
private readonly IConfiguration _config;
|
|
private readonly IDatabase _db;
|
|
private readonly RouteStatsManager _stats;
|
|
|
|
#endregion Private Fields
|
|
|
|
#region Private Methods
|
|
|
|
//private static string DayBucketKey(string dest, string method, DateTime dtRif)
|
|
private static string DayBucketKey(string dest, DateTime dtRif)
|
|
{
|
|
return $"{_redisBaseKey}:stats:day:{dest}:{dtRif.ToString("yyyyMMdd", CultureInfo.InvariantCulture)}";
|
|
//return $"{_redisBaseKey}:stats:day:{dest}:{method}:{dtRif.ToString("yyyyMMdd", CultureInfo.InvariantCulture)}";
|
|
}
|
|
|
|
//private static string DaysIndexKey(string dest, string method)
|
|
private static string DaysIndexKey(string dest)
|
|
{
|
|
return $"{_redisBaseKey}:stats:days:{dest}";
|
|
//return $"{_redisBaseKey}:stats:days:{dest}:{method}";
|
|
}
|
|
|
|
private static string HourBucketKey(string dest, string method, DateTime dtRif)
|
|
{
|
|
return $"{_redisBaseKey}:stats:hour:{dest}:{method}:{dtRif.ToString("yyyyMMddHH", CultureInfo.InvariantCulture)}";
|
|
}
|
|
|
|
private static string HoursIndexKey(string dest, string method)
|
|
{
|
|
return $"{_redisBaseKey}:stats:hours:{dest}:{method}";
|
|
}
|
|
|
|
private static long ToEpochSeconds(DateTime dt)
|
|
{
|
|
return new DateTimeOffset(dt).ToUnixTimeSeconds();
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
}
|