Я новичок в COM-программировании, и хотел бы получить вашу консультацию по вопросам, ответы на которые я не смог найти в книжках.
Такая ситуация: exe-сервер, несколько клиентов. Клиенты обращаются к объекту
внутри сервера (один экземпляр для всех клиентов), объект генерирует события для клиентов (через механизм connection points). Клиенты и сервер на разных машинах. Все работает, но если мы посмотрим на это дело пессимистически, у нас могут возникнуть такие вопросы:
1) А что произойдет, если сервер упадет? Глюкануло что-нибудь, подвисло, сетевой шнурок отвалился, кто-то решил тачку перегрузить (кнопкой reset), да мало ли причин может быть. Как клиент узнает, что указатель на интерфейс серверного объекта уже не актуален?
2) Допустим, мы знаем ответ на первый вопрос, и наш клиент умеет определять состояние сервера, и перестает обращаться к серверу, если с ним возникли какие-то проблемы. Но если сервер перезапустили, как клиент узнает, что уже можно опять начинать с ним работать?
3) А если упадет не сервер, а совсем наоборот, один из клиентов? Как сервер узнает что данного клиента нужно удалить из списка клиентов, которым он посылает сообщения (т.е. для которых он генерирует события)?
Существуют в COM стандартные механизмы обработки таких ситуаций?
Если нет, то как такие ситуации вообще можно отследить? Или скажем так: как это обычно делают?
Здравствуйте asv, Вы писали:
asv>Я новичок в COM-программировании, и хотел бы получить вашу консультацию по вопросам, ответы на которые я не смог найти в книжках.
asv>Такая ситуация: exe-сервер, несколько клиентов. Клиенты обращаются к объекту asv>внутри сервера (один экземпляр для всех клиентов), объект генерирует события для клиентов (через механизм connection points). Клиенты и сервер на разных машинах. Все работает, но если мы посмотрим на это дело пессимистически, у нас могут возникнуть такие вопросы:
<вопросы переброшены вниз> asv>Существуют в COM стандартные механизмы обработки таких ситуаций? asv>Если нет, то как такие ситуации вообще можно отследить? Или скажем так: как это обычно делают?
Мне видится, что СОМ как технология изначально не имеет механизмов обработки таких ситуаций. У нас есть HRESULT, и только через него мы и можем поиметь ту или иную информацию. Но проблема-то есть. И скорей всего она решается в области RPC (подсистема вызова удалённых процедур).
Вызов метода несуществующего объекта (изчезнувшего легально или аварийно), скорей всего, вернётся с кодом ошибки RPC_E_XXXX или CO_E_XXX_RPC_XXXX. Во всяком случае, код ошибки не будет связан с работой объекта. Для этого и есть требование СОМ, чтобы любой метод имел сигнатуру HRESULT fff(...), обеспечивающей возврат ошибки от RPC. Однако здесь же лежат ошибки, связанные с таймаутами соединения.
Исчезновение клиента объекта (опять же легально или аварийно), вполне возможно, может обслуживаться stub/proxy соединения с ним: они могут освободить свои ссылки на этот объект.
В любом случае оповещение не предусматривается. Да и механизма такого на поверхности СОМ нет. Т.е. узнавание таких ситуаций лежит как на клиенте, так и на сервере.
asv>1) А что произойдет, если сервер упадет? Глюкануло что-нибудь, подвисло, сетевой шнурок отвалился, кто-то решил тачку перегрузить (кнопкой reset), да мало ли причин может быть. Как клиент узнает, что указатель на интерфейс серверного объекта уже не актуален?
Вызов метода несуществующего объекта (изчезнувшего легально или аварийно), скорей всего, вернётся с кодом ошибки RPC_E_XXXX или CO_E_XXX_RPC_XXXX.
asv>2) Допустим, мы знаем ответ на первый вопрос, и наш клиент умеет определять состояние сервера, и перестает обращаться к серверу, если с ним возникли какие-то проблемы. Но если сервер перезапустили, как клиент узнает, что уже можно опять начинать с ним работать?
Никак. Того объекта, с которым он работал уже нет и никогда не будет. Т.е. нужно пытаться вновь создавать связь точно также, как и при начале работы клиента.
asv>3) А если упадет не сервер, а совсем наоборот, один из клиентов? Как сервер узнает что данного клиента нужно удалить из списка клиентов, которым он посылает сообщения (т.е. для которых он генерирует события)?
С точки зрения событий, сервер уже является клиентом объекта (бывшего) клиента, поэтому смотри п.1.
PS
Упадёт-упадёт... А если "Что будет, если клиент закончит работу (нормальный выход) и не освободит объект сервера?"
Для сервера это тоже ведь плохо. Не так ли.
Здравствуйте asv, Вы писали:
asv>Я новичок в COM-программировании, и хотел бы получить вашу консультацию по вопросам, ответы на которые я не смог найти в книжках.
asv>Такая ситуация: exe-сервер, несколько клиентов. Клиенты обращаются к объекту asv>внутри сервера (один экземпляр для всех клиентов), объект генерирует события для клиентов (через механизм connection points). Клиенты и сервер на разных машинах. Все работает, но если мы посмотрим на это дело пессимистически, у нас могут возникнуть такие вопросы:
asv>1) А что произойдет, если сервер упадет? Глюкануло что-нибудь, подвисло, сетевой шнурок отвалился, кто-то решил тачку перегрузить (кнопкой reset), да мало ли причин может быть. Как клиент узнает, что указатель на интерфейс серверного объекта уже не актуален?
У меня были такие ситуации. В таком случае proxy вернёт HRESULT с соответствующей ошибкой.
На самом деле проблема (которую вы может быть и не усмотрели) состоит в том, что возможно,
что proxy вернёт HRESULT с ошибкой из за таймаута (временные проблемы с сетью) и тогда начинается самое интересное, но это уже совсем другая история.
asv>2) Допустим, мы знаем ответ на первый вопрос, и наш клиент умеет определять состояние сервера, и перестает обращаться к серверу, если с ним возникли какие-то проблемы. Но если сервер перезапустили, как клиент узнает, что уже можно опять начинать с ним работать?
Вариантов тут очень много. Как реализуешь, так и будет, главное тут определить, что сервер умер,
а дальше дело твоё.
asv>3) А если упадет не сервер, а совсем наоборот, один из клиентов? Как сервер узнает что данного клиента нужно удалить из списка клиентов, которым он посылает сообщения (т.е. для которых он генерирует события)?
Тут всё сложнее, так как в таком случае клиент оставит неосвобождённые ссылки. Если логика
работы сервера особо от этого не зависит, то это не очень страшно, так как в конце концов
сервер должен определить сам, что клиент умер (так написано у Бокса, хотя у меня этого не происходило). А само определение того, что клиент умер происходит опять через proxy и HRESULT.
(Опять оцените что начнётся в данной ситуации при таймауте !)
asv>Существуют в COM стандартные механизмы обработки таких ситуаций? asv>Если нет, то как такие ситуации вообще можно отследить? Или скажем так: как это обычно делают?
Обработка как обычно. Не ленишся проверять HRESULT и всё в порядке. Интересно другое (может кто знает) если соединение с сервером потеряно из за таймаута, то как его восстановить, не применяя искусственных методов ?
asv>
Народная мудрось
всем все никому ничего(с).
Re[2]: Обработка ошибок в COM
От:
Аноним
Дата:
12.07.02 02:10
Оценка:
Здравствуйте Vi2, Вы писали:
Vi2>Вызов метода несуществующего объекта (изчезнувшего легально или аварийно), >скорей всего, вернётся с кодом ошибки RPC_E_XXXX или CO_E_XXX_RPC_XXXX. Во >всяком случае, код ошибки не будет связан с работой объекта. Для этого и есть >требование СОМ, чтобы любой метод имел сигнатуру HRESULT fff(...), >обеспечивающей возврат ошибки от RPC. Однако здесь же лежат ошибки, связанные с >таймаутами соединения.
Провел эксперименты. И в случае клиента, и в случае сервера, при вызове метода
умершего объекта возвращается ошибка RPC_S_SERVER_UNAVAILABLE. Правда, пускалось все в пределах одной машины.
Vi2>PS Vi2>Упадёт-упадёт... А если "Что будет, если клиент закончит работу (нормальный выход) и не освободит объект сервера?" Vi2>Для сервера это тоже ведь плохо. Не так ли.
Ну это-то как раз нетрудно предусмотреть. При нормальном завершении клиента, он может отписаться от сервера перед тем, как закрыться. Точно так же если сервер закрывается корректно, он может сообщить клиентам о этом (через тот же механизм событий), и они отключатся от сервера.
Проблема как раз в некорректном закрытии одного из компонентов. Но здесь вроде тоже ясность наступила (надо отслеживать HRESULT).
Остался вопрос не столько технологии, сколько алгоритма: как клиенту восстановить соединение? Единственное, что пока приходит в голову: периодически (c некоторым заданным интервалом, скажем 5 секунд) вызывать CoCreateInstance до тех пор, пока это не увенчается успехом. Есть какие-нибудь идеи по этому поводу?
Re[2]: Обработка ошибок в COM
От:
Аноним
Дата:
12.07.02 02:19
Оценка:
Здравствуйте Tom, Вы писали:
Tom>У меня были такие ситуации. В таком случае proxy вернёт HRESULT с соответствующей ошибкой.
Проверил, точно: RPC_S_SERVER_UNAVAILABLE
Tom>На самом деле проблема (которую вы может быть и не усмотрели) состоит в том, что возможно, Tom>что proxy вернёт HRESULT с ошибкой из за таймаута (временные проблемы с сетью) и тогда начинается самое интересное, но это уже совсем другая история.
В MSDN упоминаются еще такие ошибки:
RPC_S_COMM_FAILURE
RPC_S_SEND_INCOMPLETE
RPC_S_SERVER_TOO_BUSY
Возможно, при тайм-ауте возникает одна из них? Правда, как это проверить, не знаю.
asv>>Но если сервер перезапустили, как клиент узнает, что уже можно опять начинать с ним работать? Tom>Вариантов тут очень много. Как реализуешь, так и будет, главное тут определить, что сервер умер, Tom>а дальше дело твоё.
Мне вот пока ничего в голову не приходит, кроме как без конца вызывать CoCreateInstance, пока этот вызов не увенчается успехом. Какие еще есть варианты?
Здравствуйте Аноним, Вы писали:
Vi2>>PS Vi2>>Упадёт-упадёт... А если "Что будет, если клиент закончит работу (нормальный выход) и не освободит объект сервера?"
А>Ну это-то как раз нетрудно предусмотреть. При нормальном завершении клиента, он может отписаться от сервера перед тем, как закрыться. Точно так же если сервер закрывается корректно, он может сообщить клиентам о этом (через тот же механизм событий), и они отключатся от сервера.
Я же говорил о том, что аварийный завершение клиента и нормальный выход, но с ошибкой алгоритма по освобождению сервера (мог бы отписАться, но не отписАлся!), для сервера эквивалентны — клиента нет, а счётчики остаются. Поэтому как бы особых телодвижений со стороны СОМ в таких ситуациях нет.
А>Проблема как раз в некорректном закрытии одного из компонентов. Но здесь вроде тоже ясность наступила (надо отслеживать HRESULT).
А>Остался вопрос не столько технологии, сколько алгоритма: как клиенту восстановить соединение? Единственное, что пока приходит в голову: периодически (c некоторым заданным интервалом, скажем 5 секунд) вызывать CoCreateInstance до тех пор, пока это не увенчается успехом. Есть какие-нибудь идеи по этому поводу?
Где-то в прошлых темах форума были идеи на этот счёт. Разрыв связи клиента и сервера и восстановление её — эта тема периодически всплывает вновь и вновь.
А>Мне вот пока ничего в голову не приходит, кроме как без конца вызывать CoCreateInstance, пока этот вызов не увенчается успехом. Какие еще есть варианты?
В принципе почему бы и нет, хотя кривовато как то. По человечески софт в данной ситуации должен уведомить пользователя, что "произошло отключение, восстановить соединение или нет" и НИЧЕГО АВТОМАТИЧЕСКИ НЕ ВОССТАНАВЛИВАТЬ. Это конечно если софт с ГУИ, если нет то хрен его знает... Вообще мне кажется, что если ты хочешь восстанавить соединение, и попытаться работать с того места на котором ты остановился, то это очень сложно и в большом проекте не получится. Я точно могу сказать одно: "В DCOM разрыв соединения это самое слабое место"
Народная мудрось
всем все никому ничего(с).
Re[4]: Обработка ошибок в COM
От:
Аноним
Дата:
13.07.02 09:37
Оценка:
Здравствуйте Vi2, Вы писали:
Vi2>Где-то в прошлых темах форума были идеи на этот счёт. Разрыв связи клиента и сервера и восстановление её — эта тема периодически всплывает вновь и вновь.
Просмотрел почти все темы — не нашел ничего подобного...
Re[4]: Обработка ошибок в COM
От:
Аноним
Дата:
13.07.02 09:44
Оценка:
Здравствуйте Tom, Вы писали:
А>>Мне вот пока ничего в голову не приходит, кроме как без конца вызывать CoCreateInstance, пока этот вызов не увенчается успехом. Какие еще есть варианты?
Tom>В принципе почему бы и нет, хотя кривовато как то. По человечески софт в данной ситуации должен уведомить пользователя, что "произошло отключение, восстановить соединение или нет" и НИЧЕГО АВТОМАТИЧЕСКИ НЕ ВОССТАНАВЛИВАТЬ.
Интуитивно это кажется правильным. Но на практике не ясно, как реализовать. Дело в том, что (в моей системе) на одной машине может быть несколько сотен клиентов, и если они каждый из них покажет message box при разрыве, у пользователя будет бледный вид.
>Это конечно если софт с ГУИ, если нет то хрен его знает... Вообще мне кажется, >что если ты хочешь восстанавить соединение, и попытаться работать с того места >на котором ты остановился, то это очень сложно и в большом проекте не >получится. Я точно могу сказать одно: "В DCOM разрыв соединения это самое >слабое место"
Да, но что-то все равно надо делать. Если перезагрузка сервера вынуждает перезагружать все клиенты на всех компьютерах (а клиентских компьютеров может быть десятки) — это никуда не годная система.
Здравствуйте Аноним, Вы писали:
А>Здравствуйте Tom, Вы писали:
А>Интуитивно это кажется правильным. Но на практике не ясно, как реализовать. Дело в том, что (в моей системе) на одной машине может быть несколько сотен клиентов, и если они каждый из них покажет message box при разрыве, у пользователя будет бледный вид.
Опиши более подробно проект.
А>Да, но что-то все равно надо делать. Если перезагрузка сервера вынуждает перезагружать все клиенты на всех компьютерах (а клиентских компьютеров может быть десятки) — это никуда не годная система.
Если очень хочется чего нибудь с этим сделать, то напиши свой DCOM транспорт, который автоматом будет контроллировать всякую бяку, но это от всего гемороя тебя не спасёт.
Здравствуйте Tom, Вы писали:
А>>Интуитивно это кажется правильным. Но на практике не ясно, как реализовать. Дело в том, что (в моей системе) на одной машине может быть несколько сотен клиентов, и если они каждый из них покажет message box при разрыве, у пользователя будет бледный вид.
Tom>Опиши более подробно проект.
Сервер: один exe-модуль с одним объектом (и одним экземпляром объекта). Объект, реализующий соединение с сервером на стороне клиента (т.е. хранящий указатель на серверный объект и принимающий события от сервера), заключен в dll. Этот объект может использоваться как exe-приложениями (на VC, VB и т.д.), так и ActiveX-ами, которые в свою очередь могут быть вставлены в контейнер типа того же VB. Клиенты обращаются к серверу либо по событию от сервера ("данные изменились, нужно считать"), либо по своей собственной инициативе (если они хотят туда что-то записать). Ну вот и хотелось бы доработать вот этот универсальный клиентский объект, чтобы он мог восстанавливать соединение при перезагрузке сервера.
А>>Да, но что-то все равно надо делать. Если перезагрузка сервера вынуждает перезагружать все клиенты на всех компьютерах (а клиентских компьютеров может быть десятки) — это никуда не годная система.
Tom>Если очень хочется чего нибудь с этим сделать, то напиши свой DCOM транспорт, который автоматом будет контроллировать всякую бяку, но это от всего гемороя тебя не спасёт.
Как-то не верится, что в тех существующих системах, в которых эта проблема решена, написан свой DCOM транспорт.
asv>Сервер: один exe-модуль с одним объектом (и одним экземпляром объекта). Объект, реализующий соединение с сервером на стороне клиента (т.е. хранящий указатель на серверный объект и принимающий события от сервера), заключен в dll. Этот объект может использоваться как exe-приложениями (на VC, VB и т.д.), так и ActiveX-ами, которые в свою очередь могут быть вставлены в контейнер типа того же VB. Клиенты обращаются к серверу либо по событию от сервера ("данные изменились, нужно считать"), либо по своей собственной инициативе (если они хотят туда что-то записать). Ну вот и хотелось бы доработать вот этот универсальный клиентский объект, чтобы он мог восстанавливать соединение при перезагрузке сервера.
На ум пришёл явный способ восстановления соединения.
1. На клиенте пишешь COM-SINGLETON с методом OnResumeConnection. (см. дальше)
На сервере добавляешь метод NotifyOnResumeConnection(адрес клиента), который добавляет данного клиента в список для извещения о обрыве связи.
2. Сервер сохраняет:
2.1 Cвоё состояние
2.2 Cписок клиентов
2.3 Состояние клиента (что именно сохранять уже думать тебе, в зависимости от требований по восстановлению)
После запуска сервера он проверяет был ли выход корректным, и если нет, то создаёт обьект на клиентах из списка и вызывает метод OnResumeConnection(...). Самое главное здесь определить промежуточные состояния, (которые будут сохраниться, и для которых возможно корректное восстановление) и реализовать OnResumeConnection. Для не очень сложного проекта или небольшого числа обьектов может и подойдёт.
PS. В OnResumeConnection ты можешь передавать указатель на новый экземпляр сервера.
Tom>>Если очень хочется чего нибудь с этим сделать, то напиши свой DCOM транспорт, который автоматом будет контроллировать всякую бяку, но это от всего гемороя тебя не спасёт.
У нас в одном проекте именно так эта проблемма и решена. (хотя это крайний выход)
Здравствуйте Tom, Вы писали:
Tom> 2. Сервер сохраняет: Tom> 2.1 Cвоё состояние Tom> 2.2 Cписок клиентов Tom> 2.3 Состояние клиента (что именно сохранять уже думать тебе, в зависимости от требований по восстановлению)
Где сохраняет? В файле? Как-то нехорошо... И что делать с клиентами, которые запустились, пока сервера не было?
В-общем, я сделал так, как ты мне сначала посоветовал. Т.е. при разрыве связи на клиенте вылазит окошко типа "Восстановить соединение?". Тут были свои проблемы, но, как оказалось, решаемые. Спасибо за помощь!