Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [RMI] Как поймать дисконнект rmi-клиента? каприз заказчика 
V
    Опции темы
jk1
Дата 29.9.2010, 17:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 2
Всего: 75



Коллеги,

Прошу Вашей помощи в решении следующей проблемы:

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

Процесс обработки запроса на сервере занимает около 3-4 секунд. Если за это время связь с клиентом была потеряна, то это надо как-то отловить и выполнить откат изменений.
Вопрос состоит в том, как отловить то что клиент отвалился?

Реализация интерфейса Unreferenced не подходит - требуется определить дисконнект именно конкретного клиента.

Буду признателен за любую помощь.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
LSD
Дата 29.9.2010, 18:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 19
Всего: 538



Создавайте registry програмно и передавайте ему свою RMIServerSocketFactory и RMIClientSocketFactory. А в этих фабриках создавайте свои сокеты которые будут враперами поверх обычных сокетов и которые будут перехватывать ошибки сокетного соединения.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Skipy
Дата 29.9.2010, 19:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 487
Регистрация: 24.8.2006
Где: Москва, Россия

Репутация: 4
Всего: 16



Цитата(LSD @ 29.9.2010,  18:30)
Создавайте registry програмно и передавайте ему свою RMIServerSocketFactory и RMIClientSocketFactory. А в этих фабриках создавайте свои сокеты которые будут враперами поверх обычных сокетов и которые будут перехватывать ошибки сокетного соединения.

smile Мы идем одинаковым путем... Однако этот путь как-то слабо пока проходим. Мне не удалось, имея сокет, возвращенный ServerSocket.accept(), уловить, что с другой стороны закрыли соединение. Никаких ошибок, сокет по-прежнему bound, connected, !closed, !inputShutdown, !outputShutdown. getSocketChannel возвращает null, отсюда тоже не подступишься.

В общем, если эту задачу решить - остальное расскажу.


--------------------
С уважением,
Евгений aka Skipy
www.skipy.ru
PM MAIL WWW ICQ   Вверх
COVD
Дата 29.9.2010, 20:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



А разве RMI клиент не получит соответствующее исключение вместо ответа сервера? И сервер при попытке отослать ответ не получит исключение? Смысл RMI вроде в том, чтобы не знать слова "сокет".

Это сообщение отредактировал(а) COVD - 29.9.2010, 20:08
PM MAIL   Вверх
jk1
Дата 29.9.2010, 20:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 2
Всего: 75



Цитата

А разве RMI клиент не получит соответствующее исключение вместо ответа сервера?


Клиент получит исключение если отвалился сервер, но этот случай к задаче не относится. Если отвалился клиент, то сервер ничего не получает, отправляет данные фактически "в пустоту". Там ситуация похожа на то, что описал Skipy: соединение со всех сторон валидно, а клиента уже нет.

LSD,

Спасибо за идею - буду пробовать, по результатам отпишусь. Пока же вопрос еще открыт.

Это сообщение отредактировал(а) jk1 - 29.9.2010, 20:34


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
COVD
Дата 29.9.2010, 21:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Цитата

отправляет данные фактически "в пустоту". 


Они разве по UDP общаются? Если же по TCP, то проблема в том, что встроенный механизм проверки соединения в TCP имеет слишком большой период и обрыв соединения может обнаружиться естественным образом, т.е. в виде исключения, через час, например. Это на чтении. А при записи сразу. И как можно "в пустоту" отправить по TCP непонятно. 

Можно реализовывать таймаут на уровне приложения. Или другое архитектурное решение. Если возникает идея "оптимизировать" RMI, то может лучше его вовсе не применять.  
PM MAIL   Вверх
jk1
Дата 29.9.2010, 21:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 2
Всего: 75



Цитата

И как можно "в пустоту" отправить по TCP непонятно. 

В этом я с Вами согласен. Если мне кто-то объяснит, почему так происходит я буду очень признателен. Насчет урезать TCP-таймаут это интересно, надо будет попробовать.
Цитата

Если возникает идея "оптимизировать" RMI, то может лучше его вовсе не применять.   

Я бы и не связался с ним никогда, WS и все дела. Но решение приняли "архитекторы" наверху и руки у меня в этом отношении связаны.



--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Skipy
Дата 30.9.2010, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 487
Регистрация: 24.8.2006
Где: Москва, Россия

Репутация: 4
Всего: 16



YESSSSSS!  smile  Я это сделал!

Как говорится, если гора не идет к Магомету, то Магомет идет к горе.

Собственно, надо было пройти на шаг дальше. Я перекрыл сокеты, а надо было еще и OutputStream, который возвращает сокет. И в конце обработки запроса этому outputStream-у ставить обработчик ошибок, который будет срабатывать на все IOException, которые непременно возникнут при попытке писать ответ в закрытый сокет. 

В общем, описываю схему на словах и прикладываю код, так проще понять будет.

1. Делаем wrapper-ы - MyServerSocket, MySocket и MyOutputStream. MyServerSocket на accept возвращает MySocket, MySocket в качестве output stream-а возвращает MyOutputStream.

2. Делаем реестр MySocket-ов. Он static и thread-local. При получении inputStream-а MySocket кладет себя в этот реестр.

3. Создаем интерфейс обработчика ошибок MyOutputStreamErrorHandler. Реализуем его в сервере так, чтобы он содержал все необходимые для отката данные. Устанавливаем реализацию этого интерфейса в MyOutputStream через MySocket, полученный из реестра.

4. В MyOutputStreamErrorHandler перехватываем все IOException, обрабатываем их с помощью установленного обработчика, исключение пробрасываем дальше.

