MdpSourceHealthCheckJob.cs 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. using Admin.NET.Plugin.AiDOP.DataPlatform;
  2. using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
  3. using Furion.Schedule;
  4. using Microsoft.Extensions.DependencyInjection;
  5. using Microsoft.Extensions.Logging;
  6. namespace Admin.NET.Plugin.AiDOP.Job;
  7. /// <summary>
  8. /// 定时探活 mdp_source:DB SELECT 1 / API GET baseUrl(或 /health)。
  9. /// </summary>
  10. [JobDetail("job_mdp_source_health", Description = "MDP 数据源健康检查", GroupName = "default", Concurrent = false)]
  11. [PeriodSeconds(300, TriggerId = "trigger_mdp_source_health", Description = "每 5 分钟探活数据源", RunOnStart = false)]
  12. public class MdpSourceHealthCheckJob : IJob
  13. {
  14. private readonly IServiceScopeFactory _scopeFactory;
  15. private readonly ILogger _logger;
  16. public MdpSourceHealthCheckJob(IServiceScopeFactory scopeFactory, ILoggerFactory loggerFactory)
  17. {
  18. _scopeFactory = scopeFactory;
  19. _logger = loggerFactory.CreateLogger(nameof(MdpSourceHealthCheckJob));
  20. }
  21. public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
  22. {
  23. using var scope = _scopeFactory.CreateScope();
  24. var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
  25. var scopeFactory = scope.ServiceProvider.GetRequiredService<MdpSourceScopeFactory>();
  26. using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(15) };
  27. var sources = await db.Queryable<MdpSource>().Where(x => x.Status == 1).ToListAsync(stoppingToken);
  28. var ok = 0;
  29. var fail = 0;
  30. foreach (var src in sources)
  31. {
  32. stoppingToken.ThrowIfCancellationRequested();
  33. var now = DateTime.Now;
  34. try
  35. {
  36. if (string.Equals(src.SourceType, "DB", StringComparison.OrdinalIgnoreCase))
  37. {
  38. var scopeDb = await scopeFactory.GetScopeAsync(src.SourceCode, stoppingToken);
  39. await scopeDb.Ado.GetIntAsync("SELECT 1");
  40. src.HealthStatus = 1;
  41. src.HealthMsg = "OK";
  42. ok++;
  43. }
  44. else if (string.Equals(src.SourceType, "API", StringComparison.OrdinalIgnoreCase))
  45. {
  46. if (string.IsNullOrWhiteSpace(src.ApiBaseUrl))
  47. throw new InvalidOperationException("api_base_url 为空");
  48. var url = src.ApiBaseUrl.TrimEnd('/') + "/";
  49. using var resp = await http.GetAsync(url, stoppingToken);
  50. src.HealthStatus = (int)resp.StatusCode is >= 200 and < 500 ? 1 : 0;
  51. src.HealthMsg = $"HTTP {(int)resp.StatusCode}";
  52. if (src.HealthStatus == 1) ok++; else fail++;
  53. }
  54. else
  55. {
  56. src.HealthStatus = 0;
  57. src.HealthMsg = $"未知 source_type={src.SourceType}";
  58. fail++;
  59. }
  60. }
  61. catch (Exception ex)
  62. {
  63. src.HealthStatus = 0;
  64. src.HealthMsg = ex.Message.Length > 480 ? ex.Message[..480] : ex.Message;
  65. fail++;
  66. }
  67. src.LastHealthCheck = now;
  68. src.UpdateTime = now;
  69. await db.Updateable(src)
  70. .UpdateColumns(x => new { x.HealthStatus, x.HealthMsg, x.LastHealthCheck, x.UpdateTime })
  71. .ExecuteCommandAsync(stoppingToken);
  72. }
  73. if (sources.Count > 0)
  74. _logger.LogInformation("[MdpSourceHealthCheckJob] total={Total} ok={Ok} fail={Fail}", sources.Count, ok, fail);
  75. }
  76. }