Add async websocket send support.
This commit is contained in:
parent
cc9e5fe6db
commit
da5679d498
@ -16,7 +16,6 @@
|
|||||||
package org.thingsboard.server.controller.plugin;
|
package org.thingsboard.server.controller.plugin;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.tomcat.websocket.Constants;
|
|
||||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -41,14 +40,19 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint;
|
|||||||
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
|
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
|
||||||
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
|
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
|
||||||
|
|
||||||
|
import javax.websocket.RemoteEndpoint;
|
||||||
|
import javax.websocket.SendHandler;
|
||||||
|
import javax.websocket.SendResult;
|
||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.security.InvalidParameterException;
|
import java.security.InvalidParameterException;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -60,8 +64,8 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TelemetryWebSocketService webSocketService;
|
private TelemetryWebSocketService webSocketService;
|
||||||
|
|
||||||
@Value("${server.ws.blocking_send_timeout:5000}")
|
@Value("${server.ws.send_timeout:5000}")
|
||||||
private long blockingSendTimeout;
|
private long sendTimeout;
|
||||||
|
|
||||||
@Value("${server.ws.limits.max_sessions_per_tenant:0}")
|
@Value("${server.ws.limits.max_sessions_per_tenant:0}")
|
||||||
private int maxSessionsPerTenant;
|
private int maxSessionsPerTenant;
|
||||||
@ -106,7 +110,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
if (session instanceof NativeWebSocketSession) {
|
if (session instanceof NativeWebSocketSession) {
|
||||||
Session nativeSession = ((NativeWebSocketSession)session).getNativeSession(Session.class);
|
Session nativeSession = ((NativeWebSocketSession)session).getNativeSession(Session.class);
|
||||||
if (nativeSession != null) {
|
if (nativeSession != null) {
|
||||||
nativeSession.getUserProperties().put(Constants.BLOCKING_SEND_TIMEOUT_PROPERTY, new Long(blockingSendTimeout));
|
nativeSession.getAsyncRemote().setSendTimeout(sendTimeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String internalSessionId = session.getId();
|
String internalSessionId = session.getId();
|
||||||
@ -177,15 +181,52 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SessionMetaData {
|
private static class SessionMetaData implements SendHandler {
|
||||||
private final WebSocketSession session;
|
private final WebSocketSession session;
|
||||||
|
private final RemoteEndpoint.Async asyncRemote;
|
||||||
private final TelemetryWebSocketSessionRef sessionRef;
|
private final TelemetryWebSocketSessionRef sessionRef;
|
||||||
|
|
||||||
|
private volatile boolean isSending = false;
|
||||||
|
|
||||||
|
private Queue<String> msgQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) {
|
SessionMetaData(WebSocketSession session, TelemetryWebSocketSessionRef sessionRef) {
|
||||||
super();
|
super();
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
Session nativeSession = ((NativeWebSocketSession)session).getNativeSession(Session.class);
|
||||||
|
this.asyncRemote = nativeSession.getAsyncRemote();
|
||||||
this.sessionRef = sessionRef;
|
this.sessionRef = sessionRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void sendMsg(String msg) {
|
||||||
|
if (isSending) {
|
||||||
|
msgQueue.add(msg);
|
||||||
|
} else {
|
||||||
|
isSending = true;
|
||||||
|
sendMsgInternal(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMsgInternal(String msg) {
|
||||||
|
try {
|
||||||
|
this.asyncRemote.sendText(msg, this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[{}] Failed to send msg", session.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResult(SendResult result) {
|
||||||
|
if (!result.isOK()) {
|
||||||
|
log.error("[{}] Failed to send msg", session.getId(), result.getException());
|
||||||
|
}
|
||||||
|
String msg = msgQueue.poll();
|
||||||
|
if (msg != null) {
|
||||||
|
sendMsgInternal(msg);
|
||||||
|
} else {
|
||||||
|
isSending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -202,9 +243,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) {
|
if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) {
|
||||||
log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached"
|
log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached"
|
||||||
, sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), externalId);
|
, sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), externalId);
|
||||||
synchronized (sessionMd) {
|
sessionMd.sendMsg("{\"subscriptionId\":" + subscriptionId + ", \"errorCode\":" + ThingsboardErrorCode.TOO_MANY_UPDATES.getErrorCode() + ", \"errorMsg\":\"Too many updates!\"}");
|
||||||
sessionMd.session.sendMessage(new TextMessage("{\"subscriptionId\":" + subscriptionId + ", \"errorCode\":" + ThingsboardErrorCode.TOO_MANY_UPDATES.getErrorCode() + ", \"errorMsg\":\"Too many updates!\"}"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -212,14 +251,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
|
|||||||
blacklistedSessions.remove(externalId);
|
blacklistedSessions.remove(externalId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
synchronized (sessionMd) {
|
sessionMd.sendMsg(msg);
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
sessionMd.session.sendMessage(new TextMessage(msg));
|
|
||||||
long took = System.currentTimeMillis() - start;
|
|
||||||
if (took >= 1000) {
|
|
||||||
log.info("[{}][{}] Sending message took more than 1 second [{}ms] {}", sessionRef.getSecurityCtx().getTenantId(), externalId, took, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId);
|
log.warn("[{}][{}] Failed to find session by internal id", externalId, internalId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ server:
|
|||||||
key-alias: "${SSL_KEY_ALIAS:tomcat}"
|
key-alias: "${SSL_KEY_ALIAS:tomcat}"
|
||||||
log_controller_error_stack_trace: "${HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE:true}"
|
log_controller_error_stack_trace: "${HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE:true}"
|
||||||
ws:
|
ws:
|
||||||
blocking_send_timeout: "${TB_SERVER_WS_BLOCKING_SEND_TIMEOUT:5000}"
|
send_timeout: "${TB_SERVER_WS_SEND_TIMEOUT:5000}"
|
||||||
limits:
|
limits:
|
||||||
# Limit the amount of sessions and subscriptions available on each server. Put values to zero to disable particular limitation
|
# Limit the amount of sessions and subscriptions available on each server. Put values to zero to disable particular limitation
|
||||||
max_sessions_per_tenant: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_TENANT:0}"
|
max_sessions_per_tenant: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_TENANT:0}"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user