Tony Fich - IT, GameDev, Startups: post #211 — TG.ME

Когда один медленный WebSocket валит весь бой: как мы защищаем игровую логику от I/O-тормозов

На этой неделе еще перед мировым боссом в PvP локации игроки начали разминаться, наказывая своих врагов и всё шло хорошо, пока один клиент c плохим интернетом не заблокировал нам очереди: бой подвис, действия перестали доходить до всех. Разбираемся, почему даже три отдельные горутины (бой ⇢ пользователь ⇢ WebSocket) не спасают, как включить back-pressure и почему отлключение соединений - нормальное решение.

Что пошло не так:
• Каналы в Go блокирующие — если буфер заполнен, отправка ждёт, пока его кто-то прочитает. В нашем случае бой писал в канал пользователя, тот — в канал WebSocket; когда буфер соединения забился, поток пользователя, а потом и бой начинали подвисать.
• WebSocket канал писал клиенту по TCP. Пакеты копились, пока не подтвердится отправка. Один «узкий» клиент тормозил всех.

Казалось бы, все же понятно, все должны писать в каналы друг друга без блокировок, желательно в отдельном потоке вообще, чтобы не тормозить процесс. Но тут тоже не все так просто, есть довольно много событий, которые нужно отправлять клиенту в жесткой последовательности, которую нельзя нарушать, т.к. потеряется целостность данных, пользователь увидит не то что должен и т.д. Поэтому на данный момент мы обходимся именно буферами и следим, чтобы не было переполнения.

Какое решение на данный момент:
1. Троттлинг простых, но шумных метрик.
• События типа «здоровье», «мана» или «онлайн в локации» теперь приходят не чаще X мс: бой кладёт их в буфер, и каждые N мс мы берём только последнее значение. Это избавило клиента от спама и сократило трафик.

2. Жёсткий back-pressure на канале WebSocket
• У WebSocket канала фиксированный буфер 100 сообщений.
• Если при отправке User видит, что буфер полон, мы считаем клиента «slow» и вызываем kick(), который разрывает соединение и больше не пытается в него писать.
• Бизнес-логика (бой, User, AI) никогда не блокируется на I/O.

Почему не делаем «супер-оптимизацию» прямо сейчас? Преждевременные оптимизации — главный пожиратель времени; сначала фиксируем узкое горлышко, потом профилируем под реальные метрики.

Что думаете?

🤔 Сталкивались с тем, что один медленный клиент душит весь сервер? Как решали back-pressure? Делитесь опытом в комментах!

#golang #websocket #gamedev #multiplayer #backend #highload
👍5🔥4🤩2
June 14, 2025 510 1 3