[openhabcloud] reconnect on connection errors (#11153)
* [openhabcloud] reconnect on connection errors According to documentation (albeit for 2.x Socket IO version) [1], reconnection is responsibility of the user on connect_error events. [1] Lifecycle diagram in https://socketio.github.io/socket.io-client-java/socket_instance.html Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] Update Socket IO dependency to 1.0.1 Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] feature.xml updated also with socket io 1.0.1 Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] Re-connect manually on error events when not connected Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] less loud logging on retries Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] removing unnecessary conditional in logging Signed-off-by: Sami Salonen <ssalonen@gmail.com> * [openhabcloud] javadoc corrections and clarifications Signed-off-by: Sami Salonen <ssalonen@gmail.com>
This commit is contained in:
parent
58e7cb67bb
commit
dcca9c0ac6
|
@ -36,13 +36,13 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.osgiify</groupId>
|
<groupId>org.openhab.osgiify</groupId>
|
||||||
<artifactId>io.socket.socket.io-client</artifactId>
|
<artifactId>io.socket.socket.io-client</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.osgiify</groupId>
|
<groupId>org.openhab.osgiify</groupId>
|
||||||
<artifactId>io.socket.engine.io-client</artifactId>
|
<artifactId>io.socket.engine.io-client</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jsr305/3.0.2_1</bundle>
|
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jsr305/3.0.2_1</bundle>
|
||||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1</bundle>
|
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/3.8.1_1</bundle>
|
||||||
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1</bundle>
|
<bundle dependency="true">mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okio/1.13.0_1</bundle>
|
||||||
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.0</bundle>
|
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.socket.io-client/1.0.1</bundle>
|
||||||
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.0</bundle>
|
<bundle dependency="true">mvn:org.openhab.osgiify/io.socket.engine.io-client/1.0.1</bundle>
|
||||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version}</bundle>
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.io.openhabcloud/${project.version}</bundle>
|
||||||
</feature>
|
</feature>
|
||||||
</features>
|
</features>
|
||||||
|
|
|
@ -41,11 +41,13 @@ import org.openhab.core.OpenHAB;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import io.socket.backo.Backoff;
|
||||||
import io.socket.client.IO;
|
import io.socket.client.IO;
|
||||||
import io.socket.client.Manager;
|
import io.socket.client.Manager;
|
||||||
import io.socket.client.Socket;
|
import io.socket.client.Socket;
|
||||||
import io.socket.emitter.Emitter;
|
import io.socket.emitter.Emitter;
|
||||||
import io.socket.engineio.client.Transport;
|
import io.socket.engineio.client.Transport;
|
||||||
|
import io.socket.thread.EventThread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides communication between openHAB and the openHAB Cloud service.
|
* This class provides communication between openHAB and the openHAB Cloud service.
|
||||||
|
@ -122,6 +124,11 @@ public class CloudClient {
|
||||||
private boolean remoteAccessEnabled;
|
private boolean remoteAccessEnabled;
|
||||||
private Set<String> exposedItems;
|
private Set<String> exposedItems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Back-off strategy for reconnecting when manual reconnection is needed
|
||||||
|
*/
|
||||||
|
private final Backoff reconnectBackoff = new Backoff();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of CloudClient
|
* Constructor of CloudClient
|
||||||
*
|
*
|
||||||
|
@ -139,6 +146,9 @@ public class CloudClient {
|
||||||
this.remoteAccessEnabled = remoteAccessEnabled;
|
this.remoteAccessEnabled = remoteAccessEnabled;
|
||||||
this.exposedItems = exposedItems;
|
this.exposedItems = exposedItems;
|
||||||
this.jettyClient = httpClient;
|
this.jettyClient = httpClient;
|
||||||
|
reconnectBackoff.setMin(1000);
|
||||||
|
reconnectBackoff.setMax(30_000);
|
||||||
|
reconnectBackoff.setJitter(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,6 +184,25 @@ public class CloudClient {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).on(Manager.EVENT_CONNECT_ERROR, new Emitter.Listener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
if (args[0] instanceof Exception) {
|
||||||
|
Exception e = (Exception) args[0];
|
||||||
|
logger.debug(
|
||||||
|
"Error connecting to the openHAB Cloud instance: {} {}. Should reconnect automatically.",
|
||||||
|
e.getClass().getSimpleName(), e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.debug(
|
||||||
|
"Error connecting to the openHAB Cloud instance: {}. Should reconnect automatically.",
|
||||||
|
args[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug("Error connecting to the openHAB Cloud instance. Should reconnect automatically.");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
|
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -182,20 +211,86 @@ public class CloudClient {
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
onConnect();
|
onConnect();
|
||||||
}
|
}
|
||||||
|
}).on(Socket.EVENT_CONNECTING, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.debug("Socket.IO connecting");
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_RECONNECTING, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.debug("Socket.IO re-connecting (attempt {})", args[0]);
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_RECONNECT, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.debug("Socket.IO re-connected successfully (attempt {})", args[0]);
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_RECONNECT_ERROR, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
if (args[0] instanceof Exception) {
|
||||||
|
Exception e = (Exception) args[0];
|
||||||
|
logger.debug("Socket.IO re-connect attempt error: {} {}", e.getClass().getSimpleName(),
|
||||||
|
e.getMessage());
|
||||||
|
} else {
|
||||||
|
logger.debug("Socket.IO re-connect attempt error: {}", args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_RECONNECT_FAILED, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.debug("Socket.IO re-connect attempts failed. Stopping reconnection.");
|
||||||
|
}
|
||||||
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
|
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void call(Object... args) {
|
public void call(Object... args) {
|
||||||
|
if (args.length > 0) {
|
||||||
|
logger.debug("Socket.IO disconnected: {}", args[0]);
|
||||||
|
} else {
|
||||||
logger.debug("Socket.IO disconnected");
|
logger.debug("Socket.IO disconnected");
|
||||||
|
}
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
onDisconnect();
|
onDisconnect();
|
||||||
}
|
}
|
||||||
}).on(Socket.EVENT_ERROR, new Emitter.Listener() {
|
}).on(Socket.EVENT_ERROR, new Emitter.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void call(Object... args) {
|
public void call(Object... args) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (CloudClient.this.socket.connected()) {
|
||||||
logger.error("Error connecting to the openHAB Cloud instance: {}", args[0]);
|
if (logger.isDebugEnabled() && args.length > 0) {
|
||||||
|
logger.error("Error during communication: {}", args[0]);
|
||||||
} else {
|
} else {
|
||||||
logger.error("Error connecting to the openHAB Cloud instance");
|
logger.error("Error during communication");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are not connected currently, manual reconnection is needed to keep trying to (re-)establish
|
||||||
|
// connection.
|
||||||
|
//
|
||||||
|
// Socket.IO 1.x java client: 'error' event is emitted from Socket on connection errors that are not
|
||||||
|
// retried, but also with error that are automatically retried. If we
|
||||||
|
//
|
||||||
|
// Note how this is different in Socket.IO 2.x java client, Socket emits 'connect_error' event.
|
||||||
|
// OBS: Don't get confused with Socket IO 2.x docs online, in 1.x connect_error is emitted also on
|
||||||
|
// errors that are retried by the library automatically!
|
||||||
|
long delay = reconnectBackoff.duration();
|
||||||
|
// Try reconnecting on connection errors
|
||||||
|
if (logger.isDebugEnabled() && args.length > 0) {
|
||||||
|
if (args[0] instanceof Exception) {
|
||||||
|
Exception e = (Exception) args[0];
|
||||||
|
logger.error(
|
||||||
|
"Error connecting to the openHAB Cloud instance: {} {}. Reconnecting after {} ms.",
|
||||||
|
e.getClass().getSimpleName(), e.getMessage(), delay);
|
||||||
|
} else {
|
||||||
|
logger.error(
|
||||||
|
"Error connecting to the openHAB Cloud instance: {}. Reconnecting after {} ms.",
|
||||||
|
args[0], delay);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error("Error connecting to the openHAB Cloud instance. Reconnecting.");
|
||||||
|
}
|
||||||
|
socket.close();
|
||||||
|
sleepSocketIO(delay);
|
||||||
|
socket.connect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).on("request", new Emitter.Listener() {
|
}).on("request", new Emitter.Listener() {
|
||||||
|
@ -224,6 +319,7 @@ public class CloudClient {
|
||||||
|
|
||||||
public void onConnect() {
|
public void onConnect() {
|
||||||
logger.info("Connected to the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid, this.localBaseUrl);
|
logger.info("Connected to the openHAB Cloud service (UUID = {}, base URL = {})", this.uuid, this.localBaseUrl);
|
||||||
|
reconnectBackoff.reset();
|
||||||
isConnected = true;
|
isConnected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,4 +674,14 @@ public class CloudClient {
|
||||||
}
|
}
|
||||||
return headersJSON;
|
return headersJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sleepSocketIO(long delay) {
|
||||||
|
EventThread.exec(() -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(delay);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue