скорость обработки клиентов в сервере на джава.

Тема в разделе "WASM.NETWORKS", создана пользователем neutronion_old_school, 13 дек 2017.

  1. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.329
    neutronion_old_school,
    Хоть бы копирайты изменил, сеньор прогромист :)
     
    Dr.Pepper нравится это.
  2. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    Код мой в том смысле, что я затюнил его под свои нужды, большая часть взята путем ресерча на который пришлось потратить 2 месяца. Поэтому, зачем же я чужие копирайты буду менять. Смысл в силе этого сервака. Пока быстрее и эффективней я не обнаружил. Поэтому, хотел бы увидеть, то что быстрее и лучше того что выложено. Но, это сервак моего проекта, да. А тебе рмн Ванга, если будешь флудить, я попрошу модера порезать тебя. Говори по делу, либо молчи, если не въезжаешь.
    НО есть вопросы, где Садко мог бы внести в него интересные идеи. Подождем его пока.
     
  3. superakira

    superakira Guest

    Публикаций:
    0
    возьмите jmeter и замеряйте. получайте в числах нагрузку итд (сколько соединений вывозит итд) и тогда имеет смысл пердметно говорить. желательно сервер под профайлером в этот момент.
    по факту самые забористые решения вообще пишутся под конкретную железку с numa aware штуками и юзермодным тсп стэком на базе dpdk - но это дорого и это не java... так что... jmeter вам в помощь для оценки производительности) что профайлер говорит?
     
  4. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.329
    Типичный Денис Попов. Отключи таким прогромистам интернет и они тебе даже сортировку пузырьком не напишут :)
     
  5. reversecode

    reversecode Guest

    Публикаций:
    0
    neutronion_old_school, может лучше дальше изучать лингвистические языки ?
    программирования это сложная штука, в нём если нет усидчивости и терпеливости самому во всем разобраться - делать нечего
     
    TermoSINteZ нравится это.
  6. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    У вас косяк начинается с того, что вы создаёте воркеров по мере поступления запросов на сервер.
    Количество воркеров у вас не ограничено, сами потоки нигде не регистрируются и не удаляются.
    Нету методов старта и остановки сервера, а вместо этого старт осуществляется в конструкторе.
    Обработка ошибок и ведение лога? Не, не слышал.
    В общем, пилить и пилить ещё, слишком грубый набросок.
     
  7. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    Ответ:
    У вас косяк начинается с того, что вы создаёте воркеров по мере поступления запросов на сервер.
    Количество воркеров у вас не ограничено

    Твоя ошибки:
    Первая:
    Код (Text):
    1.  
    2. ...вы создаете воркеров по мере поступления запросов на сервер.
    3.  
    смотрим внимательно.

    Вторая:
    Код (Text):
    1.  
    2. ...Количество воркеров у вас не ограничено
    3.  
    смотрим внимательно... опять же. :)
    Третья:
    Код (Text):
    1.  
    2. сами потоки нигде не регистрируются и не удаляются.
    3.  
    удаляются по мере окончания работы с клиентом, либо
    по эксепшену.
    На счет регистрации, зачем я должен их регистрировать?


    С этим я соглашусь, но это я знаю, ты же понимаешь это тривиальный код
    Код (Text):
    1.  
    2. Нету методов старта и остановки сервера
    3.  
    Код (Text):
    1.  
    2. Обработка ошибок и ведение лога? Не, не слышал.
    3.  
    возможно, но не критично. Это тестовый сервак.


    По самому главному ты все же в коде на разобрался.
    Воркеры НЕ создаются по мере поступления запроса.
    Вернее не совсем. так. Есть на то причина. Смотри внимательней.

    Количество воркеров ограничено причем 2 органичения для разных
    воркеров.

    Я тебе не подсказываю, хочу чтобы ты сам разобрался.



    ps. неужели я так хорош, что даже великий Садко не разобрался
    в этом прекрасном коде(за исключением конечно логов и запуска сервака
    но это банально, хотя и нужно конечно)
     
  8. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Великому SadKo было лень тратить больше трёх минут на копание этого "гениального" кода.

    Кстати, вот это - самое что ни на есть говно:
    У вас будут слишком часто порождаться и завершаться потоки. Сделали бы хоть таймаут какой, что потоки начинают киляться после того, как пройдёт некоторое время, в течение которого нагрузка на сервер спала. LRU-алгоритм там.
     
  9. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    Извини, ты не разобрался в коде, в месте которое ты указал потоки не создаются.
    Здесь потоки не создаются друг мой.
    public void run() {
    while (!ss.isClosed() || !queue.isEmpty()) {
    try {

    ты не въехал в код.
     
  10. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Всё я понял правильно. Я привёл код выполнение воркера. И ваш воркер вывалится как только очередь станет пустой при условии, что сокет закроется.
    Неравномерная нагрузка на сервер - имеем постоянное создание/удаление потоков там, где у вас accept().
    Не надо думать, что вы меня перехитрили. Вы хоть раз в дебаггере ваш сервер запустите и разные сценарии проиграйте.
     
  11. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    SadKo, покажи то место,(точное) где создается поток. Не выполняется, а создается.
     
  12. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    SadKo, До завтра, если можно, прошу разобраться с кодом. Если не сможешь, то больше вопросов к тебе не имеется. Спасибо за внимание.
     
  13. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    В том коде, что я привёл есть ещё большее говно, на которое вы совсем не обратили внимания. Ну да ладно, не видите - может, со временем поймёте.

    Понимаете, между "не могу" и "не хочу" есть очень существенная разница. Если вы хотите, чтобы в итоге я за вас сервер написал на Java, то платите $100/8-часовой рабочий день. Тогда и поговорим. Мой лимит времени и терпения закончился.
     
  14. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    Да, Садко, я ожидал от тебя большего.
    Вместо того чтобы решать олимпиадные задачи, сначала нужно уделить
    внимание насущному - программированию. Я думал ты одолеешь этот сервер.
    Но ты либо ступил, либо поленился. Для меня это одно и тоже.
    Ты говорил что по моим пунктам которые я указал ранее, что перчень задач,
    которые я указал ранее это для студентов.
    Чтож, твой сервер студенческий однопоточный это точно.
    Сначала я отвечу на твои некоторый претензии:
    я спросил:
    Код (Text):
    1.  
    2. покажи то место,(точное) где создается поток. Не выполняется, а создается.
    3.  
    Ты указал неверо. Видимо ты плохо знаком со стандартными структурами джавы.
    А студентов брать на обучение тебе рано пока. Твой сервер это слова мальчика.
    Мой сервер это слова серьезного человека, и сейчас я буду это доказывать.

    Другие твои слова я пока опустил, потому-что как вижу ты еще не понял как работает
    сервер. Ты неверно указал где возникают потоки.
    Код (Text):
    1.  
    2.   В том коде, что я привёл есть ещё большее говно, на которое вы совсем не обратили внимания. Ну да ладно, не видите - может, со временем поймёте.
    3.  
    Ты научился плохому у местных бандерлогов. Голосование доказало это
    Твои слова:
    Код (Text):
    1.  
    2.   Понимаете, между "не могу" и "не хочу" есть очень существенная разница. Если вы хотите, чтобы в итоге я за вас сервер написал на Java, то платите $100/8-часовой рабочий день. Тогда и поговорим. Мой лимит времени и терпения закончился.
    3.  
    Я не просил тебя творить новый сервак, а наоборот я подарил тебе базу для очень серьезного(по крайней мере лучше мне найти не удалось) сервера. Вместо этого ты уподобился тем обезъянам которые сидят на дереве и кидают шкурки от бананов в то, что они не понимают.
    Это мой ответ тебе.
    В следущем сообщении я начну уже объяснять(защищать) свой код. И возможно там есть
    ошибки, но не юнит тесты, не долгое использование этого пока не показали. Да, соглашусь,
    код требует привести в эстетических порядок, добавить кое-что, убавить. Но как сейчас я его опишу
    от изящен. И да, к сожалению это на 80% не моя заслуга. моя заслуга лишь в том, что я нашел его
    как самый эффективный которые мне пришлось встречать и немного адаптировал его под себя.
    Щас я немного возму паузу, потому-что пишу статью и хочу чтобы она была понятна.
    Наберитесь терпения, сегодня я ее опубликую.
     
  15. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    И да, Синтез, я не понял твоего последнего послания. Ты уберешь мой статус и поставишь какой я просил после статьи? Или я тебя не правильно понял?
    Да и еще, я должен закончить с Садко, потом я конечно покину вас как обещал.
     
  16. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    А я вот от вас большего и не ожидал. Мания величия over 9000.

    И это вы будете говорить человеку, который 15 лет в профессии?

    Да у меня студенты в колледже и то лучше пишут. Ладно они косячат, так как опыта ещё мало. Но вы-то дали понять, что у вас дофига опыта в паттернах.

    Тот код, что вы привели, пишут обычно либо джуниоры, либо стажёры. И чтобы их код попал в продакшн, нужно несколько раз code review проводить, пока правильно не напишут. А это зачастую достаточно внушительная трата времени и сил.

    Вы мой код плохо разобрали и не поняли/не захотели понять, для чего он предназначен. Думаю, обсуждать что-то тут бесполезно. Можно долго биться головой об стену, лучше от этого не станет.

    Я вам указал на конкретное проблемное место. Если вы не способны этого осознать - держите своё мнение при себе.

    Ой, я вас умоляю. Не надо мне пытаться что-либо доказать. Ваш код крив и уныл и находится на уровне джуниора/стажёра.

    Ну правильно, лучше опускать весомые аргументы и придираться к мелочам.
    То, что у вас используется static-поле для счётчика и это очень серьёзный архитектурный просчёт, вы не учли, ОК.

    Голосование только доказало то, что вы сами о себе создали именно то впечатление, которое заслужили.

    Ой, спасибо! А то я восемь лет на Java писал и не мог сервер написать. Вот великий neutronion_old_school снизошёл до нас, недочеловеков, и родил великое поделие.

    В общем, джуниор, возомнивший себя архитектором. ОК.
     
  17. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    Код сервера:
    Начало
    Код (C++):
    1.   public static void main(String[] args) {
    2.   try {
    3.   BlockingQueue<Socket> bq = new ArrayBlockingQueue<>(40);
    4.   Server serv = new Server(bq);
    5.   } catch (IOException ex) {
    6.   }
    7.   }
    Здесь все ясно, но хочу обратить внимание на эту строку.
    BlockingQueue <Socket> bq = new ArrayBlockingQueue<>(40);
    Это ограничение номер 2. Что за ограничение я позже объясню как это работает.
    Далее важный код.
    Код сервера:
    Код (C++):
    1. ///////////////////
    2.   public Server(BlockingQueue<Socket> q) throws IOException {
    3.   ss = new ServerSocket(port);
    4.   noStopRequested = true;
    5.   this.queue = q;
    6.   this.init();
    7.  
    8.   Runnable r = new Runnable() {
    9.   public void run() {
    10.   try {
    11.   runWorkFast();
    12.   } catch (Exception x) {
    13.   netDebugger.fireServerFaultEvent("SERVER: there is exception: ", x);
    14.   x.printStackTrace();
    15.   }
    16.   }
    17.   };
    18.   internalThread = new Thread(r);
    19.   internalThread.setName("My server Thread");
    20.   internalThread.start();
    21.   }
    22. /////////////////////
    Здесь все довольно тривиально, запускается в новом потоке
    функция runWorkFast() Это и есть код сервера принимающяя соединения.
    сохраняем BlockingQueue
    Код (C):
    1. this.queue = q;
    Оно общий для всех потоков и для сервер. Это очень важно!
    Запомните это.

    Далее есть одно интересное место.:
    Код (C):
    1. this.init();
    Это очень важный код.
    Посмотрим что он делает:
    Код (C++):
    1. ///////////////////
    2.   private void init() {
    3.   for (int i = 0; i < INITIALWORKERTHREADS; i++) {
    4.   new Thread(new MultiSessionTask(ss, queue)).start();
    5.   }
    6.   }
    7. //////////////////
    Здесь и создаются потоки(Привет Садко) и количество их не более:
    Код (C++):
    1. static final int INITIALWORKERTHREADS = 20;// tune this!
    Это ограничение номер 1. Он ограничивает количество потоков
    рабочих. Это рабочие потоки, которые постояно находятся
    в памяти. Запомните, РАБОЧИЕ ПОТОКИ.

    Это очень интересная строка

    Код (C):
    1. new Thread(new MultiSessionTask(ss, queue)).start();
    к ней тоже, мы еще вернемся она очень интересна.
    Дальше идет код сервера, который ждет соединений
    с клиентом и получает сокет и сохраняет его
    в BlockingQueue. Как работает BlockingQueue в гугл пожалуйста,
    но общие детали я опишу как он работает в моем серваке.
    Код (C):
    1. ////////////////
    2.  private void runWorkFast() {
    3.   for (;;) {
    4.   try {
    5.   Socket sock = ss.accept();
    6.   int thr = MultiSessionTask.waitCount();
    7.   if (thr > 0) {
    8.   queue.add(sock);
    9.   } else {
    10.   queue.add(sock);
    11.   new Thread(new DynamicMultiSessionTask(ss, queue)).start();
    12.   }
    13.   } catch (IOException exc) {
    14.   exc.printStackTrace();
    15.   }
    16.   if (ss.isClosed()) {
    17.   break; // for (;;)
    18.   }
    19.   } // catch
    20.   }
    21. /////////////////
    код потока сервера останавливается/замерзает здесь:
    Код (C):
    1. Socket sock = ss.accept();
    ...пока нет соединения с клиентом. Как только соединение
    произошло, инстанс сокета сохраняется в BlockingQueue
    Это приводит к последствиям для потоков, которые были созданы.
    Покажу чуть ниже. Пока закончим с кодом сервера.
    Код (C):
    1. if (thr > 0) {
    2.   queue.add(sock);
    3. } else {
    4.   queue.add(sock);
    5.   new Thread(new DynamicMultiSessionTask(ss, queue)).start();
    6. }
    Это код забавный, и немного кажется кривым потому-что
    в экспеременальных целях в части else
    я добавил это:
    Код (C):
    1.  queue.add(sock);
    Без него можно и обойтись. Почему я его добавил догадайтесь сами :)
    Далее игра разума:
    Код (C):
    1. new Thread(new DynamicMultiSessionTask(ss, queue)).start();
    ЭТо создание нового потока дополнительно к тем которые уже были
    созданы (к рабочим). Почему он создается? Потому что те потоки которые мы создали
    в init() уже заняты запросами других клиентов. Потоков больше нет, поэтому
    приходится создавать динамические потоки.

    Вы скажет, ну-у-у. Эдак у нас вся память закончится если милльон клиентов
    ломанутся на твой сервак. И у тебя, дружище, будет жуткий ДОС. Нет не будет.
    Помните я говорил в начале, что мы вернемся к коду main

    BlockingQueue<Socket> bq = new ArrayBlockingQueue<>(40);

    Это и есть ограничение номер 2, который не позволит нам создать потоков
    более 40 вместе с уже существующими 20.
    Напоминаю, что потоки(20) не уничтожаются. Отработав они снова ждут соединений.
    Это я покажу ниже.
    С кодом сервера все. Теперь код потока РАБОЧЕГО:
    Код (C):
    1. ////////////////////
    2.   public void run() {
    3.   while (!ss.isClosed() || !queue.isEmpty()) {
    4.   try {
    5.   waitCount++;
    6.   Socket connection = queue.take();
    7.   waitCount--;
    8.   processSession(connection);
    9.   } catch (InterruptedException e) {
    10.   } catch (Exception ex) {
    11.   ex.printStackTrace();
    12.      }
    13.     }
    14.   }
    15. //////////////////
    Что здесь происходит? Опущу детали, но происходит здесь следующее:
    1) пока сервер жив:
    Код (C):
    1. while (!ss.isClosed() || !queue.isEmpty()) {
    ...держать поток всегда готовым.
    А вот это уже гениальная строка(не моя) которая скорее всего Садко и
    вызвала у тебя затрудения:
    Код (C):
    1. Socket connection = queue.take();
    Это значит, что выполнения потока останавливается здесь тоже покуда нет
    соединения клиента с сервером. Как же это работает?
    А так он разморозиться когда в сервер прийдет клиент и сервер
    получит сокет. Это здесь в коде сервера:
    Код (C):
    1. Socket sock = ss.accept();
    ... и вот в сервере же:
    Код (C):
    1. queue.add(sock);
    Что это значит? Дело в том что это queue это
    BlockingQueue<Socket> bq = new ArrayBlockingQueue<>(40);
    bq это и есть queue и он один на все потоки и сервер.
    Соотвественно когда
    когда сервер добавляет в queue
    queue.add(sock);
    Любой из потоков(рандомно) который как я указывал выше был заморожен
    здесь:
    Код (C):
    1. Socket connection = queue.take();
    размораживается и один из свободных потоков получает коннекшен из queue.
    Замороженные потоки и есть свободные потоки.

    Гениально, не правда?

    Далее поток отработав код для клиента:
    Код (C):
    1. processSession(connection);
    снова в цикле возвращается
    к ожиданию:
    Код (C):
    1. Socket connection = queue.take();
    ежели сервер прекращает свое существование, то при следущей итерации
    код потока тоже умирает:
    Код (C):
    1. public void run() {
    2.   while (!ss.isClosed() || !queue.isEmpty()) {
    3.    ....
    4.   }
    5. }
    ss - сервер закрыт, значит покидаем цикл, а значит
    поток умирает.
    а также если поток ждет здесь
    Код (C):
    1. Socket connection = queue.take();
    и сервер уничтожается, а значит уничтожается и queue
    то поток выпадает в эксепшен и тоже завершается.
    Положа руку на сердце, я это пока не проверял по-моему, а может проверял.

    теперь вернемся к этой строке, но опытные джава программеры
    знают, что это такое
    Код (C):
    1. new Thread(new MultiSessionTask(ss, queue)).start();
    дело в том, что start(); это на самом деле старт
    функции потока
    Код (C):
    1. public void run()
    Такая вот у джава кривизна, можно конечно вызвать и
    run, но не рекомендуют, есть причины. Искать в гугле
    плиз.

    В завершении скажу, код требует некоторого приведения в порядок, но у меня
    подход очень простой. Сначала я делаю самое главное, а далее тюнить можно будет
    уже в следущей итерации рефакторинга(или называйте это как хотите)
    Так как считаю, что с этим справится и monkey-programmer.

    Возникнет вопрос, а что же у тебя динамические тоже будут прибаляться
    и в итогое ты будешь иметь максимальное количество
    потоков в памяти а это
    BlockingQueue<Socket> bq = new ArrayBlockingQueue<>(40);
    40! Скажут, что же ты косячья морда, 40 потоков у тебя
    будут висеть даже если клиентов не будет. Ах, ты школлоло...
    Момент!
    Дело в том, что
    DynamicMultiSessionTask и MultiSessionTask отличаются

    DynamicMultiSessionTask я не привел ранее, но
    от MultiSessionTask разница гиганская, хотя еле заметная
    Код (C):
    1.  
    2. /////////////////////
    3.  public void run() {
    4.    while (!ss.isClosed() || !queue.isEmpty()) {
    5.     try {
    6.      //Socket connection = queue.take();
    7.      Socket connection = queue.poll(120, TimeUnit.SECONDS);
    8.      if(connection==null)
    9.       break;
    10.      processSession(connection);
    11.     } catch (InterruptedException e) {
    12.  
    13.     } catch(Exception ex){
    14.  
    15.     }
    16.    }
    17.  
    18.    System.out.println("DYNAMIC. WE OUT OF QUE: " + queue.size());
    19.   }
    20. ///////////////////////
    Вот оно:
    Код (C++):
    1. Socket connection = queue.poll(120, TimeUnit.SECONDS);
    Что это значит? А это значит, что если клиентуры не будет на серваке, то DynamicMultiSessionTask все уйдут в небытие по прошествии таймаута и не будут занимать место в памяти. И останутся только 20 потоков РАБОЧИХ! Правда изящно?

    Вот и все, собственно. проверить можете юнит тестом. Если, что-то есть лучше, то код в студию пожалуйста, мне самому интересно посмотреть код, который будет еще лучше.
     
  18. neutronion_old_school

    neutronion_old_school Попугай Сильвера

    Публикаций:
    0
    Регистрация:
    23 июл 2017
    Сообщения:
    411
    за форматирование извините, какой тег тут чтобы формат кода нормальный был?
    Я не осилил.
     
  19. rmn

    rmn Well-Known Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2004
    Сообщения:
    2.329
    Да ты просто Дональд Кнут нашего времени :)

    язвительный_комментарий.txt :)
     
  20. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Опытные java-разработчики знают, чем Thread.start() от Thread.run() отличаются.
    Вы, видимо, не знаете, раз говорите, что "такая вот у джава кривизна". Этого вполне мне хватило, чтобы оценить ваш уровень понимания многопоточного программирования.
    С JDBC также ранее потерпели полный фейл.
    Не, вы однозначно джуниор, а, может быть, даже и ниже.