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

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

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

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

  • Посещение

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

    9

Сообщения, опубликованные Камышев Александр

  1. В 27.01.2017 в 15:42, Rusland сказал:

    9000+ - это select-ы или запись в базу так же быстро делается?

    При запросе оборудование сообщает серверу свои параметры, сервер записывает их в базу, всего три записи в три таблицы, два целых числа и одна строка плюс ключевые поля. 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. 7 минут назад, Равиль Зарипов (ZuBy) сказал:

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

    и в devart тоже самое

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

  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. В 13.01.2017 в 23:59, egorea1999 сказал:

     

    Как-то вы поступили очень грубо :)

     

    Бывает B)

    Про директивы не знал, теперь буду в курсе, так изящнее, спасибо, но увы и ах:

    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. В 22.09.2016 в 12:01, kami сказал:

    На Indy, которые генерят на каждый запрос отдельный поток? Хммм....Несколько тысяч потоков, инициализация каждого - затратная операция с т.з. ОС... ну да, ну да...

    результаты краш-теста - упал в "Недостаточно памяти" после 5770 запросов в секунду, как бы и ничего так :) 

    С обращением к пулу - 3к и без задержек на доступ к критическим секциям 5к запросов с задержкой 200 мсек. - непринужденно, конечно после допила системного TThread, беда кстати была не в инди, а в теплом ламповом borland.

    тестировалось на ПК разработчика, сервер и эмулятор на localhost:

    2017-01-13_15-58-44.png

  7. 9 минут назад, Евгений Корепов сказал:

    А если  TIdHTTPServer заменить на TIdTCPServer, интересно на сколько увеличится быстродействие? И еще вопрос - по ядрам процессора нагрузка распределяется равномерно или грузит только одно ядро в вашем случае?

     

    Не думаю что сколько нибудь заметно, если брать 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. :D заработало:D

    3600 запросов в секунду, пока не падает, в клиенте время запроса 0,7 секунды поднялось с 0,1 и 70% процессорного времени

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

  12. Цитата

    Вы можете попробовать втиснуть больше потоков в ваш процесс уменьшением начального размера стека - вы можете сделать это либо указанием размера в заголовке 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 не так просто изменить.

    как применить изменения?

  13. 29 минут назад, kami сказал:

    Я бы не стал доверять стандартному диспетчеру задач. Не знаю, что означает "пиковая память", но скорее всего - это максимальное значение памяти, которое процесс занимал в физической оперативке. Это совсем не соответствует тому, сколько процесс реально взял памяти у системы.

    означает "Пиковый рабочий набор", Process Explorer показал Private Bytes 100м и Working Set 63м.

    Может я не туда смотрю... где гектары?

    Полагаю ограничение все-таки размер стэка, т.е. размер резервируемых адресов там превышен.

  14. Только что, Евгений Корепов сказал:

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

    инди создает срэд перед вызовом OnCommandGet, есть HeadersAvailable но оттуда редиректнуть нельзя, а смысл перенаправлять если ресурсы уже заняты...

    MaxConnections есть ограничение в IdHTTTPServer, и да, разумная цифра 1024 там не будет лишней.

  15. 4 минуты назад, kami сказал:

    Скорее всего, в ProcessExplorer вы увидите, что ваш процесс "сожрал" гектара полтора в PrivateBytes.

    в процессах пиковая память 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. Среди прочего дошли руки до проекта этой темы.

    Провожу краш тесты с TIdHTTPServer. Все как описано раньше, сервер, пул данных, критические секции. На localhost нагружаю эмулятором с удержанием соединения, как ни удивительно, но до 1500 запросов в секунду он держался. Это уже больше чем я ожидал, 1500 соединений по запросу в секунду, однако...

    Вылетает он после этого в ListenException, с сообщением "Недостаточно памяти для выполнения команды", после чего наглухо залипает процесс. Бог с ним с быстродействием, как обработать исключение, чтобы вернуться в рабочий режим? и какой ему не хватает памяти?

  18. 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 *'
    Специально что ли? может чего в хедерах указать?

  19. 17 часов назад, ZuBy сказал:
    • в этот момент компоненты которые использовал (MyDac, FireDac) крашились и невозможно было сделать реконект
    • их события которые имелись для этих целей, не отрабатывали правильно

    хорошие компоненты, краш при разрыве связи...

×
×
  • Создать...