-
Постов
235 -
Зарегистрирован
-
Посещение
-
Победитель дней
9
Сообщения, опубликованные Камышев Александр
-
-
В продолжение темы.
С асинхронным режимом 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%.
Все зависит от степени кривизны рук, однако...
-
7 минут назад, Равиль Зарипов (ZuBy) сказал:
это давняя болезнь, еще при переконнекте с вай-фай на мобильные данные такая же проблема. поэтому я перешел на 3-х звенку.
и в devart тоже самое
это и есть трехзвенка, код из сервера приложений , про болезнь - тогда понятно, думал может я чего путаю. спс
-
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?
-
В 13.01.2017 в 23:59, egorea1999 сказал:
Как-то вы поступили очень грубо
Бывает
Про директивы не знал, теперь буду в курсе, так изящнее, спасибо, но увы и ах:
т.е. в 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}В общем, все таки брутальный подход, только хардкор...
-
В 22.09.2016 в 12:01, kami сказал:
На Indy, которые генерят на каждый запрос отдельный поток? Хммм....Несколько тысяч потоков, инициализация каждого - затратная операция с т.з. ОС... ну да, ну да...
результаты краш-теста - упал в "Недостаточно памяти" после 5770 запросов в секунду, как бы и ничего так
С обращением к пулу - 3к и без задержек на доступ к критическим секциям 5к запросов с задержкой 200 мсек. - непринужденно, конечно после допила системного TThread, беда кстати была не в инди, а в теплом ламповом borland.
тестировалось на ПК разработчика, сервер и эмулятор на localhost:
-
9 минут назад, Евгений Корепов сказал:
А если TIdHTTPServer заменить на TIdTCPServer, интересно на сколько увеличится быстродействие? И еще вопрос - по ядрам процессора нагрузка распределяется равномерно или грузит только одно ядро в вашем случае?
Не думаю что сколько нибудь заметно, если брать http пакеты, ведь TIdHTTPServer наследник от TIdCustomTCPServer. Если пересылать бинарные массивы, без парсинга хедеров - возможно и будет небольшое увеличение.
Был приятно удивлен, по всем ядрам ровная нагрузка.
-
думаю эти два куска показывают основную обработку 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 добавить в проект.
и будет вам счастье
-
//--------------------------------------------------------------------------- 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 ); } } //---------------------------------------------------------------------------
-
//----------------------------------------------------------------------- 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 } //---------------------------------------------------------------------------
-
заработало
3600 запросов в секунду, пока не падает, в клиенте время запроса 0,7 секунды поднялось с 0,1 и 70% процессорного времени
попробую теперь без обращения к пулу, без критических секций, как время ответа измениться и до краша догнать
-
Цитата
Вы можете попробовать втиснуть больше потоков в ваш процесс уменьшением начального размера стека - вы можете сделать это либо указанием размера в заголовке PE-файле, либо вручную указав размер стека в функции CreateThread, как указано в MSDN:
1
h := CreateThread(nil, 4096, @ThreadProc, nil, STACK_SIZE_PARAM_IS_A_RESERVATION, id);Пытаюсь изменить код в исходниках 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 не так просто изменить.
как применить изменения?
-
29 минут назад, kami сказал:
Я бы не стал доверять стандартному диспетчеру задач. Не знаю, что означает "пиковая память", но скорее всего - это максимальное значение памяти, которое процесс занимал в физической оперативке. Это совсем не соответствует тому, сколько процесс реально взял памяти у системы.
означает "Пиковый рабочий набор", Process Explorer показал Private Bytes 100м и Working Set 63м.
Может я не туда смотрю... где гектары?
Полагаю ограничение все-таки размер стэка, т.е. размер резервируемых адресов там превышен.
-
Только что, Евгений Корепов сказал:
Ограничте количество соединений самостоятельно разумной цифрой, при попытке соединения сверх лимита отдавайте в хеадере редирект на следующий сервер в кластере. Если конечно есть следующий сервер...
инди создает срэд перед вызовом OnCommandGet, есть HeadersAvailable но оттуда редиректнуть нельзя, а смысл перенаправлять если ресурсы уже заняты...
MaxConnections есть ограничение в IdHTTTPServer, и да, разумная цифра 1024 там не будет лишней.
-
4 минуты назад, kami сказал:
Скорее всего, в ProcessExplorer вы увидите, что ваш процесс "сожрал" гектара полтора в PrivateBytes.
в процессах пиковая память 80м - ни о чём.
http://www.transl-gunsmoker.ru/2010/09/windows-2000.html тут пишут что в принципе больше 2k средов один процесс не может сделать.
проблема не в этом, винда при вызове API вернула ошибку - ок обработали, работаем дальше, так нет в ошибку доступа и крашиться процесс, это беда библиотеки
-
последняя строка перед исключением ведет сюда
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 и краш процесса.
Разработчики хелп... я не знаю куда дальше копать.
-
-
в мониторе ресурсов - Анализ цепочки ожидания, там пишет "оказался в ситуации взаимоблокировки"
-
ок, как обработать чтобы не вис?
-
Среди прочего дошли руки до проекта этой темы.
Провожу краш тесты с TIdHTTPServer. Все как описано раньше, сервер, пул данных, критические секции. На localhost нагружаю эмулятором с удержанием соединения, как ни удивительно, но до 1500 запросов в секунду он держался. Это уже больше чем я ожидал, 1500 соединений по запросу в секунду, однако...
Вылетает он после этого в ListenException, с сообщением "Недостаточно памяти для выполнения команды", после чего наглухо залипает процесс. Бог с ним с быстродействием, как обработать исключение, чтобы вернуться в рабочий режим? и какой ему не хватает памяти?
-
В свойствах формы Quality попробовать поменять.
-
Под виндовс WinAPI, смотреть CreateThread, андроид, соответственно, AndroidAPI.
-
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 *'
Специально что ли? может чего в хедерах указать? -
пользуйтесь поиском
-
17 часов назад, ZuBy сказал:
- в этот момент компоненты которые использовал (MyDac, FireDac) крашились и невозможно было сделать реконект
- их события которые имелись для этих целей, не отрабатывали правильно
хорошие компоненты, краш при разрыве связи...
Нет callback от TFDQuery
в Компоненты
Опубликовано
При запросе оборудование сообщает серверу свои параметры, сервер записывает их в базу, всего три записи в три таблицы, два целых числа и одна строка плюс ключевые поля. Selectы сервер делает периодически, через 5 минут, обновление кэша. Практически идет только запись.
На пакет http ответ уходит сразу, без подтверждения записи в бд. Если ждать подтверждение скорость резко падает, и больше 600-800 http запросов служба затыкается, краш где-то получается.
Всего удалось получить больше 3000 тысяч запросов т.е. 3000 * 3 = 9к в секунду, движок СУБД при этом занимает 10-15% загрузки.
MySql с максимальными настройками, измерял время потраченное на три запроса - поднималось в пике до 50мс. Десять коннектов держали по 300 таких пакетов запросов, т.е. среднее время должно было быть ~3 мс на запрос. Как-то не сходится... буду дальше копать