Конференция "Сети" » Использование TWSocketThrdServer из набора ICS
 
  • Eraser © (05.02.16 15:11) [0]
    Добрый день, коллеги.

    Есть ли среди вас кто-нибудь, кто активно использовал данный компонент для работы хотя бы с десятками соединений?

    Нужда заставила перевести часть средне-загруженного функционала на асинхронную библиотеку. АП, царство ему небесное, очень хвалил ICS.

    Документации практически ноль, примеры устаревшие/кривые и однопоточные.

    Возможно посоветуете другую асинхронную библиотеку? IOCP не предлагать, уже используется, но в указанном участке не подходит по ряду причин.
  • Eraser © (05.02.16 15:13) [1]
    О проблеме - столкнулся с тем, что без помещения всего кода OnDataAvailable и других событий в критическую секцию соединения отваливаются по не понятным причинам.
  • Eraser © (05.02.16 17:14) [2]
    Еще одно уточнение, в принципе, начинает более-менее работать, если добавить в критическую секцию Client.Receive в обработчике OnDataAvailable. Т.е. непосредственно, считывание данных из TCP стека. на ~1000 хорошо загруженных соединениях это, imho, приведет к серьезной деградации.
  • NoUser © (05.02.16 19:17) [3]
    >Eraser ©   (05.02.16 15:11)  [0]
    Поделитесь, пожалуйста, опытом - почему IOCP не предлагать?

    > Eraser ©   (05.02.16 17:14) [2]
    А сколько потоков работают  с этим 1к хорошо загруженных соединений?
  • Eraser © (05.02.16 20:17) [4]

    > NoUser ©   (05.02.16 19:17) [3]


    > Поделитесь, пожалуйста, опытом - почему IOCP не предлагать?

    в той реализации, что у нас имеется он не подходит для большого количества real-time соединений, но зато подходит для огромного количества слабо нагруженных.
    переделывать сейчас нет времени.


    > А сколько потоков работают  с этим 1к хорошо загруженных
    > соединений?

    на ICS планируется от 10 соединений на поток, а там тесты покажут. 10 соединений на поток уже приемлемо. сам проект, если сильно упростить - прокси сервер, работающий в режиме реального времени, т.е. задержки пакетов должны быть минимальны.
    с этим хорошо справлялось Indy, но на большом количестве потоков и нашей архитектуре оно начинает сыпаться через сутки примерно, и там дело не в ошибках в нашем коде.

    в принципе, на данном этапе, удалось заставить работать ICS без деадлоков и ошибок, но нормального тестирования пока не было. а код ICS меня лично очень настораживает, особенно в том, что связанно с многопоточностью. один только список клиентов-сокетов, не защищенный вообще ничем чего только стоит.
  • Rouse_ © (05.02.16 21:08) [5]
    Не ленись - напиши свое, я в свое время много всего перепробовал (и инди и ICS и еще какую-то дрянь - уже не помню названия) в итоге свой вариант накатал.
    Недели три убил (в основном на отладку и на поддержку SSL) но оно потом себя окупило. Больше нервов на правке сторонних ошибок убьешь.
  • NoUser © (05.02.16 21:11) [6]
    Хм, если это а-ля прокси, то основная ваша работа - копирование данных,
    но когда оно будет в крит-секции то работать будет, по факту, только один поток.

    Но, теперь вопрос о 'real-time', думаю, что обеспечить своевременное перекладывание данных, даже на скорости 10Gbit, сможет и один поток.
  • Eraser © (05.02.16 21:52) [7]

    > Rouse_ ©   (05.02.16 21:08) [5]
    > Не ленись - напиши свое, я в свое время много всего перепробовал
    > (и инди и ICS и еще какую-то дрянь - уже не помню названия)
    > в итоге свой вариант накатал.
    > Недели три убил (в основном на отладку и на поддержку SSL)
    > но оно потом себя окупило. Больше нервов на правке сторонних
    > ошибок убьешь.

    ну я, в свое время, написал для IOCP на основе компонента, автор которого с этого форума даже вроде. IOCP хорош для обработки огромного количества соединений, сотни тысяч держать можно. но думаю и для real time им же и закончится, только работу с буферами нужно оптимизировать )


    > NoUser ©   (05.02.16 21:11) [6]


    > Хм, если это а-ля прокси, то основная ваша работа - копирование
    > данных,
    > но когда оно будет в крит-секции то работать будет, по факту,
    >  только один поток.

    да вроде бы удалось без крит. секции заставить работать. завтра покажет пригодность в боевых условиях. если будет сыпаться, то придется IOCP допиливать.
  • Rouse_ © (06.02.16 02:39) [8]
    Сотня тыщ? Номер порта как будет выглятеть: 999999? :)
  • Rouse_ © (06.02.16 02:46) [9]
    Я свой когда писал ориетировался на нагрузку порядка 8-10 тыщ одновременных коннектов, и то перестарался ибо на практике получилось избыточно, у нас в районе 15 тысяч в сутки на сервера лицензирования идет запросов. Но штоб за сотню тысяч: у тебя их всего 65535 одновременных - лимит (по портам) :)
  • DVM © (06.02.16 11:21) [10]

    > Eraser ©   (05.02.16 21:52) [7]


    > IOCP хорош для обработки огромного количества соединений,
    >  сотни тысяч держать можно.

    Держать и реально обслуживать - это разные вещи. Сотни тысяч на одном компьютере обслуживать это малореально. Дело даже не в количестве портов, дело в пропускной способности. Если гипотетически представить, что на сервере имеется 40 Gbit адаптер (я даже не знаю, на какой площадке можно подключить сервер на такой скорости), то получается мы имеем порядка 5000 Мбайт / сек, делим на 100 000 получаем 0,05 мегабайта/сек, т.е 50 кбайт/сек. Думаю, мало кого устроит такой расклад по нынешним временам. Кроме этого, переварить 40 Gbit/сек с какой-либо более менее сложной логикой еще надо постараться, даже на очень можном сервере.
    Для большого количества соединений нужны распределенные системы с большим количеством недорогих узлов.
  • DVM © (06.02.16 11:24) [11]

    > Возможно посоветуете другую асинхронную библиотеку?

    Boost.ASIO
  • Eraser © (06.02.16 14:41) [12]

    > Rouse_ ©   (06.02.16 02:46) [9]


    >  Но штоб за сотню тысяч: у тебя их всего 65535 одновременных
    > - лимит (по портам) :)

    на одного удаленного клиента, либо общее число портов, которые можно прослушать, но не максимальное количество соединений ;)


    > DVM ©   (06.02.16 11:21) [10]
    >
    > > Eraser ©   (05.02.16 21:52) [7]
    >
    >
    > > IOCP хорош для обработки огромного количества соединений,
    >
    > >  сотни тысяч держать можно.
    >
    > Держать и реально обслуживать - это разные вещи. Сотни тысяч
    > на одном компьютере обслуживать это малореально.

    50 кб/с это избыточно, напомню, речь и мало нагруженном сервере, там обмен сообщениями крайне скуден.
    но речь не об этом, это уже все работает лет 5.

    речь про real-time асинхронный сервер. казалось бы, не бином Ньютона, а нормальных компонентов в открытом доступе и нет. потому и создал ветку, мало ли кто что подскажет.
  • NoUser © (06.02.16 15:57) [13]
    > DVM ©   (06.02.16 11:21) [10]
    постоянных 0,4 Mbit это очень даже неплохо, а для M2M так больше и не нужно.

    > Eraser ©   (06.02.16 14:41) [12]
    Еще раз, что такое в данном случаи 'real-time'?

    Относительно нормальных компонент не знаю, а своими могу поделиться.
  • Eraser © (06.02.16 16:17) [14]

    > NoUser ©   (06.02.16 15:57) [13]


    > Еще раз, что такое в данном случаи 'real-time'?

    минимальная задержка данных на стороне сервера. т.е. отсутствие как-либо накопительных буферов и т.д. при поступлении данных на сервер от одного клиента их нужно как можно быстрее отправить другому, чем меньше задержки, тем лучше.


    > Относительно нормальных компонент не знаю, а своими могу
    > поделиться.

    был бы очень благодарен.
  • DVM © (06.02.16 16:42) [15]

    > Eraser ©

    Наверное ты вот это видел, но приведу ссылку на всяк случай https://github.com/bamlan/delphi-iocp-framework

    я его проверял, оно быстро работает.
  • Eraser © (06.02.16 16:54) [16]

    > DVM ©   (06.02.16 16:42) [15]

    спасибо, посмотрю. не видел, т.к. когда нужно было разбираться с IOCP этого фреймворка еще точно не было.
  • Rouse_ © (06.02.16 19:31) [17]
    Когда устанавливается соединение под тебя открывается отдельный порт (т.е. при первом соединении занято два порта - слушающий и тот на который перенаправили только что подключившегося клиента) лимит у тебя 65535
  • DVM © (06.02.16 21:27) [18]

    > Rouse_ ©   (06.02.16 19:31) [17]

    Никто не мешает прописать несколько IP адресов на интерфейсе или сделать несколько интерфейсов и слушать их все и поставить например балансировщик перед сервером. Т.о ограничение на количество портов не существенно. Другое дело ресурсы сервера физические.
  • Eraser © (07.02.16 01:35) [19]

    > DVM ©   (06.02.16 21:27) [18]

    даже не обязательно несколько интерфейсов или балансировщик.


    > Rouse_ ©   (06.02.16 19:31) [17]

    проще всего пояснить скриншотом )
    https://dl.dropboxusercontent.com/u/26403307/2016-02-07_1-15-11.png

    Как известно, любое TCP соединение идентифицируется 4 параметрами:
    local IP
    locap Port
    remote IP
    remote Port

    Допустим, мы слушаем на сервере 999 порт, как в приведенном скриншоте.
    Соответственно локальный порт у нас 999, интерфейс, допустим, 127.0.0.1 или какой-либо другой IP адрес. Т.е. занят всего один порт, на количество соединений с разных IP адресов ограничений нет.

    Грубо говоря, ограничение такое - с одного IP адреса A на другой IP адрес Б нельзя установить более 65535 подключений.
  • Nouser © (07.02.16 03:02) [20]
    > Eraser ©   (06.02.16 16:17) [14]
    Отправил на мыло эхо-демо.
  • Eraser © (07.02.16 12:49) [21]

    > Nouser ©   (07.02.16 03:02) [20]

    получил, спасибо!
  • NoUser © (10.02.16 02:24) [22]
    > чем меньше задержки, тем лучше.

    Открыл Рихтера - увидел
    SetFileCompletionNotificationModes

    ,
    прикрутил - таки да, можно несколько сэкономить на переключениях потоков.

    Еще есть
    SetFileIoOverlappedRange

    , видимо для того чтобы дать возможность драйверу работать напрямую (без лишнего копирования) с пользовательским буфером. Пока не крутил, но заманчиво.
  • Eraser © (10.02.16 19:15) [23]

    > Nouser ©   (07.02.16 03:02) [20]
    > > Eraser ©   (06.02.16 16:17) [14]
    > Отправил на мыло эхо-демо.

    видимо вещь очень узко заточенная под конкретную задачу, в целом, много полезного, думаю применение найдется.


    > DVM ©   (06.02.16 16:42) [15]


    > Наверное ты вот это видел, но приведу ссылку на всяк случай
    > https://github.com/bamlan/delphi-iocp-framework
    >
    > я его проверял, оно быстро работает.

    по началу обрадовался, прикрутить удалось на место ICS буквально за час. Работает быстро, все то что надо. НО всплыла проблема, с которой я сталкивался и раньше. Здесь она не решена вообще никак. На хорошо загруженном сервер (около 500 нагруженных соединений, причем они коннектятся и отваливаются десятками в секунду, так надо) начинает происходить простая и очень удручающая вещь. А именно - не на каждый WSASend приходит соответствующее событие завершения в порт. В итоге получаем серьезную утечку ресурсов. Буквально за 5-10 минут работы легко может съесть 1 Гб памяти. Причем сам фреймворк отображает где именно. Да и провел простой эксперимент, посчитав количество WSASend и его завершений - все именно так.

    Еще больше удручает то, что в сети нет упоминаний о данной проблеме. Код вызова WSASend и обработка завершения в компоненте выглядят корректно, да и видно, что фреймоворк не школьник писал и для работы. Вот и думаю, то ли лыжи не едут, то ли я..
  • Eraser © (10.02.16 23:08) [24]

    > А именно - не на каждый WSASend приходит соответствующее
    > событие завершения в порт.

    дело, конечно же, оказалось не в IOCP, а во фреймворке, автор не до конца освобождал блоки память.
  • DVM © (12.02.16 21:34) [25]

    > Eraser ©   (10.02.16 23:08) [24]


    > автор не до конца освобождал блоки память.

    так может ты напишешь где он не освобождал? или еще лучше автору написать.
  • Eraser © (13.02.16 21:35) [26]

    > DVM ©   (12.02.16 21:34) [25]

    в procedure TIocpIoThread.Execute; модуля Iocp.TcpSocket

    есть такой участок:
         if not IocpStatusOk then
         begin
           if (PerIoData = nil) and (Connection = nil) then
           begin
             LastErr := GetLastError;
             //if (LastErr <> ERROR_ABANDONED_WAIT_0) then
               AppendLog('%s IoThread %d ERROR %d=%s', [FOwner.ClassName, ThreadID, LastErr, SysErrorMessage(LastErr)], ltError);
             Break;
           end;

           if (PerIoData <> nil) then
           begin
             if PerIoData.Operation = iotWrite then
             begin
               FOwner.IoCachePool.FreeMemory(PerIoData.Buffer.DataBuf.Buf);
             end;

             FOwner.FreeIoData(PerIoData);
           end;

           if (Connection <> nil) then
           begin
             Connection.Release; // 对应PostRead/PostWrite中的AddRef
             Connection.Disconnect; // 对应连接创建时的FRefCount := 1
           end;

           Continue;
         end;


    собственно, вот этой строки не хватало


             if PerIoData.Operation = iotWrite then
             begin
               FOwner.IoCachePool.FreeMemory(PerIoData.Buffer.DataBuf.Buf);
             end;

    т.к. в MSDN есть такой текст

    If *lpOverlapped is not NULL and the function dequeues a completion packet for a failed I/O operation from the completion port, the function stores information about the failed operation in the variables pointed to by lpNumberOfBytes, lpCompletionKey, and lpOverlapped. To get extended error information, call GetLastError.

    https://msdn.microsoft.com/ru-ru/library/windows/desktop/aa364986(v=vs.85).aspx

    Собственно, дальше PerIoData освобождалась, а вот PerIoData.Buffer.DataBuf.Buf - нет, данные повисали в воздухе, точнее в пулле, помеченные как занятые.

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

    вообще удивительно, почему нет ни одного приличного и полностью отлаженного инструмента. во всех таких проектах главная проблема - переоптимизация, авторы боятся потратить лишний мегабайт памяти или лишний такт процессора, в итоге, страдает архитектура, причем сильно. а что памяти пустой полно, что процессор не загружен, только AV сыпаться, а так ничего )
  • Eraser © (13.02.16 21:46) [27]
    кстати, пришлось еще добавить в пулл объектов еще одну очередь удаленных объектов, из которой они уже окончательно освобождаются строго, после указанного интервала. Некий примитивный GC. Того подхода, который был изначально не хватало, приводило к AV.

    Вообще беда таких проектов - отсутствие инфраструктуры для отладки. Естественно на 10 или 100 соединениях все будет работать нормально, а вот под нагрузкой мельчайшие недоработки рано или поздно вылезают, классика. Приходится заниматься отладкой на боевом сервере, к сожалению.
 
Конференция "Сети" » Использование TWSocketThrdServer из набора ICS
Есть новые Нет новых   [118684   +12][b:0][p:0.001]