Я пишу распределенное приложение Java RMI. Клиент RMI регистрирует обработчик/обратный вызов события на сервере RMI, и сервер при необходимости вызывает функцию обратного вызова клиента. Теперь проблема заключается в том, что при сбое сетевого подключения (например, кабель Ethernet подключен...) сервер RMI и клиент не будут уведомлены, а сервер RMI не сможет выполнить попытку вызвать зарегистрированную клиентом функцию обратного вызова. RMI сервер также не может оповестить об этом RMI-клиента. Еще хуже то, что при восстановлении сетевого соединения служба клиента RMI все равно теряет связь с сервером RMI, потому что никто не уведомляет ее о повторном подключении.
Моя нынешняя идея - реализовать метод ping() в клиенте RMI в отдельном потоке. Этот поток мог просыпаться с регулярными интервалами и проверять сервер. если не удалось, то фарс для повторного подключения.
Любые другие элегантные решения? Надеюсь, вы, ребята, можете помочь!
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MyInterface extends Remote {
public int RegisterEventHandler(RemoteMyEventHandler eventHandler) throws RemoteException;
public void unRegisterEventHandler(int eventHandlerId) throws RemoteException;
}
import java.rmi.RemoteException;
import com.me.MyInterface;
public class MyInterfaceImpl implements MyInterface {
{
public void init() {
try {
//... initialize RMI server....
//....
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public int RegisterEventHandler(RemoteMyEventHandler eventHandler)
throws RemoteException {
return MyEventHandlerImp.getInstance().addHandler(eventHandler);
}
@Override
public void unRegisterEventHandler(int eventHandlerId)
throws RemoteException {
MyEventHandlerImp.getInstance().removeHandler(eventHandlerId);
}
}
//handler.notifyEventSnap(events);
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Properties;
import org.apache.log4j.Logger;
import com.me.MyInterface;
public class MyService implements NotifyHandler{
{
private MyInterface client;
private MyEventHandler myEventHandler;
private void connectToServer() {
try {
//...
Registry registry = LocateRegistry.getRegistry(rmiHost, rmiPort);
client = (MyInterface) registry.lookup(MyCInterface.class.getName());
} catch (RemoteException er) {
} catch (NotBoundException en) {
} catch (Exception en) {
}
}
private void startService(){
//Attach my event handler
if(client != null)
{
myEventHandler = new MyEventHandler();
myEventHandlerId = client.RegisterEventHandler(myEventHandler);
}
}
}
при сбое сетевого подключения (например, кабель Ethernet подключен...) сервер RMI и клиент не будут уведомлены, а сервер RMI не сможет выполнить попытку вызвать зарегистрированную клиентом функцию обратного вызова.
Err, то есть уведомление для сервера. Сервер просто должен отметить это и повторить попытку позже.
Сервер RMI не может уведомить клиента RMI об этой проблеме.
Клиенту не нужно знать.
Еще хуже то, что при восстановлении сетевого соединения служба клиента RMI все равно теряет связь с сервером RMI, потому что никто не уведомляет ее о повторном подключении.
Клиент не должен "повторно подключаться". В RMI отсутствует этап подключения или повторного подключения. Пока клиентские JVM и удаленные объекты остаются и экспортируются соответственно, заглушки на сервере остаются в силе и могут продолжать использоваться сервером.
Вы решаете проблему без проблем.
Кажется, вы частично выполняете сеанс клиент/сервер. Это токен, который сервер может отслеживать, чтобы клиент был действительным. Если есть ошибка, когда сервер общается с клиентом, сеанс должен быть завершен и все ссылки на клиента удалены.
На вашем сервере уже выполняется сеанс с целым числом, используемым для unRegisterEventHandler. Вы должны отслеживать эти целые числа где-то как Карта. Если сервер не может подключиться к клиенту, он должен просто отменить регистрацию этого клиента и сделать сеанс недействительным, удалив его с карты. Сервер должен удалить все ссылки на клиента и не пытаться связываться с клиентом до тех пор, пока не будет создан новый сеанс.
Если клиент пытается связаться с сервером, он должен получить исключение InvalidException с сервера. Таким образом, клиент может попытаться создать новый сеанс, вызвав RegisterEventHandler в блоке catch.
Я работал над проектом, который рассматривал эту проблему, используя пинг, как вы предложили на https://code.google.com/p/umuc-team-factor/
Вся клиентская связь с сервером находилась в замкнутом блоке try catch catch
private void getSession() {
while(isRun()) {
try {
if(server == null) {
Logger.getLogger(JobClient.class.getName()).info("Server is null.");
setupServer();
}
UUID sid = server.getSession(this);
synchronized (this) {
id = sid;
}
Logger.getLogger(JobClient.class.getName()).info("Session id is " + id);
return;
} catch (RemoteException ex) {
Logger.getLogger(JobClient.class.getName()).info("Could not get session from server: " + ex + ". setting up server.");
setupServer();
}
}
}
Попробуйте настроить сеанс с сервером до остановки программы.
Вся коммуникация сервера с клиентом должна завершать сеанс для клиента, если возникает исключение RemoteException. c.status() похож на ping.
List<UUID> endSessions = new ArrayList<UUID>();
for (UUID id : copy.keySet()) {
ClientCallback c = copy.get(id).client;
try {
ClientStatus status = c.status();
Logger.getLogger(ProcessManager.class.getName()).info("got client status for " + id + ": " + status.getSessionID() + " -" + status.getJobStatus());
if (status.getSessionID() == null || !status.getSessionID().equals(id)) {
endSessions.add(id);
}
} catch (Exception ex) {
endSessions.add(id);
Logger.getLogger(ProcessManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
for (UUID id : endSessions) {
try {
endSession(id);
} catch (SessionExpiredException ex) {
Logger.getLogger(ProcessManager.class.getName()).log(Level.SEVERE, null, ex);
}
}