Files
tencent-im-util/src/main/java/com/imutil/common/HealthService.java
T
yixiongandClaude b4c6652f66 fix(review): 代码审查 6 项修复(M2/M3/m1/m2/m3/m5)
集中修复 T8/T9/T11/T15 已实现代码的审查问题:
- M2 令牌桶 Lua 改用 Redis TIME(消除多节点时钟偏斜)
- M3 after 跨租户标记复查授权 + 无授权告警兜底
- m1 before 审计 MsgSeq/MsgRandom null 防御
- m2 HealthService 加 forceDown 开关,实测 DOWN 真返 503
- m3 countByStatus 排除 done(WHERE status IN (0,1,3))
- m5 配额查询异常不缓存(下次重试)
- 附带 app.yml console charset GBK→UTF-8
M1 限流审计/事务收窄暂缓。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-09 09:52:13 +08:00

111 lines
3.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 堆积)
* <p>
* 聚合 PG / Redis 连通性与分发队列状态分布,输出整体 status:
* - UPPG + Redis 均连通
* - DEGRADEDPG 连通、Redis 异常(限流 fail-open、缓存降级,核心链路仍可用)
* - DOWN:PG 异常(消息落库不可用,核心受损)
* <p>
* 队列 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<String, Object> health() {
// 运维测试开关:强制 DOWN,验证探针据 503 摘流
if (forceDown) {
Map<String, Object> 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<String, Object> distQueue = new LinkedHashMap<>();
boolean pgUp;
try {
List<Map<String, Object>> rows = distQueueMapper.countByStatus();
// 四态计数初始化为 0(无记录的状态不出现在 GROUP BY 结果中)
long[] cnt = new long[4];
for (Map<String, Object> 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<String, Object> 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;
}
}