feat(instance): метрики и healthcheck инстанса для дашборда
Раздел «Инстанс» получает живые показатели сервера (AGENT.md 7.19). - `internal/sysinfo`: загрузка процессора по cgroup v2 (`cpu.stat`/`cpu.max`) с откатом на `/proc/stat`, память по лимиту контейнера либо `/proc/meminfo`, место на разделе данных и размер базы вместе с журналом WAL; - `GET /api/v1/instance/metrics` (только администратор инстанса): метрики, состояние (`ok|warn|failed`), проверки app/database/disk/storage/voice/ gateway, версия и время работы; недоступные источники не ломают ответ, а попадают в проверку `metrics`; - тяжёлые проверки (БД, SFU, запись) кэшируются на 5 секунд: ручку можно опрашивать раз в секунду; - `voice.IssueRoomList` и `AdminClient.Ping` проверяют доступность RoomService и валидность ключей LiveKit (та же связка, что ломала модерацию); - у метрик свой лимит частоты, они исключены из общего лимита API (`RateLimiter.MiddlewareExcept`), иначе открытый раздел съедал бы половину бюджета запросов; - тесты: `internal/sysinfo` (cgroup, /proc, память, диск, размеры файлов) и `internal/server/api_metrics_test.go` (доступ, состав метрик, кэш, 429).
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
"glchat/internal/permissions"
|
||||
"glchat/internal/source"
|
||||
"glchat/internal/store"
|
||||
"glchat/internal/sysinfo"
|
||||
"glchat/internal/voice"
|
||||
)
|
||||
|
||||
@@ -66,6 +67,13 @@ type Server struct {
|
||||
voice *voice.TokenIssuer
|
||||
// voiceAdmin — RoomService для модерации на стороне SFU.
|
||||
voiceAdmin *voice.AdminClient
|
||||
// sysinfoPaths и cpuSampler — источники живых метрик дашборда инстанса.
|
||||
sysinfoPaths sysinfo.Paths
|
||||
cpuSampler *sysinfo.Sampler
|
||||
// metricsLimiter — отдельный лимит для ручки метрик (опрос раз в секунду).
|
||||
metricsLimiter *httpx.RateLimiter
|
||||
// health — кэш тяжёлых проверок инстанса.
|
||||
health healthCache
|
||||
// presence — время последнего обновления last_seen по пользователю.
|
||||
presenceMu sync.Mutex
|
||||
presence map[uint64]time.Time
|
||||
@@ -101,9 +109,13 @@ func New(cfg config.Config, db *database.DB, logger *slog.Logger, deps Deps) *Se
|
||||
slowmode: map[string]time.Time{},
|
||||
presence: map[uint64]time.Time{},
|
||||
webhookSeen: map[string]time.Time{},
|
||||
voice: voice.NewIssuer(cfg.LiveKitAPIKey, cfg.LiveKitAPISecret, cfg.LiveKitTokenTTL),
|
||||
voiceAdmin: voice.NewAdminClient(voice.NewIssuer(cfg.LiveKitAPIKey, cfg.LiveKitAPISecret, cfg.LiveKitTokenTTL), cfg.LiveKitAPIURL),
|
||||
// Дашборд опрашивает метрики раз в секунду: 120/мин с запасом.
|
||||
metricsLimiter: httpx.NewRateLimiter(120, 30),
|
||||
sysinfoPaths: sysinfo.DefaultPaths(),
|
||||
voice: voice.NewIssuer(cfg.LiveKitAPIKey, cfg.LiveKitAPISecret, cfg.LiveKitTokenTTL),
|
||||
voiceAdmin: voice.NewAdminClient(voice.NewIssuer(cfg.LiveKitAPIKey, cfg.LiveKitAPISecret, cfg.LiveKitTokenTTL), cfg.LiveKitAPIURL),
|
||||
}
|
||||
s.cpuSampler = sysinfo.NewSampler(s.sysinfoPaths)
|
||||
switch {
|
||||
case deps.Permissions != nil:
|
||||
s.perms = deps.Permissions
|
||||
@@ -115,7 +127,8 @@ func New(cfg config.Config, db *database.DB, logger *slog.Logger, deps Deps) *Se
|
||||
router.Route("/api/v1", func(apiRouter chi.Router) {
|
||||
// Сессия резолвится один раз на запрос: huma-ручки читают её из контекста.
|
||||
apiRouter.Use(s.sessionContext)
|
||||
apiRouter.Use(s.apiLimiter.Middleware(apiRateLimitKey))
|
||||
// Метрики дашборда вне общего лимита: у них свой (см. api_metrics.go).
|
||||
apiRouter.Use(s.apiLimiter.MiddlewareExcept(apiRateLimitKey, metricsPath))
|
||||
s.api = s.registerAPI(apiRouter)
|
||||
s.registerMetaRoutes(s.api)
|
||||
s.registerAuthRoutes(apiRouter)
|
||||
@@ -123,6 +136,7 @@ func New(cfg config.Config, db *database.DB, logger *slog.Logger, deps Deps) *Se
|
||||
s.registerUserRoutes(s.api)
|
||||
s.registerGuildRoutes(s.api)
|
||||
s.registerInstanceRoutes(s.api)
|
||||
s.registerMetricsRoutes(s.api)
|
||||
s.registerMessageRoutes(s.api)
|
||||
s.registerInviteRoutes(s.api)
|
||||
s.registerFileRoutes(s.api, apiRouter)
|
||||
|
||||
Reference in New Issue
Block a user