package com.imutil.common; import com.imutil.mapper.DistQueueMapper; import lombok.extern.slf4j.Slf4j; import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Inject; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import java.time.OffsetDateTime; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * 健康检查服务(对齐设计:监控告警 - 中间件连通、dist_queue 堆积) *

* 聚合 PG / Redis 连通性与分发队列状态分布,输出整体 status: * - UP:PG + Redis 均连通 * - DEGRADED:PG 连通、Redis 异常(限流 fail-open、缓存降级,核心链路仍可用) * - DOWN:PG 异常(消息落库不可用,核心受损) *

* 队列 pending/processing/dead 计数供运维判断是否需干预: * - pending 持续上涨 → worker 处理不过去,考虑扩容 * - processing 长期不归零且无 done 增长 → 工作线程可能卡死 * - dead 积累 → 需排查死信或重发 * * @author imutil */ @Slf4j @Component public class HealthService { @Inject private DistQueueMapper distQueueMapper; @Inject private JedisPool jedisPool; /** 运维测试用:强制返回 DOWN,验证 LB/k8s 探针摘流(默认关) */ @Inject("${imutil.health.forceDown:false}") private boolean forceDown; /** * 采集健康指标 * * @return 含 status / pg / redis / distQueue / timestamp */ public Map health() { // 运维测试开关:强制 DOWN,验证探针据 503 摘流 if (forceDown) { Map r = new LinkedHashMap<>(); r.put("status", "DOWN"); r.put("pg", "DOWN"); r.put("redis", "UP"); r.put("distQueue", new LinkedHashMap<>()); r.put("forced", true); r.put("timestamp", OffsetDateTime.now().toString()); return r; } Map distQueue = new LinkedHashMap<>(); boolean pgUp; try { List> rows = distQueueMapper.countByStatus(); // 四态计数初始化为 0(无记录的状态不出现在 GROUP BY 结果中) long[] cnt = new long[4]; for (Map row : rows) { int st = ((Number) row.get("status")).intValue(); long c = ((Number) row.get("cnt")).longValue(); if (st >= 0 && st < 4) { cnt[st] = c; } } distQueue.put("pending", cnt[0]); distQueue.put("processing", cnt[1]); distQueue.put("dead", cnt[3]); // done(status=2) 不统计:无索引且无限增长,健康检查只关心堆积/死信 pgUp = true; } catch (Exception e) { log.warn("健康检查 PG 查询失败 : {}", e.getMessage()); distQueue.put("error", e.getMessage()); pgUp = false; } boolean redisUp; try (Jedis j = jedisPool.getResource()) { redisUp = "PONG".equals(j.ping()); } catch (Exception e) { log.warn("健康检查 Redis ping 失败 : {}", e.getMessage()); redisUp = false; } String status; if (pgUp && redisUp) { status = "UP"; } else if (pgUp) { status = "DEGRADED"; } else { status = "DOWN"; } Map r = new LinkedHashMap<>(); r.put("status", status); r.put("pg", pgUp ? "UP" : "DOWN"); r.put("redis", redisUp ? "UP" : "DOWN"); r.put("distQueue", distQueue); r.put("timestamp", OffsetDateTime.now().toString()); return r; } }