samp.directory
← Скрипты и библиотеки

h5vx/samp-monitor

Prometheus exporter for SAMP Server statistics and players

Инструмент★ 0обновлён 22 дня назадБез лицензииGo
Открыть на GitHub

В этом репозитории нет файла лицензии. Код открыт для чтения, но само по себе это не даёт права использовать его.

README.md

SA-MP Monitor

Мониторинг серверов San Andreas Multiplayer (SA-MP / open.mp): экспортёр метрик Prometheus, REST API, веб-интерфейс для управления списком серверов и готовый дашборд Grafana.

  • Написан на Go без внешних зависимостей: протокол запросов SA-MP, формат Prometheus и хранилище реализованы вручную.
  • Работает в пределах ~10–15 МБ RSS. Потребление памяти жёстко ограничено (подробнее в разделе Память).
  • Отслеживает статус каждого игрока (онлайн/офлайн), его счёт, пинг и длительность сессии.
  • Настраивается только переменными окружения.

Быстрый старт

cp .env.example .env        # при желании поправьте
docker compose up -d --build
СервисURL
Веб-интерфейсhttp://localhost:8080
Метрикиhttp://localhost:8080/metrics
Prometheushttp://localhost:9090
Grafanahttp://localhost:3000 (admin / admin)

Дашборд SA-MP Monitor подключается через provisioning и открывается как домашний.

Чтобы попробовать без настоящего сервера, запустите фейковый SA-MP сервер с «живыми» игроками:

go run ./cmd/fakesamp     # напечатает адрес, добавьте его в UI

API

Если задан API_TOKEN, все изменяющие запросы требуют заголовок Authorization: Bearer <token> (или X-API-Token). Запросы на чтение требуют его только при API_READ_AUTH=true.

МетодПутьОписание
GET/api/serversсписок серверов с текущим статусом
GET/api/servers/{address}сервер + игроки онлайн и недавно вышедшие
POST/api/serversдобавить: {"address":"1.2.3.4:7777","name":"My RP","encoding":"cp1251"}
PATCH/api/servers/{address}изменить name / encoding
DELETE/api/servers/{address}удалить из мониторинга
POST/api/servers/{address}/pollопросить сервер вне очереди
GET/api/infoверсия и настройки для UI
GET/metricsметрики Prometheus
GET/healthzпроверка живости

Адрес указывается как host[:port], порт по умолчанию 7777. Поддерживаются домены (с кэшем DNS) и только IPv4: у протокола SA-MP нет IPv6.

curl -X POST localhost:8080/api/servers -H 'Content-Type: application/json' \
     -d '{"address":"127.0.0.1:7777","name":"Local"}'
curl -X DELETE localhost:8080/api/servers/127.0.0.1:7777

Список серверов хранится в DATA_FILE. Формат выбирается по расширению: .json (по умолчанию) или .csv (address,name,encoding,added_at). Запись атомарная (временный файл + rename).

Метрики

У каждого сервера одно уникальное значение метки server — нормализованный host:port.

МетрикаМеткиОписание
samp_server_upserver1, если сервер ответил
samp_server_infoserver, name, hostname, gamemode, language, version, mapname, weburlвсегда 1
samp_server_players / samp_server_max_playersserverонлайн / слоты
samp_server_passwordserverзапаролен ли сервер
samp_server_ping_secondsserverRTT запроса info
samp_server_query_duration_secondsserverдлительность всего опроса
samp_server_weatherserverID погоды из rules
samp_server_players_list_availableserverудалось ли получить список игроков
samp_server_last_poll_timestamp_seconds, samp_server_last_success_timestamp_secondsserver
samp_server_polls_total, samp_server_poll_failures_totalserverсчётчики опросов
samp_server_player_joins_total, samp_server_player_leaves_totalserverвходы и выходы между опросами
samp_player_onlineserver, player1 — в игре, 0 — вышел (хранится PLAYER_RETENTION)
samp_player_scoreserver, playerсчёт (только онлайн)
samp_player_ping_secondsserver, playerпинг, если сервер отвечает на detailed-запрос
samp_player_online_since_timestamp_secondsserver, playerначало текущей сессии
samp_player_last_seen_timestamp_secondsserver, playerкогда игрока видели последний раз
samp_exporter_*память, горутины, пропущенные опросы и т. д.

Про список игроков: сначала выполняется запрос d (id, ник, счёт, пинг). Если сервер его не поддерживает, используется c (ник, счёт), а к d мониторинг вернётся через час. SA-MP не отдаёт список, когда на сервере больше 100 игроков. В этом случае samp_server_players_list_available равна 0, и повторная попытка будет через 10 минут.

