DCOM: Как закрыть соединение на сервере при сбое клиента? - PullRequest
6 голосов
/ 25 января 2011

У меня довольно старый проект: клиент и сервер DCOM, оба на C ++ \ ATL, только платформа Windows. Все работает отлично: локальные и удаленные клиенты подключаются к серверу и работают одновременно без проблем.

Но когда происходит сбой удаленного клиента или его уничтожение диспетчером задач или командой "taskkill" или выключением питания - у меня проблема. Мой сервер ничего не знает о сбое клиента и пытается отправить новые события всем клиентам (также, чтобы уже произошел сбой). В результате у меня пауза (сервер не может отправить данные уже сбойному клиенту), и его продолжительность пропорциональна количеству сбитых удаленных клиентов. После 5 сбоев клиентов паузы настолько длинные, что это равносильно полной остановке сервера.

Я знаю о механизме пинга DCOM (DCOM должен отключать клиентов, которые не отвечают на пинг каждые 2 минуты после 6 минут молчания). И действительно, после 6 минут зависания у меня небольшой период нормальной работы, но затем сервер возвращается в состояние «приостановлено».

Что я могу сделать со всем этим? Как заставить DCOM "пинг" нормально работать? Если я реализую свой собственный код ping, возможно ли вручную отключить соединение со старыми клиентами DCOM? Как это сделать?

Ответы [ 4 ]

1 голос
/ 25 января 2011

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

Самый простой способ сделать это - использовать QueueUserWorkItem - это вызовет переданный обратный вызов в системном пуле потоков приложения.Предполагая, что вы используете MTA, это все, что вам нужно сделать:

static InfoStruct {
    IRemoteHost *pRemote;
    BSTR someData;
};

static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) {
  CoInitializeEx(COINIT_MULTITHREADED);

  InfoStruct *is = (InfoStruct *)lpInfo;
  is->pRemote->notify(someData);
  is->pRemote->Release();
  SysFreeString(is->someData);
  delete is;

  CoUninitialize();
  return 0;
}

void InvokeClient(IRemoteHost *pRemote, BSTR someData) {

  InfoStruct *is = new InfoStruct;
  is->pRemote = pRemote;
  pRemote->AddRef();

  is->someData = SysAllocString(someData);
  QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION);
}

Если ваш основной поток находится в STA, это только немного сложнее;вам просто нужно использовать CoMarshalInterThreadInterfaceInStream и CoGetInterfaceAndReleaseStream для передачи указателя интерфейса между квартирами:

static InfoStruct {
    IStream *pMarshalledRemote;
    BSTR someData;
};

static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) {
  CoInitializeEx(COINIT_MULTITHREADED); // can be STA as well

  InfoStruct *is = (InfoStruct *)lpInfo;
  IRemoteHost *pRemote;
  CoGetInterfaceAndReleaseStream(is->pMarshalledRemote, __uuidof(IRemoteHost), (LPVOID *)&pRemote);

  pRemote->notify(someData);
  pRemote->Release();
  SysFreeString(is->someData);
  delete is;

  CoUninitialize();

  return 0;
}

void InvokeClient(IRemoteHost *pRemote, BSTR someData) {
  InfoStruct *is = new InfoStruct;
  CoMarshalInterThreadInterfaceInStream(__uuidof(IRemoteHost), pRemote, &is->pMarshalledRemote);

  is->someData = SysAllocString(someData);
  QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION);
}

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

Некоторые более сложные варианты, которые вы можете захотетьрассмотреть возможность обеспечения того, чтобы только один запрос выполнялся во время полета для каждого клиента за раз (таким образом еще больше снижая влияние зависшего клиента) и кэширование указателя маршализованного интерфейса в MTA (если ваш основной поток является STA) - так как я считаю, CoMarshalInterThreadInterfaceInStream может выполнять сетевые запросы, в идеале вы бы хотели позаботиться об этом заранее, когда знаете, клиент подключен, вместо того, чтобы рисковать блокировкой в ​​основном потоке.

0 голосов
/ 30 мая 2011

Вы можете реализовать свой собственный механизм ping, чтобы ваши клиенты время от времени вызывали метод ping сервера.Вы уже поддерживаете своего рода контейнер для своих клиентов на стороне сервера.На этой карте пометьте каждого клиента отметкой времени последнего пинга.Затем проверьте, жив ли клиент, прежде чем отправлять события этому клиенту.Вы можете настроить стратегию, когда прекратить отправку событий, например, на основе времени или количества пропущенных пингов, типа события или некоторых других факторов.Вам, вероятно, не нужно беспокоиться об удалении клиентов - это может подождать, пока DCOM не поймет, что конкретный клиент мертв.Эта схема не может полностью устранить проблему, поскольку клиент может умереть непосредственно перед отправкой события, но вы сможете полностью контролировать количество таких клиентов, настроив период проверки.Чем меньше этот период, тем меньше мертвых клиентов, хотя вы платите трафиком.

0 голосов
/ 28 февраля 2011

Используйте DCOM для установки уведомления именованного канала.Отключение лучше обрабатывается с помощью труб.Слушатель мгновенно реагирует на сообщения.например, Server-> Client (как называется ваш канал?).Клиент-> Сервер отвечает именем, которое включает машину.Клиент создает именованный канал и слушает.Сервер открывает канал сразу или при необходимости.

0 голосов
/ 25 января 2011

Одним из решений будет устранение событий - заставить клиентов запрашивать у сервера, есть ли что-то интересное.

...