Este repositorio no tiene archivo de licencia. Se puede leer, pero eso por sí solo no da permiso para reutilizarlo.
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 |
| Prometheus | http://localhost:9090 |
| Grafana | http://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_up | server | 1, если сервер ответил |
samp_server_info | server, name, hostname, gamemode, language, version, mapname, weburl | всегда 1 |
samp_server_players / samp_server_max_players | server | онлайн / слоты |
samp_server_password | server | запаролен ли сервер |
samp_server_ping_seconds | server | RTT запроса info |
samp_server_query_duration_seconds | server | длительность всего опроса |
samp_server_weather | server | ID погоды из rules |
samp_server_players_list_available | server | удалось ли получить список игроков |
samp_server_last_poll_timestamp_seconds, samp_server_last_success_timestamp_seconds | server | |
samp_server_polls_total, samp_server_poll_failures_total | server | счётчики опросов |
samp_server_player_joins_total, samp_server_player_leaves_total | server | входы и выходы между опросами |
samp_player_online | server, player | 1 — в игре, 0 — вышел (хранится PLAYER_RETENTION) |
samp_player_score | server, player | счёт (только онлайн) |
samp_player_ping_seconds | server, player | пинг, если сервер отвечает на detailed-запрос |
samp_player_online_since_timestamp_seconds | server, player | начало текущей сессии |
samp_player_last_seen_timestamp_seconds | server, 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_ENABLED | true | отдавать веб-интерфейс |
API_TOKEN | — | токен для изменяющих запросов API |
API_READ_AUTH | false | требовать токен и для чтения |
LOG_LEVEL / LOG_FORMAT | info / text | debug/info/warn/error; text/json |
HTTP_READ_TIMEOUT | 10s | |
DATA_FILE | data/servers.json (в Docker /data/servers.json) | файл со списком серверов (.json / .csv) |
INITIAL_SERVERS | — | host:port через запятую; используется, только если файла ещё нет |
POLL_INTERVAL | 15s | период опроса |
QUERY_TIMEOUT | 1500ms | таймаут одного UDP-запроса |
QUERY_RETRIES | 2 | повторы при таймауте |
POLL_WORKERS | 8 | параллельные опросы (= UDP-сокеты) |
QUERY_RULES | true | запрашивать rules (version, mapname, weather…) |
QUERY_PLAYERS | true | запрашивать список игроков |
DETAILED_PLAYERS | true | пробовать detailed-запрос (пинг игроков) |
DEFAULT_ENCODING | cp1251 | кодировка строк: cp1251, cp1252, utf8 (можно задать для каждого сервера) |
DNS_CACHE_TTL | 5m | |
MAX_SERVERS | 200 | максимум серверов в списке |
MAX_PLAYERS_PER_SERVER | 1000 | максимум отслеживаемых игроков на сервер. При переполнении первыми вытесняются офлайн-игроки |
PLAYER_RETENTION | 30m | сколько держать вышедшего игрока со значением 0 |
RECV_BUFFER_SIZE | 64KiB | максимальный размер UDP-ответа |
MEMORY_LIMIT | — | мягкий лимит Go (GOMEMLIMIT), например 40MiB |
MEMORY_LIMIT_RATIO | 0.8 | если MEMORY_LIMIT не задан: доля от лимита cgroup |
GOGC | 50 | стандартная переменная рантайма Go (меньше — меньше памяти, больше CPU) |
Переменные для docker-compose (SAMP_MONITOR_PORT, SAMP_MONITOR_MEM_LIMIT, GRAFANA_*, PROMETHEUS_* и др.) описаны в .env.example.
Память
Потребление ограничено на трёх уровнях:
- Жёсткий лимит контейнера.
mem_limit+memswap_limit(по умолчанию 48 МБ, без swap),pids_limit,cpus. Выше лимита ядро убьёт процесс, поэтому потребление никогда не превысит лимит. - Мягкий лимит Go. При старте процесс читает лимит cgroup и выставляет
GOMEMLIMIT= 80 % от него. Приближаясь к лимиту, GC работает агрессивнее, так что до OOM дело не доходит. Дополнительно заданGOGC=50. - Ограниченные структуры данных.
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
Esta página se genera con lo que GitHub publica sobre el repositorio. samp.directory no aloja el código. Informar de un problema con esta ficha