Конференция "Сети" » стабильный и мощный FTP - реально? [D7]
 
  • istok (11.12.07 21:13) [0]
    Надо пересылать файлы с клиентских модулей программы на серверный. Используем для этого ftp. Надо обеспечить бесперебойную работу системы при 3х тысячах клиентов и сотнях файлов по ~2Кб. Причём всё должно работать (не обязательно быстро, но надёжно) в случае если все клиенты приконнектятся одновременно и будут слать данные.

    Это реально?

    Тесты indy, ics с серверами на indy, ics, file zilla не дают 100% гарантии доставки каждого файла.

    То есть операция Put иногда не проходит. И для этого достаточно 2-4х клиентов, постоянно отсылающих файлы.

    ics и indy сервера вообще убиваются после определенного времени.

    Может фтп для таких задач не очень годится или надо специальное что-то делать или я что-то не понимаю? (ведь фтп сервера в инете как-то работают с тысячами клиентов?)

    В идеале я хочу чтоб любой файл мог быть доставлен, если сервер доступен. И ничего при этом не глючило.
  • DVM © (11.12.07 22:07) [1]

    > Это реально?

    реально


    > Тесты indy, ics с серверами на indy, ics .... не дают 100% гарантии доставки каждого файла.

    Это уже не от них а от программиста зависит.


    > ics и indy сервера вообще убиваются после определенного
    > времени.

    они тут не причем

    А почему бы не взять один из множества готовых и проверенных временем фтп серверов, нпример под FreeBSD ?
  • Anatoly Podgoretsky © (11.12.07 22:09) [2]
    > istok  (11.12.2007 21:13:00)  [0]

    Может ты просто не умеешь их готовить?
    Известны примеры работы ICS  с 30000 клиентов одновременно, при том в одном потоке.
  • istok (11.12.07 22:22) [3]

    > Это уже не от них а от программиста зависит.


    я делаю банальный put. делаю его в цикле. что тут можно поправить? может низя в цикле его юзать, а давать "передохнуть"?

    function TForm1.UploadFileIndy(AFileName: string): Boolean;
    var
     i: Integer;
     sent: Boolean;
    begin
     Result := False;
     sent := False;
     if FileExists(AFileName) then
     with IdFTP1 do
     begin

       AddLog('>....');      

       try
         Put(AFileName, ExtractFileName(AFileName));
         sent := True;
       except
         on e: exception do
           AddLog('Put exception: ' + e.Message, True);
       end;

       AddLog('....<');
       if not sent then
         AddLog('not sent ', True);
     end;

     Result := sent;
    end;




    > Известны примеры работы ICS  с 30000 клиентов одновременно,
    >  при том в одном потоке.


    В этом примере кажется речь шла о месагинге а не о пересылке файлов по фтп с частыми открытиями и закрытиями соединений, не?
  • tesseract © (11.12.07 22:25) [4]

    > я делаю банальный put. делаю его в цикле. что тут можно
    > поправить? может низя в цикле его юзать, а давать "передохнуть"?
    >


    Код оптимистичен, как Джордж Буш. В цикле без потоков и ассиметричной синхронизации, ты лосося не получишь. 30000 - при грамотном коде мелочь. У тебя даже обработки искоючения не присутствует.
  • DVM © (11.12.07 22:28) [5]

    > В этом примере кажется речь шла о месагинге а не о пересылке
    > файлов по фтп с частыми открытиями и закрытиями соединений,
    >  не?

    разницы нет
  • istok (11.12.07 22:59) [6]

    > Код оптимистичен, как Джордж Буш. В цикле без потоков и
    > ассиметричной синхронизации, ты лосося не получишь. 30000
    > - при грамотном коде мелочь. У тебя даже обработки искоючения
    > не присутствует.


    Можете грамотной закачки файлов пример показать?

    Насчет исключений - а что конкретно имеется в виду? я ж там вроде try except юзаю..

    >разницы нет
    ок, а в чем разница - в том что написал tesseract  ?
  • DVM © (11.12.07 23:03) [7]

    > istok   (11.12.07 22:59) [6]

    Я чего то не пойму, ты клиента или сервера делаешь? И кто из них у тебя не работает? В [0] речь о серверах, а в [3] вроде как код клиента?
  • istok (12.12.07 00:21) [8]

    > DVM ©   (11.12.07 23:03) [7]


    Замечение правомерное, ща объясню.

    Я делаю и сервера и клиента.

    Проблемы клиента:
    1.Нет гарантии что Put сработает удачно (когда я его вызываю в цикле и есть еще пара клиентов) - вне зависимости от сервере (будь то мой или не мой сервер). Я понимаю что результат Put обрабатываем и всё такое, меня смущает сам факт того, что если 4 клиента не могут стабильно аплоадить файлы, то что уж говорить о тысячах.

    2.ICS клиент работает медленнее чем Indy'шный. Где-то в 10 раз. Но с Indy'шным иногда возникают то exception'ы, то просто зависания, а ics клиент работает стабильно. Наверное дело в том, что я криво юзаю инди клиента.

    В связи с этими вопросами я дал пример кода. Может что-то явно не так делаю.

    Проблемы сервера:
    3.Стабильность. С 50ю клиентами мой ICS сервер живёт от пары часов до пары недель и потом винда выдаёт неизвестную ошибку. Как и что ловить пока не ясно. Сервер довольно простой, ничего особенного, простые обработчики событий.

    Сервер на инди, при желании падает хоть от одного клиента, причём то зависнет, то exception вылетил. по-разному. В сервере опять же ничего особенного, пара обработчиков событий и всё.

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

    Но то что есть сейчас - упадёт от сотни клиентов без проблем.

    Резюме: я не ищу решений конкретных проблем своими силами, косяков много,  а компетенция в этой области никакая. я хочу комплексное решение, которое гарантирует мне вышеобозначенные цели с минимумом моего кода. Желательно чтоб оно было платное и с сапортом.

    К примеру, меня смущает что есть удобные, мощные 'ready-ro-use' средства для работы с данными, но я не вижу аналогичных вещей для обмена данными по сети.

    В идеале хотелось бы использовать что-то опробованное на тысячах клиентов и готовое к употреблению :)

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

    Any concerns?
  • Сергей М. © (12.12.07 08:20) [9]

    > Нет гарантии что Put сработает удачно


    Что говорит сообщение об исключении при неудачном "срабатывании" ?
  • tesseract © (12.12.07 10:41) [10]

    > Сервер на инди, при желании падает хоть от одного клиента,
    >  причём то зависнет, то exception вылетил. по-разному.


    Exception-ы они для того и созданы, чтобы их обрабатывать E:EXception  многого не скажет. Почитай справку по exception-ам indy.

    "Зависнет " на какой высоте над уровнем моря ?


    > потом винда выдаёт неизвестную ошибку.


    Телепатор поставил стрелку на BufferOverflow. Или утечку памяти.
  • umbra © (12.12.07 10:45) [11]

    > Нет гарантии что Put сработает удачно

    Такой гарантии нет никогда. Поэтому надо заранее предусмотреть возможность при обрыве соединения его восстановления и докачки файла.


    > Но с Indy'шным иногда возникают то exception'ы, то просто
    > зависания,

    иногда - это когда? как часто? какие исключения?


    > В связи с этими вопросами я дал пример кода. Может что-то
    > явно не так делаю.

    при исключении рекомендуется явно разрывать соединение, еще хорошо бы на сервере выставить разумный таймаут на чтение (т.е. если в течение некоторого времени от клиента нет данных, то соединение закрывать.
  • umbra © (12.12.07 11:08) [12]

    > Надо обеспечить бесперебойную работу системы при 3х тысячах
    > клиентов

    если делать на инди, то надо использовать не потоки, а волокна (fibers). три тысячи потоков в процессе - довольно круто.
  • Anatoly Podgoretsky © (12.12.07 12:06) [13]
    > tesseract  (12.12.2007 10:41:10)  [10]

    > Exception-ы они для того и созданы, чтобы их обрабатывать E:EXception  многого не скажет. Почитай справку по exception-ам indy.

    Инди с тысячами потоков не справится, просто не хватит памяти по стек (1-2 мб на поток).
  • MetalFan © (12.12.07 12:38) [14]

    > Известны примеры работы ICS  с 30000 клиентов одновременно,
    >  при том в одном потоке.

    разве ICS не использует асинхронность? тогда это уже не один поток.

    может почитать про порты завершения?
    http://www.gamedev.ru/community/mmorpg/articles/?id=6
  • Anatoly Podgoretsky © (12.12.07 12:54) [15]
    > MetalFan  (12.12.2007 12:38:14)  [14]

    асинхронности не требуются потоки, кроме главного.
  • istok (12.12.07 13:26) [16]

    > Anatoly Podgoretsky ©   (12.12.07 12:54) [15]


    В какую сторону копать - асинхронный сервер на ICS ?

    В TFtpServer подобных свойств\событий я не видел - от чего надо отталкиваться?
  • Anatoly Podgoretsky © (12.12.07 13:39) [17]
    > istok  (12.12.2007 13:26:16)  [16]

    С ICS идет огромное количество примеров, на любой вкус.
    Скажу, что правильно написаный сервер, просто не возможно уронить.
  • MetalFan © (12.12.07 14:59) [18]

    > асинхронности не требуются потоки, кроме главного.
    >

    явно - не требует... неявно система наверняка создает доп.потоки
  • istok (12.12.07 15:13) [19]
    я правильно понял, что вот этот кусок демки фтп сервера ics - поточная обработка get?:

    procedure TFtpServerForm.FtpServer1GetProcessing(
       Sender          : TObject;
       Client          : TFtpCtrlSocket;
       var DelayedSend : Boolean);
    var
       MyServer : TFtpServer;
       MyClient : TMyClient;
    begin                              InfoMemo.Lines.Add('-GetProcessing');
       MyServer := Sender as TFtpServer;
       MyClient := Client as TMyClient;
       { If client request a *.ZZZ file, then start a thread to do some      }
       { processing (here the thread just sleep 10 sec to show other clients }
       { are not blocked.                                                    }
       if UpperCase(ExtractFileExt(MyClient.FileName)) = '.ZZZ' then begin
           MyClient.FWorkerThread := TGetProcessingThread.Create(TRUE);
           MyClient.FWorkerThread.Server          := MyServer;
           MyClient.FWorkerThread.Client          := MyClient;
           MyClient.FWorkerThread.FreeOnTerminate := TRUE;
           MyClient.FWorkerThread.OnTerminate     := WorkerThreadTerminated;
           MyClient.FWorkerThread.Resume;
           { Ask server component to not start sending immediately           }
           { We will ask to start sending from WorkerThreadTerminated event  }
           DelayedSend := TRUE;
       end;
    end;



    и по аналогии можно сделать поточную обработку put ? Если "да", то в каком событии - "OnStorSessionConnected" ?
  • Anatoly Podgoretsky © (12.12.07 15:29) [20]
    > MetalFan  (12.12.2007 14:59:18)  [18]

    Система тоже не создает, это стандартный WinSock работает на сообщениях, а не потоках.
    Ну не получилось бы создать FTP сервер на 30000 одновременных подключений, только под стек потребуется 30 гб виртуальной памяти.
  • DVM © (12.12.07 15:47) [21]

    > Ну не получилось бы создать FTP сервер на 30000 одновременных
    > подключений, только под стек потребуется 30 гб виртуальной
    > памяти.

    Кстати, а под Unix не знаете как реализуется подобное? Там ведь насколько я знаю вообще процесс сервера клонируется особым образом для каждого клиента.
  • Anatoly Podgoretsky © (12.12.07 16:12) [22]
    > DVM  (12.12.2007 15:47:21)  [21]

    В общем это клонирование, но в Линуксе многое взяли из Виндоус
  • istok (12.12.07 17:18) [23]
    Ситуация такая. Ниже приведен код службы сервера, которая при нагрузочном тестировании быстро падает.

    Под падением понимается
    -неожиданный вызов TFtpServer.OnStop
    -запись "Ошибка приложения weServerDataTransfer.exe, версия 2.1.0.340, модуль ntdll.dll, версия 5.2.3790.3959, адрес 0x0002a754. Дополнительные сведения можно найти в центре справки и поддержки, в "http://go.microsoft.com/fwlink/events.asp"." в журнале ошибок

    Когда ровно также тестирую аналогичную демку ics ftp server'a от автора - она не падает.

    Может есть какие-то особенности при реализации ics сервера в службе?
    Или данный код кривой?

    procedure TweServerDataTransferService.FtpServer1Authenticate(Sender: TObject;
     Client: TFtpCtrlSocket; UserName, Password: TFtpString;
     var Authenticated: Boolean);
    begin
     Authenticated := (UserName = 'admin') and (PassWord = 'we_pass');

     if Authenticated then
     begin
       AddLog('User authenticated: ' + Client.GetPeerAddr + ', User ''' + UserName);

       //Client.HomeDir := FweFolders.dirServerLogs;
       //ForceDirectories(Client.HomeDir);
     end
     else
       AddLog('User NOT authenticated: ' + Client.GetPeerAddr + ', User ''' + UserName);
    end;

    procedure TweServerDataTransferService.FtpServer1ClientConnect(Sender: TObject;
     Client: TFtpCtrlSocket; AError: Word);
    begin
     AddLog('! ' + Client.GetPeerAddr + ' connected');

     if Error <> 0 then
     begin
       Client.Close;
       AddLog('quick close: FtpServer1ClientConnect');
     end;
    end;

    procedure TweServerDataTransferService.FtpServer1ClientDisconnect(Sender: TObject;
     Client: TFtpCtrlSocket; AError: Word);
    begin
     AddLog('! ' + Client.GetPeerAddr + ' disconnected');
    end;

    procedure TweServerDataTransferService.FtpServer1RetrDataSent(Sender: TObject;
     Client: TFtpCtrlSocket; Data: TWSocket; AError: Word);
    begin
       if Error <> 0 then
           AddLog('! ' + Client.GetPeerAddr
           + ', ['+ Client.UserName + '], ' +
                              ' Data sent. Error #' + IntToStr(Error));

       if Error <> 0 then
       begin
         Client.Close;
         AddLog('quick close: FtpServer1RetrDataSent');
       end;
    end;

    procedure TweServerDataTransferService.FtpServer1RetrSessionClosed(Sender: TObject;
     Client: TFtpCtrlSocket; Data: TWSocket; AError: Word);
    begin
     if (AError = 0) then
     begin
       if Client.AllSent then
         //FilesSent := FilesSent + 1
       else
         Addlog('FtpServer1RetrSessionClosed: ALL_SENT=FALSE '+Client.GetPeerAddr  + ', ['+ Client.UserName + ']');
     end
     else
       AddLog('FtpServer1RetrSessionClosed: '+ ' ['+ Client.UserName + '], ' +'error = ' + IntToStr(AError));

       if AError <> 0 then
       begin
         Client.Close;
         AddLog('quick close: FtpServer1RetrSessionClosed');
       end;
    end;

    procedure TweServerDataTransferService.FtpServer1RetrSessionConnected(Sender: TObject;
     Client: TFtpCtrlSocket; Data: TWSocket; AError: Word);
    begin
       if Error <> 0 then
           AddLog('! ' + Client.GetPeerAddr
           + ', ['+ Client.UserName + '], ' +
                              ' Data session connected. Error #' + IntToStr(Error)
                              + ' AllSent = ' + BoolToStr(Client.AllSent, True));

       if (Error = 0) and Client.AllSent then
         Addlog('All Sent - ok');

       if Error <> 0 then
       begin
         Client.Close;
         AddLog('quick close: FtpServer1RetrSessionConnected; error = ' + IntToStr(Error));
       end;
    end;

    procedure TweServerDataTransferService.FtpServer1Start(Sender: TObject);
    begin
     AddLog('! Server started');
    end;

    procedure TweServerDataTransferService.FtpServer1Stop(Sender: TObject);
    begin
     AddLog('! Ftp Server stopped', False);
     if not FServiceStoppedManually then
     begin
       AddLog('not FServiceStoppedManually, Restart', False);
       Sleep(5000); //
       HardRestart;
       //Restart;
     end;
    end;

    procedure TweServerDataTransferService.FtpServer1StorSessionClosed(Sender: TObject;
     Client: TFtpCtrlSocket; Data: TWSocket; AError: Word);
    begin
     if AError = 0 then
       AddLog('File Received - ok')
     else
       AddLog('FtpServer1StorSessionClosed: error = ' + IntToStr(AError));
    end;

    procedure TweServerDataTransferService.FtpServer1ValidateGet(Sender: TObject;
     Client: TFtpCtrlSocket; var FilePath: TFtpString; var Allowed: Boolean);
    begin
     Allowed := False;

     if (Pos(fnClientOptions2, FilePath) > 0) then
     begin
       AddLog('ClientOptions requested, path = ' + FweFolders.dirServerOptions + fnClientOptions2);
       FilePath := FweFolders.dirServerClientOptions + LowerCase(ExtractFileName(FilePath));
       Allowed := FileExists(FilePath);
     end;
    end;

    procedure TweServerDataTransferService.ServiceStart(Sender: TService; var Started: Boolean);
    begin
                   
       FtpServer1.Port := IntToStr(portServerClientsDT);
       FtpServer1.PasvPortRangeStart := portServerClientsDT2;
       FtpServer1.PasvPortRangeSize := portServerClientsDT2Size;
       
       FtpServer1.Start;  

    end;

    procedure TweServerDataTransferService.FtpServer1ValidateSize(Sender: TObject;
     Client: TFtpCtrlSocket; var FilePath: TFtpString; var Allowed: Boolean);
    begin
     AddLog('FtpServer1ValidateSize - enter');
     AddLog('FilePath = ' + FilePath);

     Allowed := False;

     if (Pos(fnClientOptions2, FilePath) > 0) then
     begin
       Allowed := FileExists(FweFolders.dirServerClientOptions + ExtractFileName(FilePath));
     end;
    end;

    procedure TweServerDataTransferService.FtpServer1ValidatePut(Sender: TObject;
     Client: TFtpCtrlSocket; var FilePath: TFtpString; var Allowed: Boolean);
    begin
     Allowed := False;

     if (Pos(prefPackage, FilePath) > 0) then
     begin
       Allowed := True;
       FilePath := FweFolders.dirServerLogs + ExtractFileName(FilePath);
     end;

    end;

    end.

  • istok (12.12.07 17:19) [24]
    P.S.:  AddLog я отключил, он ничего не делает.
  • MetalFan © (12.12.07 17:37) [25]

    > Anatoly Podgoretsky ©   (12.12.07 15:29) [20]
    >
    > Система тоже не создает, это стандартный WinSock работает
    > на сообщениях, а не потоках.

    Создает. но использует для этого специальные механизмы. как может асинхронность в ОДНОМ потоке работать???
  • istok (12.12.07 18:06) [26]
    лол, стандартная демка от ics под нагрузкой тоже выдаёт "-AnswerToClient
    > 127.0.0.1 500 PASV exception: 'Access violation at address 7C92BD02 in module 'ntdll.dll'. Read of address 00000001'.
    -ClientCommand"

    просто на разных машинах по-разному ловится.

    нагрузка - 3 клиента.

    в чем тут может быть косяк?
  • MetalFan © (12.12.07 18:43) [27]
    в 17 строке
  • istok (12.12.07 19:04) [28]

    > MetalFan ©   (12.12.07 18:43) [27]
    >
    > в 17 строке


    Могу конечно скинуть код примера фтп сервера от ics, только вряд ли в нём будут явные ошибки :)

    Что тут можно придумать?
  • Anatoly Podgoretsky © (12.12.07 19:12) [29]
    > istok  (12.12.2007 17:18:23)  [23]

    Особенность одна, все и везде должно быть обернуто в try except end, что бы любые ошибки не уронили сервер, он должен выживать в самых тяжелых условиях.
  • Anatoly Podgoretsky © (12.12.07 19:13) [30]
    > istok  (12.12.2007 19:04:28)  [28]

    Примеры не обладают никакой живучестью, они примеры.
  • istok (12.12.07 19:54) [31]
    Anatoly Podgoretsky ©   (12.12.07 19:12) [29][30]

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

    но такое AV из недр ics это разве нормально? оно не проходит без последствий и после этого только перезапуск процессе\службы помогает. Делать перезапуски службы каждые 10минут это ж бред.. не?

    by the way:
    а насчёт того что Put'ы часто не срабатывали я нашел ответ в малом диапазоне pasv портов сервера.
  • Anatoly Podgoretsky © (12.12.07 19:55) [32]
    > istok  (12.12.2007 19:54:31)  [31]

    Это AV не из недр ics, а из NTDLL - ты туда передаешь недопустимые параметры.
  • istok (12.12.07 20:00) [33]

    > Это AV не из недр ics, а из NTDLL - ты туда передаешь недопустимые
    > параметры.


    может не я, а ics? я-то тут причём? :)

    итого - что надо сделать, чтоб такой косяк пофиксить? править ics?
  • Anatoly Podgoretsky © (12.12.07 20:02) [34]
    > istok  (12.12.2007 20:00:33)  [33]

    Провести отладку.
  • tesseract © (12.12.07 22:55) [35]

    > В общем это клонирование, но в Линуксе многое взяли из Виндоус


    А не наоборот ?


    > Кстати, а под Unix не знаете как реализуется подобное? Там
    > ведь насколько я знаю вообще процесс сервера клонируется
    > особым образом для каждого клиента.


    Функция Fork полностью клонирует процесс, со всеми данными и состоянием. Поэтому там все попроще. Fork вообще крайне полезная функция, чего ее в винду не ввели ?
  • DVM © (12.12.07 23:00) [36]

    > Функция Fork полностью клонирует процесс, со всеми данными
    > и состоянием

    Ну вот я и спрашиваю, что же это получается для 30000 юзеров будет 30000 копий одного и того же процесса со всеми ресурсами занятыми для каждого процесса отдельно. Что то как то расточительно. Оно вообще работать то будет?
  • tesseract © (13.12.07 10:22) [37]

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


    Ну и что ? Работает же, во всех Unix. И не всё копируеться. Занятые файловые дескрипторы тебе же не надо копировать ? да и 30 тысяч юзеров одновременно навряд-ли ломануться на FTP - канал столько твой не выдержит. Даже 1 гигабит/30000 =34 килобита.
  • Slym © (13.12.07 11:56) [38]
    tesseract ©   (13.12.07 10:22) [37]
    к тому же 30000*2 сокетов (команды+данные) т.е. близко к пределу MaxWord, а если в пассиве то ваще 30000*3 (команды+данные_серверн+данные_клиент) сокетов что уже больше MaxWord
  • umbra © (13.12.07 15:51) [39]

    > 30000 копий одного и того же процесса со всеми ресурсами
    > занятыми для каждого процесса отдельно.

    fork не занимает новых ресурсов, кроме памяти, разве что, он передает процессу копии открытых дескрипторов родительского процесса.
 
Конференция "Сети" » стабильный и мощный FTP - реально? [D7]
Есть новые Нет новых   [134431   +10][b:0][p:0.006]