Перейти к содержанию
Fire Monkey от А до Я

Камышев Александр

Пользователи
  • Постов

    235
  • Зарегистрирован

  • Посещение

  • Победитель дней

    9

Весь контент Камышев Александр

  1. При запросе оборудование сообщает серверу свои параметры, сервер записывает их в базу, всего три записи в три таблицы, два целых числа и одна строка плюс ключевые поля. Selectы сервер делает периодически, через 5 минут, обновление кэша. Практически идет только запись. На пакет http ответ уходит сразу, без подтверждения записи в бд. Если ждать подтверждение скорость резко падает, и больше 600-800 http запросов служба затыкается, краш где-то получается. Всего удалось получить больше 3000 тысяч запросов т.е. 3000 * 3 = 9к в секунду, движок СУБД при этом занимает 10-15% загрузки. MySql с максимальными настройками, измерял время потраченное на три запроса - поднималось в пике до 50мс. Десять коннектов держали по 300 таких пакетов запросов, т.е. среднее время должно было быть ~3 мс на запрос. Как-то не сходится... буду дальше копать
  2. В продолжение темы. С асинхронным режимом amAsync в FD все плохо, нет колбэков - полбеды, на 1200-1500 крашится все сразу, на нескольких сотнях запросов умирает медленно и мучительно. Завершение работы через раз с крашем. Режим amNonBlocking ненамного лучше, те же яйца только в профиль, годится только для клиентского интерфейса работы с БД. Перемудрили что-то разработчики там с потоками. Остался режим amBlocking + TTrhead. Класс для простоты общения со средом: //--------------------------------------------------------------------------- class TMyDBConnThread : public TThread { private: TInterlocked *Interlocked; TNotifyEvent FOnExecute; TNotifyEvent FOnReady; void __fastcall SyncReady(){ FOnReady(this); } protected: void __fastcall Execute(); public: __fastcall TMyDBConnThread(bool CreateSuspended); __property TNotifyEvent OnExecute = { read = FOnExecute, write = FOnExecute }; __property TNotifyEvent OnReady = { read = FOnReady, write = FOnReady }; TFDConnection *connection; TFDQuery *query; TSimpleEvent *event; int buzy; }; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- void __fastcall TMyDBConnThread::Execute() { Interlocked = new TInterlocked; while ( !Terminated ) { event->WaitFor( INFINITE ); if ( Terminated ) break; Interlocked->Exchange( buzy, 1 ); FOnExecute(this); Interlocked->Exchange( buzy, 0 ); event->ResetEvent(); Synchronize( SyncReady ); } delete Interlocked; delete event; } //--------------------------------------------------------------------------- Вся работа с бд в FOnExecute и потокозащищенный колбэк FOnReady. Далее удивление: устойчивая работа, отсутствие ошибок, 9000+ запросов в БД в секунду и при этом 20-30% загрузки, в асинхронном режиме для сравнения 1500 запросов под 60%. Все зависит от степени кривизны рук, однако...
  3. это и есть трехзвенка, код из сервера приложений , про болезнь - тогда понятно, думал может я чего путаю. спс
  4. Windows, TFDConnection + TFDQuery, асинхронный режим запросов, 10 TFDQuery создаются динамически fconnection = new TFDConnection(NULL); fquery = new TFDQuery(NULL); fquery->Connection = fconnection; fquery->AfterOpen = AfterOpen; fquery->AfterExecute = AfterExecute; fquery->OnError = QueryError; Всего около 500 запросов в секунду, т.е. по 50 на каждый, после каждого запроса по AfterOpen и AfterExecute сразу следующий запрос из очереди. В случае QueryError - разрыв соединения и через паузу повтор. При тестировании была выявлена деградация количества записей в секунду, вплоть до полной остановки. Локализация показала, что при высокой нагрузке TFDQuery иногда не возвращает callback менее чем совсем, ни AfterExecute ни QueryError, в общем "друг, оставь покурить, а в ответ - тишина". При этом у TFDConnection State = csConnected и у TFDQuery State = dsInactive. Проблема была решена костылем с таймаутом, если за секунду нет ответа - уход в ошибку с разрывом соединения и повтором. Теперь вопрос, может я чего-то не знаю? где callback?
  5. Бывает Про директивы не знал, теперь буду в курсе, так изящнее, спасибо, но увы и ах: The memory allocation directives are meaningful only in a program. They should not be used in a library or a unit. т.е. в System.Classes.pas и в IdThread.pas вставлять нельзя, ибо should not be used in a library or a unit. Indy сама создает потоки в коде библиотеки IdCustomTCPServer.pas. и еще {$M 16384, 65535} - полагаю размеры указаны неверно, от 64К до 1М, возможно я ошибаюсь, вроде: гранулярность выделения адресного пространства равна 64 Кб, сдругой стороны из описания Default {$M 16384,1048576} В общем, все таки брутальный подход, только хардкор...
  6. результаты краш-теста - упал в "Недостаточно памяти" после 5770 запросов в секунду, как бы и ничего так С обращением к пулу - 3к и без задержек на доступ к критическим секциям 5к запросов с задержкой 200 мсек. - непринужденно, конечно после допила системного TThread, беда кстати была не в инди, а в теплом ламповом borland. тестировалось на ПК разработчика, сервер и эмулятор на localhost:
  7. Не думаю что сколько нибудь заметно, если брать http пакеты, ведь TIdHTTPServer наследник от TIdCustomTCPServer. Если пересылать бинарные массивы, без парсинга хедеров - возможно и будет небольшое увеличение. Был приятно удивлен, по всем ядрам ровная нагрузка.
  8. думаю эти два куска показывают основную обработку OnCommandGet, из хедера: typedef std::deque< strDBQueueMember* > db_queue_deque; db_queue_deque db_queue; TCriticalSection *cs_pool, *cs_queue, *cs_files; TInterlocked *Interlocked; 3600 это запрос без обращения к бд, только данные из пула с критическими секциями ну и вот это обязательно в System.Classes.pas в Embarcadero\Studio\17.0\source\rtl\common\ constructor TThread.Create(CreateSuspended: Boolean); -//- {$IF Defined(MSWINDOWS)} //#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 - это для информации //#define CREATE_SUSPENDED 0x00000004 - это для информации // заменить FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID); // на FHandle := BeginThread(nil, 65536, @ThreadProc, Pointer(Self), $00010004, FThreadID); System.Classes.pas добавить в проект. и будет вам счастье
  9. //--------------------------------------------------------------------------- void TdmSkyUpdate::PostCommand( strCommandData *cd ) { bool is_client = false; if ( !cd->req->AuthExists ) { cd->sx_auth = cd->req->RawHeaders->Values["X-SX-auth"]; if ( cd->sx_auth.IsEmpty() ) { cd->resp->ResponseNo = 401; return; } is_client = true; } if ( !is_client ) { strDeviceCmd dc; dc.cd = cd; cs_pool->Enter(); PostDevice( &dc ); cs_pool->Leave(); if ( !dc.db_queue.size() ) return; if ( !PushToDBQueue( &dc.db_queue ) ) { cd->resp->ResponseNo = 500; return; } } else { strClientCmd cc; cc.cd = cd; cs_pool->Enter(); PostClient( &cc ); cs_pool->Leave(); if ( !cc.db_queue.size() ) return; if ( !PushToDBQueue( &cc.db_queue ) ) { cd->resp->ResponseNo = 500; return; } if ( cc.file_data.data ) SaveTaskFile( &cc.file_data, cc.file_id ); } } //---------------------------------------------------------------------------
  10. //----------------------------------------------------------------------- void __fastcall TdmSkyUpdate::serverCommandGet(TIdContext *AContext, TIdHTTPRequestInfo *ARequestInfo, TIdHTTPResponseInfo *AResponseInfo) { strCommandData cd; #ifdef AUX_MODE #ifdef MY_DEBUG_MODE cd.log_queue.push_back( ARequestInfo->RawHTTPCommand ); // debug cd.log_queue.push_back( ARequestInfo->RawHeaders->Text ); // debug #endif MyTimer timer; timer.Start(); #endif if ( db_queue_size >= max_db_queue_size ) { AResponseInfo->ResponseNo = 503; #ifdef MY_DEBUG_MODE cd.log_queue.push_back( "Переполнение очереди в бд, ограничение " + (AnsiString)max_db_queue_size ); // debug PushToLogQueue( &cd.log_queue ); TIdNotify::NotifyMethod( OnLogQueue ); #endif return; } cd.req = ARequestInfo; cd.resp = AResponseInfo; cd.user = cd.req->AuthUsername; cd.pass = cd.req->AuthPassword; AResponseInfo->CloseConnection = ARequestInfo->Connection.LowerCase() != "keep-alive"; switch ( ARequestInfo->CommandType ) { case hcGET: GetCommand( &cd ); break; case hcPOST: PostCommand( &cd ); break; case hcHEAD: HeadCommand( &cd ); break; case hcPUT: break; } #ifdef MY_DEBUG_MODE PushToLogQueue( &cd.log_queue ); #endif #ifdef AUX_MODE int req_time = timer.GetTimeMSec(); if ( stat.dev_req_time < req_time ) Interlocked->Exchange( stat.dev_req_time, req_time ); #endif } //---------------------------------------------------------------------------
  11. заработало 3600 запросов в секунду, пока не падает, в клиенте время запроса 0,7 секунды поднялось с 0,1 и 70% процессорного времени попробую теперь без обращения к пулу, без критических секций, как время ответа измениться и до краша догнать
  12. Пытаюсь изменить код в исходниках System.Classes.pas: constructor TThread.Create(CreateSuspended: Boolean); -//- {$IF Defined(MSWINDOWS)} //#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 //#define CREATE_SUSPENDED 0x00000004 //FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID); FHandle := BeginThread(nil, 65536, @ThreadProc, Pointer(Self), $00010004, FThreadID); // изменения тут if FHandle = 0 then raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]); компилируется, создаю System.Classes.hpp, однако нет эффекта совсем, удалял совсем стоку создания потока BeginThread - никакого эффекта, видимо rtl не так просто изменить. как применить изменения?
  13. означает "Пиковый рабочий набор", Process Explorer показал Private Bytes 100м и Working Set 63м. Может я не туда смотрю... где гектары? Полагаю ограничение все-таки размер стэка, т.е. размер резервируемых адресов там превышен.
  14. инди создает срэд перед вызовом OnCommandGet, есть HeadersAvailable но оттуда редиректнуть нельзя, а смысл перенаправлять если ресурсы уже заняты... MaxConnections есть ограничение в IdHTTTPServer, и да, разумная цифра 1024 там не будет лишней.
  15. в процессах пиковая память 80м - ни о чём. http://www.transl-gunsmoker.ru/2010/09/windows-2000.html тут пишут что в принципе больше 2k средов один процесс не может сделать. проблема не в этом, винда при вызове API вернула ошибку - ок обработали, работаем дальше, так нет в ошибку доступа и крашиться процесс, это беда библиотеки
  16. последняя строка перед исключением ведет сюда constructor TThread.Create(CreateSuspended: Boolean); {$IFDEF POSIX} var ErrCode: Integer; {$ENDIF POSIX} begin inherited Create; FSuspended := not FExternalThread; FCreateSuspended := CreateSuspended and not FExternalThread; if not FExternalThread then begin {$IF Defined(MSWINDOWS)} FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID); if FHandle = 0 then raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]); далее иду по шагам до строки в IdStack procedure TIdStack.RaiseLastSocketError; -//- raise EIdSocketError.CreateError(AErr, WSTranslateSocketErrorMsg(AErr)); после чего вылет в AccessViolation и краш процесса. Разработчики хелп... я не знаю куда дальше копать.
  17. в мониторе ресурсов - Анализ цепочки ожидания, там пишет "оказался в ситуации взаимоблокировки"
  18. Среди прочего дошли руки до проекта этой темы. Провожу краш тесты с TIdHTTPServer. Все как описано раньше, сервер, пул данных, критические секции. На localhost нагружаю эмулятором с удержанием соединения, как ни удивительно, но до 1500 запросов в секунду он держался. Это уже больше чем я ожидал, 1500 соединений по запросу в секунду, однако... Вылетает он после этого в ListenException, с сообщением "Недостаточно памяти для выполнения команды", после чего наглухо залипает процесс. Бог с ним с быстродействием, как обработать исключение, чтобы вернуться в рабочий режим? и какой ему не хватает памяти?
  19. В свойствах формы Quality попробовать поменять.
  20. Под виндовс WinAPI, смотреть CreateThread, андроид, соответственно, AndroidAPI.
  21. CBuilder, Seattle, VCL, один и тот же модуль используется в приложении для отладки и в службе, код собственно: String fn = app_dir_path + "log\\"; CreateDirectory( fn.c_str(), 0 ); В приложении этот код работает, начал собирать Service: E2034 Cannot convert 'wchar_t *' to 'const char *' Ок, тогда так: AnsiString fn = app_dir_path + "log\\"; CreateDirectory( fn.c_str(), 0 ); В сервисе работает, в приложении: SkyUpdateDM.cpp(81): E2034 Cannot convert 'char *' to 'const wchar_t *' Специально что ли? может чего в хедерах указать?
  22. хорошие компоненты, краш при разрыве связи...
×
×
  • Создать...