Commit cc9e5fe6dbc2c7769094127d537b1cfd012ce66f
1 parent
f026b588
Introduce WebSocket blocking send timeout parameter. Use Work Stealing Pool for …
…dynamic threads management instead of custom ThreadPoolExecutor.
Showing
9 changed files
with
28 additions
and
69 deletions
... | ... | @@ -41,7 +41,6 @@ import java.util.Map; |
41 | 41 | public class WebSocketConfiguration implements WebSocketConfigurer { |
42 | 42 | |
43 | 43 | public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; |
44 | - public static final String WS_SECURITY_USER_ATTRIBUTE = "SECURITY_USER"; | |
45 | 44 | private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; |
46 | 45 | |
47 | 46 | @Bean |
... | ... | @@ -68,7 +67,6 @@ public class WebSocketConfiguration implements WebSocketConfigurer { |
68 | 67 | response.setStatusCode(HttpStatus.UNAUTHORIZED); |
69 | 68 | return false; |
70 | 69 | } else { |
71 | - attributes.put(WS_SECURITY_USER_ATTRIBUTE, user); | |
72 | 70 | return true; |
73 | 71 | } |
74 | 72 | } | ... | ... |
... | ... | @@ -16,14 +16,17 @@ |
16 | 16 | package org.thingsboard.server.controller.plugin; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.apache.tomcat.websocket.Constants; | |
19 | 20 | import org.springframework.beans.factory.BeanCreationNotAllowedException; |
20 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 22 | import org.springframework.beans.factory.annotation.Value; |
23 | +import org.springframework.security.core.Authentication; | |
22 | 24 | import org.springframework.stereotype.Service; |
23 | 25 | import org.springframework.util.StringUtils; |
24 | 26 | import org.springframework.web.socket.CloseStatus; |
25 | 27 | import org.springframework.web.socket.TextMessage; |
26 | 28 | import org.springframework.web.socket.WebSocketSession; |
29 | +import org.springframework.web.socket.adapter.NativeWebSocketSession; | |
27 | 30 | import org.springframework.web.socket.handler.TextWebSocketHandler; |
28 | 31 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
29 | 32 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -38,6 +41,7 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketMsgEndpoint; |
38 | 41 | import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; |
39 | 42 | import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; |
40 | 43 | |
44 | +import javax.websocket.Session; | |
41 | 45 | import java.io.IOException; |
42 | 46 | import java.net.URI; |
43 | 47 | import java.security.InvalidParameterException; |
... | ... | @@ -56,6 +60,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr |
56 | 60 | @Autowired |
57 | 61 | private TelemetryWebSocketService webSocketService; |
58 | 62 | |
63 | + @Value("${server.ws.blocking_send_timeout:5000}") | |
64 | + private long blockingSendTimeout; | |
65 | + | |
59 | 66 | @Value("${server.ws.limits.max_sessions_per_tenant:0}") |
60 | 67 | private int maxSessionsPerTenant; |
61 | 68 | @Value("${server.ws.limits.max_sessions_per_customer:0}") |
... | ... | @@ -96,6 +103,12 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr |
96 | 103 | public void afterConnectionEstablished(WebSocketSession session) throws Exception { |
97 | 104 | super.afterConnectionEstablished(session); |
98 | 105 | try { |
106 | + if (session instanceof NativeWebSocketSession) { | |
107 | + Session nativeSession = ((NativeWebSocketSession)session).getNativeSession(Session.class); | |
108 | + if (nativeSession != null) { | |
109 | + nativeSession.getUserProperties().put(Constants.BLOCKING_SEND_TIMEOUT_PROPERTY, new Long(blockingSendTimeout)); | |
110 | + } | |
111 | + } | |
99 | 112 | String internalSessionId = session.getId(); |
100 | 113 | TelemetryWebSocketSessionRef sessionRef = toRef(session); |
101 | 114 | String externalSessionId = sessionRef.getSessionId(); |
... | ... | @@ -159,7 +172,7 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr |
159 | 172 | if (!"telemetry".equalsIgnoreCase(serviceToken)) { |
160 | 173 | throw new InvalidParameterException("Can't find plugin with specified token!"); |
161 | 174 | } else { |
162 | - SecurityUser currentUser = (SecurityUser) session.getAttributes().get(WebSocketConfiguration.WS_SECURITY_USER_ATTRIBUTE); | |
175 | + SecurityUser currentUser = (SecurityUser) ((Authentication)session.getPrincipal()).getPrincipal(); | |
163 | 176 | return new TelemetryWebSocketSessionRef(UUID.randomUUID().toString(), currentUser, session.getLocalAddress(), session.getRemoteAddress()); |
164 | 177 | } |
165 | 178 | } | ... | ... |
... | ... | @@ -75,14 +75,7 @@ import java.util.List; |
75 | 75 | import java.util.Map; |
76 | 76 | import java.util.Optional; |
77 | 77 | import java.util.Set; |
78 | -import java.util.concurrent.ConcurrentHashMap; | |
79 | -import java.util.concurrent.ConcurrentMap; | |
80 | -import java.util.concurrent.ExecutorService; | |
81 | -import java.util.concurrent.Executors; | |
82 | -import java.util.concurrent.LinkedBlockingQueue; | |
83 | -import java.util.concurrent.SynchronousQueue; | |
84 | -import java.util.concurrent.ThreadPoolExecutor; | |
85 | -import java.util.concurrent.TimeUnit; | |
78 | +import java.util.concurrent.*; | |
86 | 79 | import java.util.function.Consumer; |
87 | 80 | import java.util.stream.Collectors; |
88 | 81 | |
... | ... | @@ -137,7 +130,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
137 | 130 | |
138 | 131 | @PostConstruct |
139 | 132 | public void initExecutor() { |
140 | - executor = new ThreadPoolExecutor(0, 50, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); | |
133 | + executor = Executors.newWorkStealingPool(50); | |
141 | 134 | } |
142 | 135 | |
143 | 136 | @PreDestroy | ... | ... |
... | ... | @@ -23,28 +23,10 @@ import org.springframework.stereotype.Service; |
23 | 23 | import org.thingsboard.rule.engine.api.util.DonAsynchron; |
24 | 24 | import org.thingsboard.server.actors.ActorSystemContext; |
25 | 25 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
26 | -import org.thingsboard.server.common.transport.SessionMsgListener; | |
27 | -import org.thingsboard.server.common.transport.TransportService; | |
28 | 26 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
29 | 27 | import org.thingsboard.server.common.transport.service.AbstractTransportService; |
30 | 28 | import org.thingsboard.server.gen.transport.TransportProtos; |
31 | -import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg; | |
32 | -import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | |
33 | -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; | |
34 | -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; | |
35 | -import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | |
36 | -import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | |
37 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; | |
38 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | |
39 | -import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | |
40 | -import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; | |
41 | -import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; | |
42 | -import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; | |
43 | -import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; | |
44 | -import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; | |
45 | -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; | |
46 | -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | |
47 | -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | |
29 | +import org.thingsboard.server.gen.transport.TransportProtos.*; | |
48 | 30 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
49 | 31 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
50 | 32 | import org.thingsboard.server.service.encoding.DataDecodingEncodingService; |
... | ... | @@ -53,15 +35,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra |
53 | 35 | import javax.annotation.PostConstruct; |
54 | 36 | import javax.annotation.PreDestroy; |
55 | 37 | import java.util.Optional; |
56 | -import java.util.UUID; | |
57 | -import java.util.concurrent.ConcurrentHashMap; | |
58 | -import java.util.concurrent.ConcurrentMap; | |
59 | -import java.util.concurrent.ExecutorService; | |
60 | -import java.util.concurrent.Executors; | |
61 | -import java.util.concurrent.ScheduledExecutorService; | |
62 | -import java.util.concurrent.SynchronousQueue; | |
63 | -import java.util.concurrent.ThreadPoolExecutor; | |
64 | -import java.util.concurrent.TimeUnit; | |
65 | 38 | import java.util.function.Consumer; |
66 | 39 | |
67 | 40 | /** | ... | ... |
... | ... | @@ -28,11 +28,7 @@ import org.thingsboard.server.kafka.*; |
28 | 28 | |
29 | 29 | import javax.annotation.PostConstruct; |
30 | 30 | import javax.annotation.PreDestroy; |
31 | -import java.util.concurrent.ExecutorService; | |
32 | -import java.util.concurrent.LinkedBlockingQueue; | |
33 | -import java.util.concurrent.SynchronousQueue; | |
34 | -import java.util.concurrent.ThreadPoolExecutor; | |
35 | -import java.util.concurrent.TimeUnit; | |
31 | +import java.util.concurrent.*; | |
36 | 32 | |
37 | 33 | /** |
38 | 34 | * Created by ashvayka on 05.10.18. |
... | ... | @@ -68,7 +64,7 @@ public class RemoteTransportApiService { |
68 | 64 | |
69 | 65 | @PostConstruct |
70 | 66 | public void init() { |
71 | - this.transportCallbackExecutor = new ThreadPoolExecutor(0, 100, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); | |
67 | + this.transportCallbackExecutor = Executors.newWorkStealingPool(100); | |
72 | 68 | |
73 | 69 | TBKafkaProducerTemplate.TBKafkaProducerTemplateBuilder<TransportApiResponseMsg> responseBuilder = TBKafkaProducerTemplate.builder(); |
74 | 70 | responseBuilder.settings(kafkaSettings); | ... | ... |
... | ... | @@ -33,6 +33,7 @@ server: |
33 | 33 | key-alias: "${SSL_KEY_ALIAS:tomcat}" |
34 | 34 | log_controller_error_stack_trace: "${HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE:true}" |
35 | 35 | ws: |
36 | + blocking_send_timeout: "${TB_SERVER_WS_BLOCKING_SEND_TIMEOUT:5000}" | |
36 | 37 | limits: |
37 | 38 | # Limit the amount of sessions and subscriptions available on each server. Put values to zero to disable particular limitation |
38 | 39 | max_sessions_per_tenant: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SESSIONS_PER_TENANT:0}" |
... | ... | @@ -167,6 +168,9 @@ cassandra: |
167 | 168 | buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}" |
168 | 169 | concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}" |
169 | 170 | permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" |
171 | + dispatcher_threads: "${CASSANDRA_QUERY_DISPATCHER_THREADS:2}" | |
172 | + callback_threads: "${CASSANDRA_QUERY_CALLBACK_THREADS:4}" | |
173 | + poll_ms: "${CASSANDRA_QUERY_POLL_MS:50}" | |
170 | 174 | rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:10000}" |
171 | 175 | tenant_rate_limits: |
172 | 176 | enabled: "${CASSANDRA_QUERY_TENANT_RATE_LIMITS_ENABLED:false}" | ... | ... |
... | ... | @@ -28,15 +28,7 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; |
28 | 28 | import org.thingsboard.server.gen.transport.TransportProtos; |
29 | 29 | |
30 | 30 | import java.util.UUID; |
31 | -import java.util.concurrent.ConcurrentHashMap; | |
32 | -import java.util.concurrent.ConcurrentMap; | |
33 | -import java.util.concurrent.ExecutorService; | |
34 | -import java.util.concurrent.Executors; | |
35 | -import java.util.concurrent.LinkedBlockingQueue; | |
36 | -import java.util.concurrent.ScheduledExecutorService; | |
37 | -import java.util.concurrent.SynchronousQueue; | |
38 | -import java.util.concurrent.ThreadPoolExecutor; | |
39 | -import java.util.concurrent.TimeUnit; | |
31 | +import java.util.concurrent.*; | |
40 | 32 | |
41 | 33 | /** |
42 | 34 | * Created by ashvayka on 17.10.18. |
... | ... | @@ -278,7 +270,7 @@ public abstract class AbstractTransportService implements TransportService { |
278 | 270 | new TbRateLimits(perDevicesLimitsConf); |
279 | 271 | } |
280 | 272 | this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(); |
281 | - this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); | |
273 | + this.transportCallbackExecutor = Executors.newWorkStealingPool(20); | |
282 | 274 | this.schedulerExecutor.scheduleAtFixedRate(this::checkInactivityAndReportActivity, sessionReportTimeout, sessionReportTimeout, TimeUnit.MILLISECONDS); |
283 | 275 | } |
284 | 276 | ... | ... |
... | ... | @@ -52,7 +52,7 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor< |
52 | 52 | @Value("${cassandra.query.concurrent_limit}") int concurrencyLimit, |
53 | 53 | @Value("${cassandra.query.permit_max_wait_time}") long maxWaitTime, |
54 | 54 | @Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads, |
55 | - @Value("${cassandra.query.callback_threads:2}") int callbackThreads, | |
55 | + @Value("${cassandra.query.callback_threads:4}") int callbackThreads, | |
56 | 56 | @Value("${cassandra.query.poll_ms:50}") long pollMs, |
57 | 57 | @Value("${cassandra.query.tenant_rate_limits.enabled}") boolean tenantRateLimitsEnabled, |
58 | 58 | @Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration, | ... | ... |
... | ... | @@ -25,17 +25,7 @@ import org.thingsboard.server.common.msg.tools.TbRateLimits; |
25 | 25 | |
26 | 26 | import javax.annotation.Nullable; |
27 | 27 | import java.util.UUID; |
28 | -import java.util.concurrent.BlockingQueue; | |
29 | -import java.util.concurrent.ConcurrentHashMap; | |
30 | -import java.util.concurrent.ConcurrentMap; | |
31 | -import java.util.concurrent.ExecutorService; | |
32 | -import java.util.concurrent.Executors; | |
33 | -import java.util.concurrent.LinkedBlockingDeque; | |
34 | -import java.util.concurrent.LinkedBlockingQueue; | |
35 | -import java.util.concurrent.ScheduledExecutorService; | |
36 | -import java.util.concurrent.ThreadPoolExecutor; | |
37 | -import java.util.concurrent.TimeUnit; | |
38 | -import java.util.concurrent.TimeoutException; | |
28 | +import java.util.concurrent.*; | |
39 | 29 | import java.util.concurrent.atomic.AtomicInteger; |
40 | 30 | |
41 | 31 | /** |
... | ... | @@ -72,7 +62,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend |
72 | 62 | this.concurrencyLimit = concurrencyLimit; |
73 | 63 | this.queue = new LinkedBlockingDeque<>(queueLimit); |
74 | 64 | this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads); |
75 | - this.callbackExecutor = new ThreadPoolExecutor(callbackThreads, 50, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); | |
65 | + this.callbackExecutor = Executors.newWorkStealingPool(callbackThreads); | |
76 | 66 | this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); |
77 | 67 | this.perTenantLimitsEnabled = perTenantLimitsEnabled; |
78 | 68 | this.perTenantLimitsConfiguration = perTenantLimitsConfiguration; | ... | ... |