-
Надо пересылать файлы с клиентских модулей программы на серверный. Используем для этого ftp. Надо обеспечить бесперебойную работу системы при 3х тысячах клиентов и сотнях файлов по ~2Кб. Причём всё должно работать (не обязательно быстро, но надёжно) в случае если все клиенты приконнектятся одновременно и будут слать данные.
Это реально?
Тесты indy, ics с серверами на indy, ics, file zilla не дают 100% гарантии доставки каждого файла.
То есть операция Put иногда не проходит. И для этого достаточно 2-4х клиентов, постоянно отсылающих файлы.
ics и indy сервера вообще убиваются после определенного времени.
Может фтп для таких задач не очень годится или надо специальное что-то делать или я что-то не понимаю? (ведь фтп сервера в инете как-то работают с тысячами клиентов?)
В идеале я хочу чтоб любой файл мог быть доставлен, если сервер доступен. И ничего при этом не глючило.
-
> Это реально?
реально
> Тесты indy, ics с серверами на indy, ics .... не дают 100% гарантии доставки каждого файла.
Это уже не от них а от программиста зависит.
> ics и indy сервера вообще убиваются после определенного > времени.
они тут не причем
А почему бы не взять один из множества готовых и проверенных временем фтп серверов, нпример под FreeBSD ?
-
> istok (11.12.2007 21:13:00) [0]
Может ты просто не умеешь их готовить? Известны примеры работы ICS с 30000 клиентов одновременно, при том в одном потоке.
-
> Это уже не от них а от программиста зависит.
я делаю банальный 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 клиентов одновременно, > при том в одном потоке.
В этом примере кажется речь шла о месагинге а не о пересылке файлов по фтп с частыми открытиями и закрытиями соединений, не?
-
> я делаю банальный put. делаю его в цикле. что тут можно > поправить? может низя в цикле его юзать, а давать "передохнуть"? >
Код оптимистичен, как Джордж Буш. В цикле без потоков и ассиметричной синхронизации, ты лосося не получишь. 30000 - при грамотном коде мелочь. У тебя даже обработки искоючения не присутствует.
-
> В этом примере кажется речь шла о месагинге а не о пересылке > файлов по фтп с частыми открытиями и закрытиями соединений, > не?
разницы нет
-
> Код оптимистичен, как Джордж Буш. В цикле без потоков и > ассиметричной синхронизации, ты лосося не получишь. 30000 > - при грамотном коде мелочь. У тебя даже обработки искоючения > не присутствует.
Можете грамотной закачки файлов пример показать?
Насчет исключений - а что конкретно имеется в виду? я ж там вроде try except юзаю..
>разницы нет ок, а в чем разница - в том что написал tesseract ?
-
> istok (11.12.07 22:59) [6]
Я чего то не пойму, ты клиента или сервера делаешь? И кто из них у тебя не работает? В [0] речь о серверах, а в [3] вроде как код клиента?
-
> 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?
-
> Нет гарантии что Put сработает удачно
Что говорит сообщение об исключении при неудачном "срабатывании" ?
-
> Сервер на инди, при желании падает хоть от одного клиента, > причём то зависнет, то exception вылетил. по-разному.
Exception-ы они для того и созданы, чтобы их обрабатывать E:EXception многого не скажет. Почитай справку по exception-ам indy.
"Зависнет " на какой высоте над уровнем моря ?
> потом винда выдаёт неизвестную ошибку.
Телепатор поставил стрелку на BufferOverflow. Или утечку памяти.
-
> Нет гарантии что Put сработает удачно
Такой гарантии нет никогда. Поэтому надо заранее предусмотреть возможность при обрыве соединения его восстановления и докачки файла.
> Но с Indy'шным иногда возникают то exception'ы, то просто > зависания,
иногда - это когда? как часто? какие исключения?
> В связи с этими вопросами я дал пример кода. Может что-то > явно не так делаю.
при исключении рекомендуется явно разрывать соединение, еще хорошо бы на сервере выставить разумный таймаут на чтение (т.е. если в течение некоторого времени от клиента нет данных, то соединение закрывать.
-
> Надо обеспечить бесперебойную работу системы при 3х тысячах > клиентов
если делать на инди, то надо использовать не потоки, а волокна (fibers). три тысячи потоков в процессе - довольно круто.
-
> tesseract (12.12.2007 10:41:10) [10]
> Exception-ы они для того и созданы, чтобы их обрабатывать E:EXception многого не скажет. Почитай справку по exception-ам indy.
Инди с тысячами потоков не справится, просто не хватит памяти по стек (1-2 мб на поток).
-
-
> MetalFan (12.12.2007 12:38:14) [14]
асинхронности не требуются потоки, кроме главного.
-
> Anatoly Podgoretsky © (12.12.07 12:54) [15]
В какую сторону копать - асинхронный сервер на ICS ?
В TFtpServer подобных свойств\событий я не видел - от чего надо отталкиваться?
-
> istok (12.12.2007 13:26:16) [16]
С ICS идет огромное количество примеров, на любой вкус. Скажу, что правильно написаный сервер, просто не возможно уронить.
-
> асинхронности не требуются потоки, кроме главного. >
явно - не требует... неявно система наверняка создает доп.потоки
-
я правильно понял, что вот этот кусок демки фтп сервера 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 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;
DelayedSend := TRUE;
end;
end; и по аналогии можно сделать поточную обработку put ? Если "да", то в каком событии - "OnStorSessionConnected" ?
-
> MetalFan (12.12.2007 14:59:18) [18]
Система тоже не создает, это стандартный WinSock работает на сообщениях, а не потоках. Ну не получилось бы создать FTP сервер на 30000 одновременных подключений, только под стек потребуется 30 гб виртуальной памяти.
-
> Ну не получилось бы создать FTP сервер на 30000 одновременных > подключений, только под стек потребуется 30 гб виртуальной > памяти.
Кстати, а под Unix не знаете как реализуется подобное? Там ведь насколько я знаю вообще процесс сервера клонируется особым образом для каждого клиента.
-
> DVM (12.12.2007 15:47:21) [21]
В общем это клонирование, но в Линуксе многое взяли из Виндоус
-
Ситуация такая. Ниже приведен код службы сервера, которая при нагрузочном тестировании быстро падает. Под падением понимается -неожиданный вызов 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);
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
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;
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.
-
P.S.: AddLog я отключил, он ничего не делает.
-
> Anatoly Podgoretsky © (12.12.07 15:29) [20] > > Система тоже не создает, это стандартный WinSock работает > на сообщениях, а не потоках.
Создает. но использует для этого специальные механизмы. как может асинхронность в ОДНОМ потоке работать???
-
лол, стандартная демка от ics под нагрузкой тоже выдаёт "-AnswerToClient > 127.0.0.1 500 PASV exception: 'Access violation at address 7C92BD02 in module 'ntdll.dll'. Read of address 00000001'. -ClientCommand"
просто на разных машинах по-разному ловится.
нагрузка - 3 клиента.
в чем тут может быть косяк?
-
в 17 строке
-
> MetalFan © (12.12.07 18:43) [27] > > в 17 строке
Могу конечно скинуть код примера фтп сервера от ics, только вряд ли в нём будут явные ошибки :)
Что тут можно придумать?
-
> istok (12.12.2007 17:18:23) [23]
Особенность одна, все и везде должно быть обернуто в try except end, что бы любые ошибки не уронили сервер, он должен выживать в самых тяжелых условиях.
-
> istok (12.12.2007 19:04:28) [28]
Примеры не обладают никакой живучестью, они примеры.
-
Anatoly Podgoretsky © (12.12.07 19:12) [29][30]
а, значит я вас изначально не совсем верно понял, подумав что на сервере совсем не будет ошибок.
но такое AV из недр ics это разве нормально? оно не проходит без последствий и после этого только перезапуск процессе\службы помогает. Делать перезапуски службы каждые 10минут это ж бред.. не?
by the way: а насчёт того что Put'ы часто не срабатывали я нашел ответ в малом диапазоне pasv портов сервера.
-
> istok (12.12.2007 19:54:31) [31]
Это AV не из недр ics, а из NTDLL - ты туда передаешь недопустимые параметры.
-
> Это AV не из недр ics, а из NTDLL - ты туда передаешь недопустимые > параметры.
может не я, а ics? я-то тут причём? :)
итого - что надо сделать, чтоб такой косяк пофиксить? править ics?
-
> istok (12.12.2007 20:00:33) [33]
Провести отладку.
-
> В общем это клонирование, но в Линуксе многое взяли из Виндоус
А не наоборот ?
> Кстати, а под Unix не знаете как реализуется подобное? Там > ведь насколько я знаю вообще процесс сервера клонируется > особым образом для каждого клиента.
Функция Fork полностью клонирует процесс, со всеми данными и состоянием. Поэтому там все попроще. Fork вообще крайне полезная функция, чего ее в винду не ввели ?
-
> Функция Fork полностью клонирует процесс, со всеми данными > и состоянием
Ну вот я и спрашиваю, что же это получается для 30000 юзеров будет 30000 копий одного и того же процесса со всеми ресурсами занятыми для каждого процесса отдельно. Что то как то расточительно. Оно вообще работать то будет?
-
> Ну вот я и спрашиваю, что же это получается для 30000 юзеров > будет 30000 копий одного и того же процесса со всеми ресурсами > занятыми для каждого процесса отдельно.
Ну и что ? Работает же, во всех Unix. И не всё копируеться. Занятые файловые дескрипторы тебе же не надо копировать ? да и 30 тысяч юзеров одновременно навряд-ли ломануться на FTP - канал столько твой не выдержит. Даже 1 гигабит/30000 =34 килобита.
-
tesseract © (13.12.07 10:22) [37] к тому же 30000*2 сокетов (команды+данные) т.е. близко к пределу MaxWord, а если в пассиве то ваще 30000*3 (команды+данные_серверн+данные_клиент) сокетов что уже больше MaxWord
-
> 30000 копий одного и того же процесса со всеми ресурсами > занятыми для каждого процесса отдельно.
fork не занимает новых ресурсов, кроме памяти, разве что, он передает процессу копии открытых дескрипторов родительского процесса.
|