![]() |
|
Модераторы: LSD, AntonSaburov |
![]()
|
|
| jk1 |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1168 Регистрация: 17.10.2008 Где: Санкт-Петербург Репутация: 2 Всего: 75 |
Коллеги,
Прошу Вашей помощи в решении следующей проблемы: Есть RMI-взаимодействие вида клиент-сервер. Клиентом выступает некая система заказчика, то есть код клиента поменять возможности нет. К коду сервера имею доступ, могу поменять в нем практически все, в пределах разумного конечно. Процесс обработки запроса на сервере занимает около 3-4 секунд. Если за это время связь с клиентом была потеряна, то это надо как-то отловить и выполнить откат изменений. Вопрос состоит в том, как отловить то что клиент отвалился? Реализация интерфейса Unreferenced не подходит - требуется определить дисконнект именно конкретного клиента. Буду признателен за любую помощь. -------------------- Opinions are like assholes — everybody has one |
|||
|
||||
| LSD |
|
|||
![]() 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. |
|||
|
||||
| Skipy |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 487 Регистрация: 24.8.2006 Где: Москва, Россия Репутация: 4 Всего: 16 |
В общем, если эту задачу решить - остальное расскажу. |
|||
|
||||
| COVD |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1655 Регистрация: 26.7.2005 Репутация: 11 Всего: 43 |
А разве RMI клиент не получит соответствующее исключение вместо ответа сервера? И сервер при попытке отослать ответ не получит исключение? Смысл RMI вроде в том, чтобы не знать слова "сокет".
Это сообщение отредактировал(а) COVD - 29.9.2010, 20:08 |
|||
|
||||
| jk1 |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1168 Регистрация: 17.10.2008 Где: Санкт-Петербург Репутация: 2 Всего: 75 |
Клиент получит исключение если отвалился сервер, но этот случай к задаче не относится. Если отвалился клиент, то сервер ничего не получает, отправляет данные фактически "в пустоту". Там ситуация похожа на то, что описал Skipy: соединение со всех сторон валидно, а клиента уже нет. LSD, Спасибо за идею - буду пробовать, по результатам отпишусь. Пока же вопрос еще открыт. Это сообщение отредактировал(а) jk1 - 29.9.2010, 20:34 -------------------- Opinions are like assholes — everybody has one |
|||
|
||||
| COVD |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1655 Регистрация: 26.7.2005 Репутация: 11 Всего: 43 |
Они разве по UDP общаются? Если же по TCP, то проблема в том, что встроенный механизм проверки соединения в TCP имеет слишком большой период и обрыв соединения может обнаружиться естественным образом, т.е. в виде исключения, через час, например. Это на чтении. А при записи сразу. И как можно "в пустоту" отправить по TCP непонятно. Можно реализовывать таймаут на уровне приложения. Или другое архитектурное решение. Если возникает идея "оптимизировать" RMI, то может лучше его вовсе не применять. |
|||
|
||||
| jk1 |
|
||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1168 Регистрация: 17.10.2008 Где: Санкт-Петербург Репутация: 2 Всего: 75 |
В этом я с Вами согласен. Если мне кто-то объяснит, почему так происходит я буду очень признателен. Насчет урезать TCP-таймаут это интересно, надо будет попробовать.
Я бы и не связался с ним никогда, WS и все дела. Но решение приняли "архитекторы" наверху и руки у меня в этом отношении связаны. -------------------- Opinions are like assholes — everybody has one |
||||
|
|||||
| Skipy |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 487 Регистрация: 24.8.2006 Где: Москва, Россия Репутация: 4 Всего: 16 |
YESSSSSS!
Как говорится, если гора не идет к Магомету, то Магомет идет к горе. Собственно, надо было пройти на шаг дальше. Я перекрыл сокеты, а надо было еще и 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. Ну повышайте же мне репутацию немедленно сейчас же! P.P.S. jk1, за задачу спасибо! Получил большое удовольствие! Добавлено @ 11:13
Сервер, разумеется, получит. Но он устроен так: Серверный сокет обрабатывает подключения. Создает клиентский сокет и некий обработчик в виде Runnable. Этот обработчик отдается потоку из пула, имеющегося у RMI. Обработчик разбирает входной поток, десериализует данные, понимает, какой метод какого объекта надо вызвать. Находит соответствующий объект у себя в реестре, вызывает метод. Получает результат. Всё, Ваш серверный код RMI отработал. Дальше производится сериализация данных для ответа и только тут начинается запись в OutputStream. Естественно, она вызывает ошибку, ибо сокет закрыт, а в пустоту TCP отправлять не умеет. Но до Вашего серверного кода она не доходит, да и не должна. Сервер не должен отрабатывать клиентские ошибки (а то, что клиент отвалился, это проблемы исключительно клиента), он должен выдавать результат. Если нужен надежный возврат ответа сервера - делайте подтверждение вторым вызовом с клиента, при отсутствии которого в течение определенного времени осуществляется автооткат. Не наш вариант, мы не можем менять клиента. Это сообщение отредактировал(а) Skipy - 30.9.2010, 11:16 Присоединённый файл ( Кол-во скачиваний: 12 )
rmi_disconnect.zip 6,13 Kb |
|||
|
||||
| Skipy |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 487 Регистрация: 24.8.2006 Где: Москва, Россия Репутация: 4 Всего: 16 |
Упс, забыл! Там надо еще policy-файл при запуске сервера указать, иначе ошибки будут.
-Djava.security.policy=rmitest.policy Файл прикрепил. Присоединённый файл ( Кол-во скачиваний: 8 )
rmitest.policy 0,05 Kb |
|||
|
||||
| jk1 |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 1168 Регистрация: 17.10.2008 Где: Санкт-Петербург Репутация: 2 Всего: 75 |
Skipy,
Спасибо за решение, то что надо. +1. -------------------- Opinions are like assholes — everybody has one |
|||
|
||||
| Skipy |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 487 Регистрация: 24.8.2006 Где: Москва, Россия Репутация: 4 Всего: 16 |
Единственное, что там надо доделать - аккуратно удалять из реестра MySocket, когда произошел сбой. Обычно он удаляется по socket.close, но в этом случае не факт, что socket.close вызовется. В принципе, ничего страшного не произойдет, если не удалить - я-то по ссылке проверяю, что сокет не мой, и переписываю. Но чтобы не оставались висячие объекты, лучше сделать.
|
|||
|
||||
| COVD |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1655 Регистрация: 26.7.2005 Репутация: 11 Всего: 43 |
Skipy, +1
Я рассуждал с позиции "используем RMI, следовательно, забываем про существование сокетов" и пришел к простому (и наверняка известному) правилу, что клиентское приложение всегда должно иметь таймаут на сетевое чтение. Если используемое средство коммуникации (например, RMI) не имеет встроенного таймаута или его параметры не настраиваются, то таймаут надо реализовывать в приложении. |
|||
|
||||
| Skipy |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 487 Регистрация: 24.8.2006 Где: Москва, Россия Репутация: 4 Всего: 16 |
Да есть у него таймаут... Лучше бы не было...
Красиво? Кстати, в коде, где этот таймаут выставляется сокету, комментарий - "может, оставить это для фабрики?". ТАк что в фабрике тоже можно выставить, может, что-то интересное и получится. Хотя все равно сокет надо протащить в обработчик, чтобы понять, закрыт он или нет. |
|||
|
||||
| COVD |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1655 Регистрация: 26.7.2005 Репутация: 11 Всего: 43 |
Да, два часа - это пока стандартное значение. Выставить вручную в сокете - вроде не на всех системах работает ( по слухам
Это сообщение отредактировал(а) COVD - 30.9.2010, 17:50 |
|||
|
||||
![]()
|
| Правила форума "Java" | |
|
|
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | Java: Работа с сетью | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |