Files
Edu/services/msg/src/shared/health/health.controller.ts
SpecialX 7b7abbb309 feat(msg): 完整实现 msg 消息服务
包含 channels/preferences/templates/grpc/kafka/outbox/push/redis 等完整实现
2026-07-10 19:09:52 +08:00

138 lines
3.7 KiB
TypeScript
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.
import { Controller, Get, HttpException, HttpStatus } from "@nestjs/common";
import { sql } from "drizzle-orm";
import { getDb } from "../../config/database.js";
import { esClient } from "../../config/elasticsearch.js";
import { getRedis } from "../redis/redis.client.js";
import { isKafkaHealthy } from "../kafka/kafka.client.js";
import { checkPushGateway } from "../push/push-gateway.client.js";
const SERVICE_NAME = "msg";
/**
* 健康检查端点。
*
* 仲裁依据 G2/readyz 多依赖检查DB/ES/Redis/Kafka/PushGateway
*
* - GET /healthzliveness仅返回进程存活不检查依赖。
* - GET /readyzreadiness检查 DB关键+ ES/Redis/Kafka/PushGateway非关键
* DB 不可用返回 503其他依赖降级标记但不影响 readyz 状态码。
*/
type CheckStatus = "ok" | "disabled" | "error";
interface DependencyCheck {
status: CheckStatus;
error?: string;
}
interface ReadyzResponse {
status: "ok" | "degraded";
service: string;
timestamp: string;
checks: {
database: DependencyCheck;
elasticsearch: DependencyCheck;
redis: DependencyCheck;
kafka: DependencyCheck;
pushGateway: DependencyCheck;
};
}
@Controller()
export class HealthController {
@Get("healthz")
liveness(): { status: string; service: string; timestamp: string } {
return {
status: "ok",
service: SERVICE_NAME,
timestamp: new Date().toISOString(),
};
}
@Get("readyz")
async readiness(): Promise<ReadyzResponse> {
const checks: ReadyzResponse["checks"] = {
database: { status: "ok" },
elasticsearch: { status: "disabled" },
redis: { status: "disabled" },
kafka: { status: "ok" },
pushGateway: { status: "disabled" },
};
// 1. DB关键依赖
try {
const db = getDb();
await db.execute(sql`SELECT 1`);
} catch (error) {
checks.database = {
status: "error",
error: error instanceof Error ? error.message : "database unreachable",
};
}
// 2. Elasticsearch可选依赖
if (esClient) {
try {
await esClient.ping();
checks.elasticsearch = { status: "ok" };
} catch (error) {
checks.elasticsearch = {
status: "error",
error: error instanceof Error ? error.message : "ES unreachable",
};
}
}
// 3. Redis可选依赖
const redis = getRedis();
if (redis) {
try {
const pong = await redis.ping();
checks.redis = { status: pong === "PONG" ? "ok" : "error" };
} catch (error) {
checks.redis = {
status: "error",
error: error instanceof Error ? error.message : "Redis unreachable",
};
}
}
// 4. Kafka非关键依赖降级模式
checks.kafka = { status: isKafkaHealthy() ? "ok" : "error" };
// 5. PushGateway软失败可选依赖
try {
const pushOk = await checkPushGateway();
checks.pushGateway = { status: pushOk ? "ok" : "error" };
} catch {
checks.pushGateway = {
status: "error",
error: "PushGateway unreachable",
};
}
// DB 不可用 -> 503其他依赖降级 -> 200 + degraded 标记
const dbOk = checks.database.status === "ok";
const allOk = dbOk && Object.values(checks).every((c) => c.status === "ok");
if (!dbOk) {
throw new HttpException(
{
status: "unavailable",
service: SERVICE_NAME,
timestamp: new Date().toISOString(),
checks,
},
HttpStatus.SERVICE_UNAVAILABLE,
);
}
return {
status: allOk ? "ok" : "degraded",
service: SERVICE_NAME,
timestamp: new Date().toISOString(),
checks,
};
}
}