Всё. Проверял, если сделать запрос, а потом срубить JVM (thread.stop не поможет) до возврата ответа - обработчик ошибки отрабатывает.

Запуск примера: сначала MyServerImpl, потом MyClient. Запрос обрабатывается 5 секунд, в течение которых JVM клиента можно срубить.

P.S. Ну повышайте же мне репутацию немедленно сейчас же!  smile 

P.P.S. jk1, за задачу спасибо! Получил большое удовольствие!

Добавлено @ 11:13
Цитата(COVD @ 29.9.2010,  20:06)
И сервер при попытке отослать ответ не получит исключение? Смысл RMI вроде в том, чтобы не знать слова "сокет".

Сервер, разумеется, получит. Но он устроен так:

Серверный сокет обрабатывает подключения. Создает клиентский сокет и некий обработчик в виде Runnable. Этот обработчик отдается потоку из пула, имеющегося у RMI.

Обработчик разбирает входной поток, десериализует данные, понимает, какой метод какого объекта надо вызвать. Находит соответствующий объект у себя в реестре, вызывает метод. Получает результат. Всё, Ваш серверный код RMI отработал. Дальше производится сериализация данных для ответа и только тут начинается запись в OutputStream. Естественно, она вызывает ошибку, ибо сокет закрыт, а в пустоту TCP отправлять не умеет. Но до Вашего серверного кода она не доходит, да и не должна. Сервер не должен отрабатывать клиентские ошибки (а то, что клиент отвалился, это проблемы исключительно клиента), он должен выдавать результат. Если нужен надежный возврат ответа сервера - делайте подтверждение вторым вызовом с клиента, при отсутствии которого в течение определенного времени осуществляется автооткат. Не наш вариант, мы не можем менять клиента.

Это сообщение отредактировал(а) Skipy - 30.9.2010, 11:16

Присоединённый файл ( Кол-во скачиваний: 12 )
Присоединённый файл  rmi_disconnect.zip 6,13 Kb


--------------------
С уважением,
Евгений aka Skipy
www.skipy.ru
PM MAIL WWW ICQ   Вверх
Skipy
Дата 30.9.2010, 11:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 487
Регистрация: 24.8.2006
Где: Москва, Россия

Репутация: 4
Всего: 16



Упс, забыл! Там надо еще policy-файл при запуске сервера указать, иначе ошибки будут.

-Djava.security.policy=rmitest.policy

Файл прикрепил.

Присоединённый файл ( Кол-во скачиваний: 8 )
Присоединённый файл  rmitest.policy 0,05 Kb


--------------------
С уважением,
Евгений aka Skipy
www.skipy.ru
PM MAIL WWW ICQ   Вверх
jk1
Дата 30.9.2010, 13:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1168
Регистрация: 17.10.2008
Где: Санкт-Петербург

Репутация: 2
Всего: 75



Skipy, 

Спасибо за решение, то что надо. +1.


--------------------
Opinions are like assholes — everybody has one
PM MAIL   Вверх
Skipy
Дата 30.9.2010, 14:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 487
Регистрация: 24.8.2006
Где: Москва, Россия

Репутация: 4
Всего: 16



Единственное, что там надо доделать - аккуратно удалять из реестра MySocket, когда произошел сбой. Обычно он удаляется по socket.close, но в этом случае не факт, что socket.close вызовется. В принципе, ничего страшного не произойдет, если не удалить - я-то по ссылке проверяю, что сокет не мой, и переписываю. Но чтобы не оставались висячие объекты, лучше сделать.


--------------------
С уважением,
Евгений aka Skipy
www.skipy.ru
PM MAIL WWW ICQ   Вверх
COVD
Дата 30.9.2010, 17:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Skipy, +1

Я рассуждал с позиции "используем RMI, следовательно, забываем про существование сокетов" и пришел к простому (и наверняка известному) правилу, что клиентское приложение всегда должно иметь таймаут на сетевое чтение. Если используемое средство коммуникации (например, RMI) не имеет встроенного таймаута или его параметры не настраиваются, то таймаут надо реализовывать в приложении.

PM MAIL   Вверх
Skipy
Дата 30.9.2010, 17:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 487
Регистрация: 24.8.2006
Где: Москва, Россия

Репутация: 4
Всего: 16



Да есть у него таймаут... Лучше бы не было... smile Свойство sun.rmi.transport.tcp.readTimeout:

Код

    private static final int connectionReadTimeout =    // default 2 hours
    AccessController.doPrivileged(
        new GetIntegerAction("sun.rmi.transport.tcp.readTimeout",
                 2 * 3600 * 1000));


Красиво? Кстати, в коде, где этот таймаут выставляется сокету, комментарий - "может, оставить это для фабрики?". ТАк что в фабрике тоже можно выставить, может, что-то интересное и получится. Хотя все равно сокет надо протащить в обработчик, чтобы понять, закрыт он или нет.


--------------------
С уважением,
Евгений aka Skipy
www.skipy.ru
PM MAIL WWW ICQ   Вверх
COVD
Дата 30.9.2010, 17:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1655
Регистрация: 26.7.2005

Репутация: 11
Всего: 43



Да, два часа - это пока стандартное значение. Выставить вручную в сокете - вроде не на всех системах работает ( по слухам smile ) . Таймаут на уровне приложения - самое надежное.

Это сообщение отредактировал(а) COVD - 30.9.2010, 17:50
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Работа с сетью | Следующая тема »


 




[ Время генерации скрипта: 0.0915 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.