- PVSM.RU - https://www.pvsm.ru -
Всем привет! Я инженер данных в Альфа-банке и еще на моменте обучения профессии хотел сделать пет-проект с обработкой данных и интересной инфраструктурой. Пришел к выводу, что это должна быть компьютерная игра, и для меня лучше всего подошла Dota 2, но с получением сырых данных было много трудностей, в которые не было времени вникнуть, и я откладывал этот проект долгое время.
Позже мне поручили подготовить проект для обучения студентов основам инженерии данных и специфике работы с данными. Я решил вернуться к той идее и геймифицировать обучение: игровые данные, на мой взгляд, помогают быстрее вовлечься в задачи, которые на бизнес-примерах могут выглядеть слишком абстрактно.
При выборе игры у меня было одно опасение: данные должны быть одновременно интересными и достаточно понятными широкой аудитории. Dota 2 показалась хорошим вариантом. В ней много сущностей и сценариев: игроки, герои, предметы, игровые события, экономика матча, команды и результат.
В итоге я хотел собрать не просто набор таблиц с матчами, а живой контур данных, который регулярно получает новые матчи, дополняет их событиями, сохраняет результат и передаёт его в хранилище данных для дальнейшей аналитики.
Для проекта я использую 2 API: Steam Web API и Stratz GraphQL. Они дополняют друг друга и работают параллельно в своих потоках — Steam API находит новые актуальные матчи, а сервис Stratz парсит их на события и хранит на своей стороне.
Сначала я рассматривал Steam как единственный источник и в его документации описаны endpoint’ы для получения детальных данных — IDOTA2Match_570/GetMatchDetails (docs [1]), который должен возвращать детальную информацию о матче по match_id, однако в текущем состоянии им невозможно пользоваться — https://github.com/ValveSoftware/Dota-2/issues/2715 [2].
Steam все еще остается главным инструментом для получения актуальных матчей, но обогащением занимается Stratz, который по match_id из Steam выдает все события, нас интересующие.
Ключ Steam API можно получить на странице регистрации [3]. После входа в Steam достаточно указать домен приложения и сохранить выданный ключ для запросов к API.
Основной endpoint проекта - GetMatchHistoryBySequenceNum (docs [4]). Он возвращает матчи начиная с заданного sequence number, это удобно для инкрементальной загрузки матчей.
GetMatchHistoryBySequenceNumработает отsequence number, не имеет фильтра «от даты» и не даёт надёжного публичного указателя текущей позиции. При старте потока приходится выбирать приближённую точку, а не получать идеально полный поток новых матчей.
params = {
"key": settings.STEAM_API_KEY,
"matches_requested": 100,
"start_at_match_seq_num": start_sequence,
}
response = await client.get(
"https://api.steampowered.com/IDOTA2Match_570/"
"GetMatchHistoryBySequenceNum/v1",
params=params,
)
Из этого ответа удается получить match_id, время начала матча, длительность, победителя, id игроков и выбранных ими героев. Дополнительно из id игроков endpoint’ом GetPlayerSummaries из Steam API порциями для игровок догружаются их nickname (название профиля).
Для Stratz нужно зарегистрироваться на stratz.com [5] (войти через Steam аккаунт) и создать API токен. Запросы отправляются на https://api.stratz.com/graphql, а токен передается в HTTP-заголовке Authorization: Bearer <token>.
Stratz используется только ради playbackData — массивы событий внутри матча: смерти, покупки, руны, изменения золота и опыта, урон, применение и прокачка способностей. Дополнительно это сервис предоставляет доступ к справочникам предметов, способностей и тд.
Ниже упрощенный пример структуры запросов, в реальном потоке он содержит больше полей:
query {
match(id: 8985356917) {
players {
steamAccountId
playbackData {
deathEvents { time steamAccountId }
purchaseEvents { time itemId }
goldEvents { time amount }
}
}
}
}
Бесплатная квота ключа Stratz позволяет обогатить около 13.7–14 тыс. матчей в сутки. Поэтому проект собирает репрезентативный поток матчей, а не гарантирует полное real-time покрытие всех игр Dota 2.
Контур источников состоит из трёх процессов: discover — перенос информации об актуальных матчей из Steam API в БД PostgreSQL, enrich — обогащение имеющихся матчей и запись этих событий в БД MongoDB, cleanup — удаление старых матчей или матчей, для которых нет информации у Stratz. discover и enrich запускаются последовательно каждые 15 минут, cleanup — каждые 12 часов. Это дает возможность развернуть проект на легковесной удаленной виртуальной машине и использовать её как буферную зону.
Схема потоков данных:
┌─────────────────────┐
│ Steam Web API │
│ │
│ Матчи, игроки, │
│ герои, справочники │
└─────────┬───────────┘
│
│ GetMatchHistoryBySequenceNum
▼
┌─────────────────────┐
│ discover │
│ Каждые 15 минут │
│ │
│ До 300 новых │
│ матчей за запуск │
└─────────┬───────────┘
│
▼
┌──────────────────────────────────────┐
│ PostgreSQL │
│ │
│ matches, match_players, players, │
│ heroes, items, справочники, │
│ discover_runs, enrich_runs │
└─────────┬────────────────────────────┘
│
│ Необогащённые матчи: match_id
▼
┌─────────────────────┐ ┌─────────────────────┐
│ enrich │──────>│ Stratz GraphQL │
│ Каждые 15 минут │ │ │
│ │<──────│ playbackData: │
│ Получает события │ │ события матча │
└─────────┬───────────┘ └─────────────────────┘
│
│ События playbackData
▼
┌──────────────────────────────────────┐
│ MongoDB │
│ │
│ death_events, purchase_events, │
│ gold_events, experience_events, │
│ rune_events, tower_damage_events, │
│ ability_used_events и другие │
└──────────────────────────────────────┘
┌─────────────────────┐
│ cleanup │
│ │
│ Удаляет устаревшие │
│ данные и очищает │
│ MongoDB и PostgreSQL│
└─────────────────────┘
Kafka могла бы быть следующим шагом при переходе к событийной архитектуре и нескольким потребителям, но для polling API и учебного проекта она добавила бы сложность без пропорциональной пользы.
Здесь лежат все сущности со стабильной структурой и явными связями. Некоторые из них:
|
Сущность |
Смысл |
Примеры полей |
|---|---|---|
|
|
Один матч |
|
|
|
Привязка игроков к матчу |
|
|
|
Публичный профиль игрока |
|
|
|
Справочник героев |
|
|
|
Справочник предметов |
|
В этой же базе я храню техническое состояние запусков в discover_runs, enrich_runs и cleanup_runs. Это позволяет ответить не только на вопрос “сколько данных есть”, но и «почему данные не появились в последнем запуске».
События отличаются друг от друга по структуре и объёму, поэтому для них я выбрал MongoDB. Коллекции разделены по типам событий, некоторые из них:
|
Коллекция |
Пример данных |
Для чего нужна |
|---|---|---|
|
|
время смерти, игрок, убийца |
Анализ убийств и темпа матча |
|
|
время и |
Построение предметов |
|
|
время и изменение золота |
Экономика игры |
|
|
время и опыт |
Темп прокачки |
|
|
время, башня, урон |
Давление на объекты |
|
|
время, игрок, тип руны |
Контроль карты |
Общий ключ для соединения источников – match_id. Чтобы связать событие с героем, используется пара match_id и steam_account_id: она соединяется с match_players.account_id в PostgreSQL.
Вся описанная выше система будет использоваться как буферная зона для сбора данных из разрозненных мест чтобы облегчить работу с исходными API. Хранилище данных уже будет собирать эти данные и хранить их историчность, и уже на этих данных можно будет строить гипотезы и аналитику.
Можно сравнить gold/xp события обеих команд в первые 10 или 15 минут и проверить, насколько раннее преимущество связано с победой. Следующий шаг — найти матчи, в которых команда с серьёзным отставанием всё же выиграла, и разобрать, какие события предшествовали этому.
tower_damage_events и события смерти башен позволяют определить время первой взятой башни. Интересная гипотеза: чем раньше команда разрушает первую башню, тем выше вероятность её победы. Здесь можно разделить матчи по длительности, патчу или рейтинговому диапазону игроков.
Из purchase_events можно восстановить порядок покупок и сопоставить его с героем, результатом и динамикой матча. Например, проверить, после каких предметов у команды растёт число убийств или урон по башням.
В этой части проекта я построил не аналитическое хранилище, а операционный слой между нестабильными внешними API и хранилищем данных. Он снимает нагрузку с API, фиксирует состояние загрузок, изолирует особенности источников и даёт хранилищу два подготовленных источника: PostgreSQL с сущностями матча и MongoDB с детальными событиями.
Было бы полезно услышать критику текущего решения и узнать насколько может быть интересно развитие такого проекта. В случае положительной обратной связи я продолжу описание автоматизированного хранилища данных в следующей главе и оставлю исходники проекта.
Автор: vechkasov__02
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/big-data/457978
Ссылки в тексте:
[1] docs: https://steamapi.xpaw.me/IDOTA2Match_570#GetMatchDetails
[2] https://github.com/ValveSoftware/Dota-2/issues/2715: https://github.com/ValveSoftware/Dota-2/issues/2715
[3] странице регистрации: https://steamcommunity.com/dev/apikey
[4] docs: https://steamapi.xpaw.me/IDOTA2Match_570#GetMatchHistoryBySequenceNum
[5] stratz.com: https://stratz.com/api
[6] Источник: https://habr.com/ru/articles/1081990/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1081990
Нажмите здесь для печати.