Комментарии 29
>А вот производительность и надежность Tomcat, проверенного годами и тысячами разработчиков, не вызывает вопросов.
Т.е. netty, которому тоже не мало лет и на котором построено очень много продуктов, не вызывает у вас доверия?
И еще, посмотрите на protobuf в качестве протокола. Очень удобная штука.
P.S. Ваша цифра 0.09 мсек на запрос. Что именно вы измеряли? Т.е. время между чем и чем?
Т.е. netty, которому тоже не мало лет и на котором построено очень много продуктов, не вызывает у вас доверия?
И еще, посмотрите на protobuf в качестве протокола. Очень удобная штука.
P.S. Ваша цифра 0.09 мсек на запрос. Что именно вы измеряли? Т.е. время между чем и чем?
+1
Спасибо, что прочитали:)
Нет, что вы, производительность Netty никаких вопросов не вызывает абсолютно. Я думаю, что ее писали хорошие программисты и она очень хорошо оптимизирована. Имелось ввиду, что мое решение несет «некий» оверхед + многопоточная работа, которую нужно довести до ума и хорошенько отладить. Все это сыграло не в пользу моего «велосипеда».
>И еще, посмотрите на protobuf в качестве протокола. Очень удобная штука.
Смотрел, интересная вещь. Изначально планировал взять JSON для реализации буфера, но он мне показался слишком «жирным» для тестов. Мб, решись я доводить систему до ума, я бы реализовал JSON или protobuf. Когда понадобится использовать ее для чата или других «узких» для http мест, я интегрирую туда какой-нибудь готовый протокол.
>P.S. Ваша цифра 0.09 мсек на запрос. Что именно вы измеряли? Т.е. время между чем и чем?
Банально мерялся пинг. Т.е. измерения проводились на клиенте с момента отправки данных и до получения первой порции.
Нет, что вы, производительность Netty никаких вопросов не вызывает абсолютно. Я думаю, что ее писали хорошие программисты и она очень хорошо оптимизирована. Имелось ввиду, что мое решение несет «некий» оверхед + многопоточная работа, которую нужно довести до ума и хорошенько отладить. Все это сыграло не в пользу моего «велосипеда».
>И еще, посмотрите на protobuf в качестве протокола. Очень удобная штука.
Смотрел, интересная вещь. Изначально планировал взять JSON для реализации буфера, но он мне показался слишком «жирным» для тестов. Мб, решись я доводить систему до ума, я бы реализовал JSON или protobuf. Когда понадобится использовать ее для чата или других «узких» для http мест, я интегрирую туда какой-нибудь готовый протокол.
>P.S. Ваша цифра 0.09 мсек на запрос. Что именно вы измеряли? Т.е. время между чем и чем?
Банально мерялся пинг. Т.е. измерения проводились на клиенте с момента отправки данных и до получения первой порции.
+1
Просто тема интересна.
У меня, тут же, есть статья, пересекающаяся с вашей по теме и содержанию.
Ну и думаю тут нет оверхеда, ибо время на его реализацию очень мало и сравнимо с реализацией без него. Просто правильные технологии и хорошая архитектура дают многие преимущества. А так же позволяют не наступить на многие грабли.
Пинг я не измерял. Привык измерять производительность запросами/сек.
На Core i5, сервер на netty из моей статьи переваривает порядка 300000 запросов/сек. И это с сериализацией тасков protobuf.
P.S. Но после того как я увлекся Scala и переписал ядро на ней, получил >800000 запросов/сек, кода раза в 3 меньше и другие полезные и нужные ништяки.
У меня, тут же, есть статья, пересекающаяся с вашей по теме и содержанию.
Ну и думаю тут нет оверхеда, ибо время на его реализацию очень мало и сравнимо с реализацией без него. Просто правильные технологии и хорошая архитектура дают многие преимущества. А так же позволяют не наступить на многие грабли.
Пинг я не измерял. Привык измерять производительность запросами/сек.
На Core i5, сервер на netty из моей статьи переваривает порядка 300000 запросов/сек. И это с сериализацией тасков protobuf.
P.S. Но после того как я увлекся Scala и переписал ядро на ней, получил >800000 запросов/сек, кода раза в 3 меньше и другие полезные и нужные ништяки.
+1
Я читал вашу статью, но уже после того, как разобрался с Netty. В частности, у вас увидел организацию Decoder через чек-поинты и сделал также, ибо удобнее.
>Ну и думаю тут нет оверхеда, ибо время на его реализацию очень мало и сравнимо с реализацией без него. Просто правильные технологии и >хорошая архитектура дают многие преимущества. А так же позволяют не наступить на многие грабли.
Да, можно согласится, но многопоточность- штука сложная и требует идеальной реализации, и было бы не очень приятно, если, внезапно, в проект пойдут пользователи и на нагрузке начнут лезть баги, которых не было на синтетических тестах.
Насчет архитектуры согласен, грамотно проектировать- всегда важно. Но я пока что только учусь.
>На Core i5, сервер на netty из моей статьи переваривает порядка 300000 запросов/сек. И это с сериализацией тасков protobuf.
protobuf, кстати, оказывается есть для AS3.0, я этого не знал.
Это очень серьезная цифра:) Запросы легковесные?
Я так понимаю, что парсинг что protobuf, что JSON работает через reflection, поэтому это не очень быстро.
Ваш тест, скорее, показывает пределы нагрузки. В идеале, нужно мерять пинг именно на больших нагрузках, когда загружены все логические ядра процессора и все серьезно.
А как вы тестируете? Мб мне тоже попробовать написать подобный тест. Я читал пару статей про jMetter. Или вы просто стандартными средствами тестили?
>P.S. Но после того как я увлекся Scala и переписал ядро на ней, получил >800000 запросов/сек, кода раза в 3 меньше и другие полезные и >нужные ништяки.
Scala- это интересно и круто, но я пока не готов к таким «преждевременным» оптимизациям. Ибо сейчас у меня нет проекта, в котором огромный поток пользователей и требуется срочно решать проблему с нагрузками на сервер.
Да и я пилю «типичную» браузерную игру, поэтому, у меня на таких запросах быстрее ляжет база данных, чем сервер с логикой.
>Ну и думаю тут нет оверхеда, ибо время на его реализацию очень мало и сравнимо с реализацией без него. Просто правильные технологии и >хорошая архитектура дают многие преимущества. А так же позволяют не наступить на многие грабли.
Да, можно согласится, но многопоточность- штука сложная и требует идеальной реализации, и было бы не очень приятно, если, внезапно, в проект пойдут пользователи и на нагрузке начнут лезть баги, которых не было на синтетических тестах.
Насчет архитектуры согласен, грамотно проектировать- всегда важно. Но я пока что только учусь.
>На Core i5, сервер на netty из моей статьи переваривает порядка 300000 запросов/сек. И это с сериализацией тасков protobuf.
protobuf, кстати, оказывается есть для AS3.0, я этого не знал.
Это очень серьезная цифра:) Запросы легковесные?
Я так понимаю, что парсинг что protobuf, что JSON работает через reflection, поэтому это не очень быстро.
Ваш тест, скорее, показывает пределы нагрузки. В идеале, нужно мерять пинг именно на больших нагрузках, когда загружены все логические ядра процессора и все серьезно.
А как вы тестируете? Мб мне тоже попробовать написать подобный тест. Я читал пару статей про jMetter. Или вы просто стандартными средствами тестили?
>P.S. Но после того как я увлекся Scala и переписал ядро на ней, получил >800000 запросов/сек, кода раза в 3 меньше и другие полезные и >нужные ништяки.
Scala- это интересно и круто, но я пока не готов к таким «преждевременным» оптимизациям. Ибо сейчас у меня нет проекта, в котором огромный поток пользователей и требуется срочно решать проблему с нагрузками на сервер.
Да и я пилю «типичную» браузерную игру, поэтому, у меня на таких запросах быстрее ляжет база данных, чем сервер с логикой.
0
Да, я написал для себя стандартный тест, чтобы измерять одинаковую величину на разных реализациях и таким образом не сравнивать ящики с бутылками.
Он простой, какое-то количество потоков которые непрерывно отсылают стандартный запрос логина в игру, состоящий из двух полей, имя и пароль.
Если загружены все логические ядра, то это уже ахтунг полный, такого никогда не должно быть.
И да, БД одно из самых узких мест в системе, но ведь игровой запрос!=запрос в базу, иначе даже для небольшого количества запросов в сек надо будет серьезное железо ставить.
Ну и Scala это не оптимизация, это очень удобный язык для реализации своих идей. Просто ее система акторов позволила сделать очень простой и быстрый многопоточный код.
Он простой, какое-то количество потоков которые непрерывно отсылают стандартный запрос логина в игру, состоящий из двух полей, имя и пароль.
Если загружены все логические ядра, то это уже ахтунг полный, такого никогда не должно быть.
И да, БД одно из самых узких мест в системе, но ведь игровой запрос!=запрос в базу, иначе даже для небольшого количества запросов в сек надо будет серьезное железо ставить.
Ну и Scala это не оптимизация, это очень удобный язык для реализации своих идей. Просто ее система акторов позволила сделать очень простой и быстрый многопоточный код.
+1
>Он простой, какое-то количество потоков которые непрерывно отсылают стандартный запрос логина в игру, состоящий из двух? полей, имя и пароль.
Попробую написать на досуге
>Если загружены все логические ядра, то это уже ахтунг полный, такого никогда не должно быть.
Подразумевалось, что ядра используются максимально эффективно и ничего не простаивает. Глупо было бы на 6-ядерном i7 работать в 4 потока, когда доступно 12.
>И да, БД одно из самых узких мест в системе, но ведь игровой запрос!=запрос в базу, иначе даже для небольшого
>количества запросов в сек надо будет серьезное железо ставить.
Для всевозможных ферм- увы. Для работы с БД сейчас используется Hibernate. У него есть кеш, конечно, но все равно вся логика завязана на внешней базе. В идеале, хотелось бы перейти на inMemory базу, но пока большого смысла не вижу.
>Ну и Scala это не оптимизация, это очень удобный язык для реализации своих идей.
Я знаю, что это язык :) Я имел ввиду, что изучать ее в данный момент для меня только для того, чтобы система работала еще быстрее- нет смысла, потому что сейчас нет никакой нагрузки. Логичнее сначала сделать прототип и посмотреть, реально ли с моей идеей набрать нагрузку в принципе. Может, никто не оценит)
Попробую написать на досуге
>Если загружены все логические ядра, то это уже ахтунг полный, такого никогда не должно быть.
Подразумевалось, что ядра используются максимально эффективно и ничего не простаивает. Глупо было бы на 6-ядерном i7 работать в 4 потока, когда доступно 12.
>И да, БД одно из самых узких мест в системе, но ведь игровой запрос!=запрос в базу, иначе даже для небольшого
>количества запросов в сек надо будет серьезное железо ставить.
Для всевозможных ферм- увы. Для работы с БД сейчас используется Hibernate. У него есть кеш, конечно, но все равно вся логика завязана на внешней базе. В идеале, хотелось бы перейти на inMemory базу, но пока большого смысла не вижу.
>Ну и Scala это не оптимизация, это очень удобный язык для реализации своих идей.
Я знаю, что это язык :) Я имел ввиду, что изучать ее в данный момент для меня только для того, чтобы система работала еще быстрее- нет смысла, потому что сейчас нет никакой нагрузки. Логичнее сначала сделать прототип и посмотреть, реально ли с моей идеей набрать нагрузку в принципе. Может, никто не оценит)
+1
Для всевозможных ферм- увы. Для работы с БД сейчас используется Hibernate. У него есть кеш, конечно, но все равно вся логика завязана на внешней базе. В идеале, хотелось бы перейти на inMemory базу, но пока большого смысла не вижу.
Когда занимался фермочками и другими социально ориентированными играми, почему-то работа с базой заключалась лишь в двух событиях:
1. достать все при логине
2. сохранить все, после логаута.
И хибернейт… брр.
Когда занимался фермочками и другими социально ориентированными играми, почему-то работа с базой заключалась лишь в двух событиях:
1. достать все при логине
2. сохранить все, после логаута.
И хибернейт… брр.
+1
Игра изначально не социалка, как фермочка- сравнение не совсем корректное, я имел ввиду механику игры(кликер по сути, но со своими элементами). Хотя клиент легко портировать под соц сети, если понадобиться.
А вы использовали готовое решение для хранение данных или писали свое?
А гибернейт- он работает, вроде бы даже быстро и стабильно:)
P.S. Меня давно не покидает идея inMemory хранилища, но, я думаю, мой скилл недостаточно высок для самостоятельной реализации таких вещей.
А вы использовали готовое решение для хранение данных или писали свое?
А гибернейт- он работает, вроде бы даже быстро и стабильно:)
P.S. Меня давно не покидает идея inMemory хранилища, но, я думаю, мой скилл недостаточно высок для самостоятельной реализации таких вещей.
+1
Мы использовали и хибернейт, и ручкамки писали голый sql через jdbc, и даже монгу. Последняя для всего этого, мне кажется, подходит идеально.
+1
Монга- эт интересно, да. Но и хибернейта, вроде бы, пока хватает. А вот писать SQL ручками- ну не знаю, пока нет конкретных узких мест, не вижу смысла. Да и гибернейт поддерживает запросы на чистом SQL, насколько я знаю…
+1
Вот одного не пойму, зачем тут
SocketSession
. Этож совершенно бредовая вещь по сути своей.+1
Чтобы можно было написать примерно такое:
Вот вам и простой чатик. В терминологии Tomcat для WebSocket мои SocketSession- это Connection, кажется.
public class Chat extends Servlet
{
@Override
public void doRequest(InputBuffer input, OutputBuffer output, SocketSession session)
{
String message= input.getPar("message");
OutputBuffer out= new OutputBuffer();
out.setPar("message", message);
for (SocketSession ses: SocketServletContainer.getInstance().getListSession())
{
ses.send(out, "Chat");
}
output.setPar("state", "succsess");
}
}
Вот вам и простой чатик. В терминологии Tomcat для WebSocket мои SocketSession- это Connection, кажется.
+1
Я немного про другое, я про то, что внутри этого класса: а именно это невероятно странное ограничение, которое сделано невероятно странным образом.
+1
Вы про выдачу id-ниов? Я ж там написал, что константа в 20000, в идеале, должна подбираться под конкретное железо. Лучше ее вообще вынести в конфиг, просто я этого не сделал.
Просто все id должны быть уникальными, чтобы, например, при логине можно было привязать id сессии к игроку и послать сообщение конкретному игроку(при реализации private-чатов, например).
Более простой системы выдачи уникальных id как-то не пришло в голову. Можно, конечно, складывать все, что уже выдано, в list, но тогда при выдаче придется делать поиск по List для проверки уникальности, в общем, мое решение мне показалось более изящным. Мб я что-то упускаю.
Просто все id должны быть уникальными, чтобы, например, при логине можно было привязать id сессии к игроку и послать сообщение конкретному игроку(при реализации private-чатов, например).
Более простой системы выдачи уникальных id как-то не пришло в голову. Можно, конечно, складывать все, что уже выдано, в list, но тогда при выдаче придется делать поиск по List для проверки уникальности, в общем, мое решение мне показалось более изящным. Мб я что-то упускаю.
+1
У нас, в глуби кода есть такое
public class LocalIdentifierSequencer implements IdentifierSequencer {
private final AtomicInteger integer;
public LocalIdentifierSequencer(int start) {
this.integer = new AtomicInteger(start);
}
public LocalIdentifierSequencer() {
this(1);
}
@Override
public int identifier() {
return integer.getAndIncrement();
}
}
+1
Изначально так и было, но потом посетила мысль, что возможно банальное переполнение при активном подключении\отключении клиентов. Допустим, клиент с id=1 подключен и висит достаточно долго(реальный игрок). Тут злоумышленник начинается подключаться\отключаться от сервера, в итоге происходит переполнение int-а и мы снова имеем значение 1.
Мб проблема притянута за уши, но все же. Я именно поэтому выбрал способ проверки ключей через массив.
Мб проблема притянута за уши, но все же. Я именно поэтому выбрал способ проверки ключей через массив.
+1
>> Более простой системы выдачи уникальных id как-то не пришло в голову.
Channel.getId() — готовый уникальный идентификатор.
>>synchronized(channel)
зачем ??
Нафига для bossExecutor использовать OrderedMemoryAwareThreadPoolExecutor?
Channel.getId() — готовый уникальный идентификатор.
>>synchronized(channel)
зачем ??
Нафига для bossExecutor использовать OrderedMemoryAwareThreadPoolExecutor?
0
>Channel.getId() — готовый уникальный идентификатор.
А ларчик просто открывался, действительно)
>зачем ??
Дабы чего не вышло. Я как-то упустил момент, является ли Channel thread-safe реализацией или нет. Если нет, то возможно одновременное чтение и запись с нескольких потоков.
>Нафига для bossExecutor использовать OrderedMemoryAwareThreadPoolExecutor?
Пишут, что они самые эффективные по тестам. Своих не проводил, поверил так.
А ларчик просто открывался, действительно)
>зачем ??
Дабы чего не вышло. Я как-то упустил момент, является ли Channel thread-safe реализацией или нет. Если нет, то возможно одновременное чтение и запись с нескольких потоков.
>Нафига для bossExecutor использовать OrderedMemoryAwareThreadPoolExecutor?
Пишут, что они самые эффективные по тестам. Своих не проводил, поверил так.
0
Советую вам посмотреть как выглядит
OrderedMemoryAwareThreadPoolExecutor
изнутри.0
Каналы — полностью thread safe.
OrderedMemoryAware — нужен не для эффективности, а для того чтобы соблюдался порядок выполнения событий в канале + защита от ООМ.
Для boss, нафиг не нужен, да и для worker тоже. Лучше создать отдельный executionHandler и запихать его в конец pipeline, если вас заботит очередность обработки событий в канале и есть какая-то блокирующая работа + можно выкинуть ваш QueueHandler.
connectTimeoutMillis — не имеет смысла для серверных каналов.
OrderedMemoryAware — нужен не для эффективности, а для того чтобы соблюдался порядок выполнения событий в канале + защита от ООМ.
Для boss, нафиг не нужен, да и для worker тоже. Лучше создать отдельный executionHandler и запихать его в конец pipeline, если вас заботит очередность обработки событий в канале и есть какая-то блокирующая работа + можно выкинуть ваш QueueHandler.
connectTimeoutMillis — не имеет смысла для серверных каналов.
0
Тогда соглашусь, что synchronized(){} там не нужен.
QueueHandler сделал для того, чтобы не заботиться о логике, реализованной в сервлете и чтобы разделить работу с сетью и обработку логики. Чтобы это были разные threadPool. Концептуально, так сказать.
По названию догадывался о назначении OrderedMemoryAwareThreadPoolExecutor, но изнутри устройство не изучал. Его выбрал, собственно, потому что пару раз мелькал в приведенных по Netty примерах.
QueueHandler сделал для того, чтобы не заботиться о логике, реализованной в сервлете и чтобы разделить работу с сетью и обработку логики. Чтобы это были разные threadPool. Концептуально, так сказать.
По названию догадывался о назначении OrderedMemoryAwareThreadPoolExecutor, но изнутри устройство не изучал. Его выбрал, собственно, потому что пару раз мелькал в приведенных по Netty примерах.
0
Годная статья.
Просто напомню что если использовать Jetty с вебсокетами то код будет гораздо легче.
Тут осторожней, либо подавайте utf8 в new и getBytes, либо джаву запускайте с -Dfile.encoding=UTF8.
Просто напомню что если использовать Jetty с вебсокетами то код будет гораздо легче.
new String(bytes)
Тут осторожней, либо подавайте utf8 в new и getBytes, либо джаву запускайте с -Dfile.encoding=UTF8.
0
Ну дык в преамбуле написано, что это- велосипед. Иногда хочется поисследовать и написать что-нибудь свое.
Вебсокеты прекрасно себя чувствуют и в Tomcat, насколько я знаю.
Правда у вебсокетов, насколько я понимаю, тройное рукопожатие сделано по http-протоколу. Как реализовать это, например, на флеш- непонятно. Видимо, придется вручную составлять заголовок и слать http-запрос. Или я не прав?
>Тут осторожней, либо подавайте utf8 в new и getBytes, либо джаву запускайте с -Dfile.encoding=UTF8.
Там все UTF-8, разумеется.
Вебсокеты прекрасно себя чувствуют и в Tomcat, насколько я знаю.
Правда у вебсокетов, насколько я понимаю, тройное рукопожатие сделано по http-протоколу. Как реализовать это, например, на флеш- непонятно. Видимо, придется вручную составлять заголовок и слать http-запрос. Или я не прав?
>Тут осторожней, либо подавайте utf8 в new и getBytes, либо джаву запускайте с -Dfile.encoding=UTF8.
Там все UTF-8, разумеется.
0
> Как реализовать это, например, на флеш- непонятно
Гугл сразу выдал github.com/y8/websocket-as
> Там все UTF-8, разумеется.
Так где конкретно он прописан? А то ведь попадётся OS с недефолтным UTF-8.
Гугл сразу выдал github.com/y8/websocket-as
> Там все UTF-8, разумеется.
Так где конкретно он прописан? А то ведь попадётся OS с недефолтным UTF-8.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Контейнер серверного java-кода с поддержкой постоянного соединения