Конфигурация

ПеременнаяПо умолчаниюОписание
LISTEN_ADDR:8080адрес HTTP-сервера
METRICS_PATH/metricsпуть метрик
UI_ENABLEDtrueотдавать веб-интерфейс
API_TOKEN—токен для изменяющих запросов API
API_READ_AUTHfalseтребовать токен и для чтения
LOG_LEVEL / LOG_FORMATinfo / textdebug/info/warn/error; text/json
HTTP_READ_TIMEOUT10s
DATA_FILEdata/servers.json (в Docker /data/servers.json)файл со списком серверов (.json / .csv)
INITIAL_SERVERS—host:port через запятую; используется, только если файла ещё нет
POLL_INTERVAL15sпериод опроса
QUERY_TIMEOUT1500msтаймаут одного UDP-запроса
QUERY_RETRIES2повторы при таймауте
POLL_WORKERS8параллельные опросы (= UDP-сокеты)
QUERY_RULEStrueзапрашивать rules (version, mapname, weather…)
QUERY_PLAYERStrueзапрашивать список игроков
DETAILED_PLAYERStrueпробовать detailed-запрос (пинг игроков)
DEFAULT_ENCODINGcp1251кодировка строк: cp1251, cp1252, utf8 (можно задать для каждого сервера)
DNS_CACHE_TTL5m
MAX_SERVERS200максимум серверов в списке
MAX_PLAYERS_PER_SERVER1000максимум отслеживаемых игроков на сервер. При переполнении первыми вытесняются офлайн-игроки
PLAYER_RETENTION30mсколько держать вышедшего игрока со значением 0
RECV_BUFFER_SIZE64KiBмаксимальный размер UDP-ответа
MEMORY_LIMIT—мягкий лимит Go (GOMEMLIMIT), например 40MiB
MEMORY_LIMIT_RATIO0.8если MEMORY_LIMIT не задан: доля от лимита cgroup
GOGC50стандартная переменная рантайма Go (меньше — меньше памяти, больше CPU)

Переменные для docker-compose (SAMP_MONITOR_PORT, SAMP_MONITOR_MEM_LIMIT, GRAFANA_*, PROMETHEUS_* и др.) описаны в .env.example.

Память

Потребление ограничено на трёх уровнях:

  1. Жёсткий лимит контейнера. mem_limit + memswap_limit (по умолчанию 48 МБ, без swap), pids_limit, cpus. Выше лимита ядро убьёт процесс, поэтому потребление никогда не превысит лимит.
  2. Мягкий лимит Go. При старте процесс читает лимит cgroup и выставляет GOMEMLIMIT = 80 % от него. Приближаясь к лимиту, GC работает агрессивнее, так что до OOM дело не доходит. Дополнительно задан GOGC=50.
  3. Ограниченные структуры данных.
    • MAX_SERVERS и MAX_PLAYERS_PER_SERVER ограничивают число серверов и игроков в памяти, PLAYER_RETENTION — время хранения вышедших игроков.
    • Число воркеров фиксировано. У каждого воркера один UDP-сокет и один переиспользуемый буфер.
    • Разбор пакетов идёт без аллокаций: срезы указывают прямо в буфер, поиск игрока по нику тоже не аллоцирует.
    • /metrics пишется потоком через bufio из пула, без промежуточных объектов. gzip отключён, так как он стоит около 800 КБ на запрос.
    • Размер тела запросов API и заголовков ограничен.

Грубая оценка: около 250 байт на отслеживаемого игрока и около 2 КБ на сервер. При 200 серверах по 100 игроков это примерно 5 МБ данных.

Разработка

go test -race ./...
go run ./cmd/samp-monitor
python3 grafana/gen_dashboard.py   # пересобрать grafana/dashboards/samp.json

Структура:

cmd/samp-monitor      точка входа, лимиты памяти, healthcheck
cmd/fakesamp          фейковый SA-MP сервер для отладки
internal/samp         протокол запросов SA-MP (+ samptest)
internal/monitor      список серверов, поллер, состояние игроков
internal/metrics      вывод в формате Prometheus
internal/api          REST API и встроенный веб-интерфейс (web/index.html)
internal/store        хранилище JSON/CSV
grafana/              provisioning и дашборд (генерируется gen_dashboard.py)
prometheus/           конфиг scrape

prometheus-exportersamp

Страница собрана из того, что GitHub публикует о репозитории. samp.directory не хранит этот код. Сообщить о проблеме с этой записью