Я больше имел в виду, что в случае с HttpListener придётся самостоятельно регулировать количество запросов, обрабатываемых одновременно, отслеживая количество выполненных и завершенных GetContextAsync методов, и т.д.. В этом ничего очень сложного нет, но IIS пока за нас неплохо справляется :)
Всё, что нам сейчас нужно от ASP.NET/IIS — это менеджмент потоков-воркеров и потоков ввода-вывода для обработки запросов, достаточно эффективный (если не считать досадного бага с блокировкой :)). С HttpListener этим придётся заняться самостоятельно.
В принципе, ASP.NET/IIS для нас имеет оверхед порядка
Да, всё верно поняли. Предлагаю посмотреть www.youtube.com/watch?v=S7fZFpXFWU8. Там всё более подробно, и про велосипеды, и про масштабируемость :)
У нас местами не слишком реляционная структура, поэтому отдельно по таблицам не всегда будет эффективно, подробности здесь www.youtube.com/watch?v=S7fZFpXFWU8 :). Спасибо за отзыв :)
Рома, привет :)
— касательно TopN не совсем понял, но текущий вариант Shrink не создаёт нам никаких проблем по нагрузке, забирая на боевых серверах до 0.2% времени и процессора. Если придавит — будем думать еще :)
— по поводу проверок: если мы сделаем TryRemove элемента, который другой поток успеет забрать и сделать Pin, то можем получить две копии одной и той же сущности — одну удалённую будет редактировать один поток, и вторую загрузит в кэш и начнёт тоже редактировать другой. Именно поэтому мы проверяем результат методов Pin/Evict, они возвращают false, если параллельно пройдёт другая операция из этой пары (т.е. собрались удалять, не успели, т.к. объект успели забрать и сделать Pin, и наоборот, достали из кэш, не успели сделать Pin, т.к. элемент вытеснили)
— Даффи, конечно же, читали)
— могут, количество событий по кэшу процессора не замеряли, но стараемся подходить аккуратно к этим операциям
— в локальном кэше должна быть актуальная копия, ну и по сути, мы же там и редактируем объект, т.е. после разблокирования там уже будут данные, это ничего не стоит. Если случится ошибка — то тогда мы можем выбросить элемент из кэш принудительно, из-за непонятного состояния, в котором могли оставить
— сборщик мусора тормозил раньше хорошо, поборолись успешно, подробности ищи здесь www.youtube.com/watch?v=S7fZFpXFWU8
— речь идёт максимум о сотнях тысяч
Целиком в память у нас не поместится, к сожалению
Спасибо, с интересом изучу :)
Было бы интересно узнать о Вашей специфике
У всех свои условия :)
Под железкой имелся в виду целый сервер :).
Такой вариант может спровоцировать больше ожиданий, сама чистая игровая логика, без сохранения а базу, также достаточно может быть трудоемкой. Но не буду спорить, возможно, при другом профиле нагрузки такой вариант будет приемлемым
Если синхронно сохранять, то согласованность сохранит и надежность обеспечит. Write-ahead log никто не отменял :)
То есть по одному такому потоку на пользователя? Если потоков меньше, чем активных пользователей, то надо отслеживать, какой поток каких пользователей взял, или фиксированно делить. Вариантов много, конечно. Пока мы проблем с блокировками не испытываем, как писалось выше в статье, за секунду на одной железке лишь несколько столкновений на блокировках
База дает большую гарантию надежности. Несколько раз было, конечно :)
По поводу обработки в одном потоке — вы имели в виду вообще всех пользователей? Логика достаточно тяжелая, одного потока будет мало :). Параллельно обрабатывается много пользователей
Блокировки потому, что к игроку может прийти какой либо внешний импульс во время обработки его собственного запрос, например.
Можно сбрасывать и не сразу, но есть риск потерять данные при внезапном выключении.
В памяти все юзеры не поместятся, к сожалению.
DAU не могу раскрыть точнее, но в пике на фичеринге одна железка успешно держала до 2к запросов в секунду при нагрузке около трети процессора
Вы наверняка правы, но лучше начинать с замеров и реального профиля нагрузки, и тогда уже принимать решения :)
Дмитрий, спасибо за вопрос. Если в общем, на примере стратегий, то игрок развивает свою базу, и взаимодействует с другими игроками (например, грабит :)). В первом случае он, отправляя посредством клиента команды на сервер, действует по простому сценарию: загрузили пользователя в кэш (если его там нет), заблокировали, выполнили валидацию, изменили состояние, сбросили в базу, разблокировали, оставив в кэше. С огромной вероятностью следующий запрос от этого игрока найдёт его в кэше. Собственно вся статья о том, как совмещать данный сценарий, с, например, необходимостью вытеснения неактивных игроков (которые не играют или с которыми сейчас не играют). Более сложный сценарий возникает, когда мы хотим кого-нибудь ограбить, здесь уже наступает время использовать шину сообщений, по которой необходимые данные и команды будут доставлены другому игроку, и там вариант простого сценария повторится с некоторыми поправками на работу с сообщениями. Т.е. в одной такой «длинной» транзакции все сущности блокируются поочередно, что позволяет избежать взаимоблокировок. Это схоже с моделью актёров, или системы, подобные mpi. Почему сделали так вместо использования распределённых транзакций — можете посмотреть здесь, в большом докладе «обо всём» www.youtube.com/...
Дмитрий, спасибо за интерес к нашей статье. Вы затронули много вопросов, которые не раскрыты в ней. Если хотите узнать больше, пожалуйста, посмотрите видео доклада, в котором наши подходы раскрываются шире youtu.be/S7fZFpXFWU8. И я буду рад обсудить любые возникшие вопросы здесь, или в linkedin. Спасибо :)
Дабы избежать холиваров, скажу лишь, что выбор .NET был в большей степени обусловлен тем, что те, кто начинали компанию, имели больший опыт в нём, т.е. технологии были более знакомыми. Ну и рынок .NET разработчиков достаточно большой. А так достойные проекты можно делать на огромном множестве технологий и подходов.