Подключение к циклу сообщений пользовательского сеанса dbx DataSnap - PullRequest
2 голосов
/ 26 января 2012

Есть ли способ подключиться к WndProc сеанса пользователя dbx?

Справочная информация: dbx DataSnap использует компоненты Indy для связи по TCP.В своей простейшей форме сервер DataSnap является сервером Indy TCP, принимающим соединения.Когда соединение установлено, Indy создает поток для этого соединения, который обрабатывает все запросы для этого соединения.

Каждое из этих пользовательских соединений потребляет ресурсы.Для сервера с несколькими сотнями одновременных подключений эти ресурсы могут быть дорогими.Многие из ресурсов могут быть объединены, но я не хочу всегда получать и освобождать ресурс каждый раз, когда он необходим.

Вместо этого я хотел бы реализовать таймер простоя.После завершения потока с ресурсом таймер запускается.Если поток обращается к ресурсу до истечения таймера, ресурс все равно будет «назначен» этому потоку.Но если таймер истекает до следующего доступа, ресурс будет возвращен обратно в пул.В следующий раз, когда потоку понадобится ресурс, из пула будет получен другой ресурс.

Я не нашел способа сделать это.Я пытался использовать SetTimer, но мой таймер обратного вызова никогда не срабатывает.Я предполагаю, что это потому, что WndProc Indy для потока не отправляет WM_TIMER.У меня нет контроля над «циклом выполнения» для этого потока, поэтому я не могу легко проверить, было ли сигнализировано событие.Фактически, ни один из моего кода для этого потока не выполняется, если поток не обрабатывает пользовательский запрос.И на самом деле, я хочу, чтобы код выполнялся вне любого пользовательского запроса.

Решения исходного вопроса или предложения альтернативных подходов были бы одинаково оценены.

Ответы [ 2 ]

1 голос
/ 02 февраля 2012

Джеймс Л, возможно, прибил это.Поскольку в Indy-потоке нет цикла обработки сообщений, вы должны полагаться на другой механизм - например, локальные свойства потока, доступные только для чтения (например, UserCount и / или LastSeem в его «примере»)- и использование основного потока сервера для запуска TTimer для освобождения ресурсов с учетом некоторого правила.

РЕДАКТИРОВАТЬ: другая идея заключается в создании общей структуры данных (пример ниже), которая обновляется каждый разпоток завершает свою работу.

ВНИМАНИЕ: кодирование только из разума ... Оно не может компилироваться ...; -)

Пример:

TThreadStatus = (tsDoingMyJob, tsFinished);

TThreadStatusInfo = class
private
  fTStatus : TThreadStatus;
  fDTFinished : TDateTime;
  procedure SetThreadStatus(value: TThreadStatus);
public
  property ThreadStatus: TThreadStatus read fTStatus write SetStatus;
  property FinishedTime: TDateTime read fDTFinished;
  procedure FinishJob ;
  procedure DoJob;
end

procedure TThreadStatusInfo.SetThreadStatus(value : TThreadStatus)
begin
  fTStatus = value;
  case fTStatus of 
    tsDoingMyJob :
       fDTFinished = TDateTime(0);
    tsFinished:
       fDTFinished = Now;
  end;
end;

procedure TThreadStatusInfo.FinishJob;
begin
  ThreadStatus := tsFinished;
end;

procedure TThreadStatusInfo.DoJob;
begin
  ThreadStatus := tsDoingMyJob;
end;

Поместите этов списке (любой класс списка, который вам нравится), и убедитесь, что каждый поток связан с индексом в этом списке.Удаление элементов из списка только в том случае, если вы больше не будете использовать это количество потоков (сокращение списка).Добавьте элемент при создании нового потока (например, у вас есть 4 потока, а теперь вам нужен 5-й, вы создаете новый элемент в основной поток ).

Поскольку каждый поток имеетиндекс в списке, вам не нужно инкапсулировать эту запись (вызовы T для TCriticalSection.

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

1 голос
/ 02 февраля 2012

Мы пытались реализовать что-то для совместного использования ресурсов между пользовательскими потоками, используя TCP-соединения (без транспорта HTTP, поэтому нет SessionManager), но столкнулись с множеством проблем. В конце мы отказались от использования отдельных пользовательских потоков (установите LifeCycle := TDSLifeCycle.Server) и создали наши собственные FResourcePool и FUserList (оба TThreadList) в ServerContainerUnit . Это заняло всего 1 день, и это работает очень хорошо.

Вот упрощенная версия того, что мы сделали:

TResource = class
  SomeResource: TSomeType;
  UserCount: Integer;
  LastSeen: TDateTime;
end;

Когда пользователь подключается, мы проверяем FResourcePool на TResource, в котором нуждается пользователь. Если он существует, мы увеличиваем свойство UserCount ресурса. Когда пользователь закончил, мы уменьшаем свойство UserCount и устанавливаем LastSeen. У нас есть TTimer, который срабатывает каждые 60 секунд, что освобождает любой ресурс с UserCount = 0 и LastSeen, превышающими 60 секунд.

FUserList очень похож. Если пользователь не был замечен в течение нескольких часов, мы предполагаем, что его соединение было разорвано (поскольку наше клиентское приложение выполняет автоматическое отключение, если пользователь простаивал в течение 90 минут), поэтому мы программно отключаем пользователя на стороне сервера. , что также уменьшает их использование каждого ресурса. Конечно, это означает, что нам пришлось самим создавать переменную сеанса (например, CreateGUID();) и передавать ее клиенту при первом подключении. Клиент передает идентификатор сеанса обратно на сервер с каждым запросом, чтобы мы знали, какая запись FUserList принадлежит им. Хотя это является недостатком , а не с использованием пользовательских потоков, им легко управлять.

...