Showing
11 changed files
with
234 additions
and
53 deletions
... | ... | @@ -134,42 +134,58 @@ public class LocalTransportService extends AbstractTransportService implements R |
134 | 134 | |
135 | 135 | @Override |
136 | 136 | public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) { |
137 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback); | |
137 | + if (checkLimits(sessionInfo, callback)) { | |
138 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSessionEvent(msg).build(), callback); | |
139 | + } | |
138 | 140 | } |
139 | 141 | |
140 | 142 | @Override |
141 | 143 | public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { |
142 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback); | |
144 | + if (checkLimits(sessionInfo, callback)) { | |
145 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostTelemetry(msg).build(), callback); | |
146 | + } | |
143 | 147 | } |
144 | 148 | |
145 | 149 | @Override |
146 | 150 | public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) { |
147 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback); | |
151 | + if (checkLimits(sessionInfo, callback)) { | |
152 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setPostAttributes(msg).build(), callback); | |
153 | + } | |
148 | 154 | } |
149 | 155 | |
150 | 156 | @Override |
151 | 157 | public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { |
152 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback); | |
158 | + if (checkLimits(sessionInfo, callback)) { | |
159 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setGetAttributes(msg).build(), callback); | |
160 | + } | |
153 | 161 | } |
154 | 162 | |
155 | 163 | @Override |
156 | 164 | public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { |
157 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback); | |
165 | + if (checkLimits(sessionInfo, callback)) { | |
166 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToAttributes(msg).build(), callback); | |
167 | + } | |
158 | 168 | } |
159 | 169 | |
160 | 170 | @Override |
161 | 171 | public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { |
162 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback); | |
172 | + if (checkLimits(sessionInfo, callback)) { | |
173 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setSubscribeToRPC(msg).build(), callback); | |
174 | + } | |
163 | 175 | } |
164 | 176 | |
165 | 177 | @Override |
166 | 178 | public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { |
167 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback); | |
179 | + if (checkLimits(sessionInfo, callback)) { | |
180 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToDeviceRPCCallResponse(msg).build(), callback); | |
181 | + } | |
168 | 182 | } |
169 | 183 | |
170 | 184 | @Override |
171 | 185 | public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { |
172 | - forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); | |
186 | + if (checkLimits(sessionInfo, callback)) { | |
187 | + forwardToDeviceActor(TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setToServerRPCCallRequest(msg).build(), callback); | |
188 | + } | |
173 | 189 | } |
174 | 190 | |
175 | 191 | @Override | ... | ... |
... | ... | @@ -444,7 +444,7 @@ transport: |
444 | 444 | bind_port: "${MQTT_BIND_PORT:1883}" |
445 | 445 | timeout: "${MQTT_TIMEOUT:10000}" |
446 | 446 | netty: |
447 | - leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}" | |
447 | + leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" | |
448 | 448 | boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}" |
449 | 449 | worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}" |
450 | 450 | max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}" | ... | ... |
... | ... | @@ -249,7 +249,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
249 | 249 | @Override |
250 | 250 | public void onError(Throwable e) { |
251 | 251 | log.trace("[{}] Failed to publish msg: {}", sessionId, msg, e); |
252 | - ctx.close(); | |
252 | + processDisconnect(ctx); | |
253 | 253 | } |
254 | 254 | }; |
255 | 255 | } | ... | ... |
... | ... | @@ -99,6 +99,10 @@ |
99 | 99 | <groupId>com.google.protobuf</groupId> |
100 | 100 | <artifactId>protobuf-java</artifactId> |
101 | 101 | </dependency> |
102 | + <dependency> | |
103 | + <groupId>com.github.vladimir-bukhtoyarov</groupId> | |
104 | + <artifactId>bucket4j-core</artifactId> | |
105 | + </dependency> | |
102 | 106 | </dependencies> |
103 | 107 | |
104 | 108 | <build> | ... | ... |
... | ... | @@ -43,6 +43,8 @@ public interface TransportService { |
43 | 43 | void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg msg, |
44 | 44 | TransportServiceCallback<TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg> callback); |
45 | 45 | |
46 | + boolean checkLimits(SessionInfoProto sessionInfo, TransportServiceCallback<Void> callback); | |
47 | + | |
46 | 48 | void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback); |
47 | 49 | |
48 | 50 | void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback); | ... | ... |
... | ... | @@ -16,8 +16,13 @@ |
16 | 16 | package org.thingsboard.server.common.transport.service; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.springframework.beans.factory.annotation.Value; | |
20 | +import org.thingsboard.server.common.data.EntityType; | |
21 | +import org.thingsboard.server.common.data.id.DeviceId; | |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
19 | 23 | import org.thingsboard.server.common.transport.SessionMsgListener; |
20 | 24 | import org.thingsboard.server.common.transport.TransportService; |
25 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | |
21 | 26 | import org.thingsboard.server.gen.transport.TransportProtos; |
22 | 27 | |
23 | 28 | import java.util.UUID; |
... | ... | @@ -36,9 +41,20 @@ import java.util.concurrent.TimeUnit; |
36 | 41 | @Slf4j |
37 | 42 | public abstract class AbstractTransportService implements TransportService { |
38 | 43 | |
44 | + @Value("${transport.rate_limits.enabled}") | |
45 | + private boolean rateLimitEnabled; | |
46 | + @Value("${transport.rate_limits.tenant}") | |
47 | + private String perTenantLimitsConf; | |
48 | + @Value("${transport.rate_limits.tenant}") | |
49 | + private String perDevicesLimitsConf; | |
50 | + | |
39 | 51 | protected ScheduledExecutorService schedulerExecutor; |
40 | 52 | protected ExecutorService transportCallbackExecutor; |
41 | - protected ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>(); | |
53 | + private ConcurrentMap<UUID, SessionMetaData> sessions = new ConcurrentHashMap<>(); | |
54 | + | |
55 | + //TODO: Implement cleanup of this maps. | |
56 | + private ConcurrentMap<TenantId, TbTransportRateLimits> perTenantLimits = new ConcurrentHashMap<>(); | |
57 | + private ConcurrentMap<DeviceId, TbTransportRateLimits> perDeviceLimits = new ConcurrentHashMap<>(); | |
42 | 58 | |
43 | 59 | @Override |
44 | 60 | public void registerAsyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener) { |
... | ... | @@ -53,7 +69,6 @@ public abstract class AbstractTransportService implements TransportService { |
53 | 69 | listener.onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto.getDefaultInstance()); |
54 | 70 | deregisterSession(sessionInfo); |
55 | 71 | }, timeout, TimeUnit.MILLISECONDS); |
56 | - | |
57 | 72 | } |
58 | 73 | |
59 | 74 | @Override |
... | ... | @@ -61,6 +76,30 @@ public abstract class AbstractTransportService implements TransportService { |
61 | 76 | sessions.remove(toId(sessionInfo)); |
62 | 77 | } |
63 | 78 | |
79 | + @Override | |
80 | + public boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, TransportServiceCallback<Void> callback) { | |
81 | + if (!rateLimitEnabled) { | |
82 | + return true; | |
83 | + } | |
84 | + TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); | |
85 | + TbTransportRateLimits rateLimits = perTenantLimits.computeIfAbsent(tenantId, id -> new TbTransportRateLimits(perTenantLimitsConf)); | |
86 | + if (!rateLimits.tryConsume()) { | |
87 | + if (callback != null) { | |
88 | + callback.onError(new TbRateLimitsException(EntityType.TENANT)); | |
89 | + } | |
90 | + return false; | |
91 | + } | |
92 | + DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); | |
93 | + rateLimits = perDeviceLimits.computeIfAbsent(deviceId, id -> new TbTransportRateLimits(perDevicesLimitsConf)); | |
94 | + if (!rateLimits.tryConsume()) { | |
95 | + if (callback != null) { | |
96 | + callback.onError(new TbRateLimitsException(EntityType.DEVICE)); | |
97 | + } | |
98 | + return false; | |
99 | + } | |
100 | + return true; | |
101 | + } | |
102 | + | |
64 | 103 | protected void processToTransportMsg(TransportProtos.DeviceActorToTransportMsg toSessionMsg) { |
65 | 104 | UUID sessionId = new UUID(toSessionMsg.getSessionIdMSB(), toSessionMsg.getSessionIdLSB()); |
66 | 105 | SessionMetaData md = sessions.get(sessionId); |
... | ... | @@ -101,11 +140,20 @@ public abstract class AbstractTransportService implements TransportService { |
101 | 140 | } |
102 | 141 | |
103 | 142 | public void init() { |
143 | + if (rateLimitEnabled) { | |
144 | + //Just checking the configuration parameters | |
145 | + new TbTransportRateLimits(perTenantLimitsConf); | |
146 | + new TbTransportRateLimits(perDevicesLimitsConf); | |
147 | + } | |
104 | 148 | this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(); |
105 | 149 | this.transportCallbackExecutor = new ThreadPoolExecutor(0, 20, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); |
106 | 150 | } |
107 | 151 | |
108 | 152 | public void destroy() { |
153 | + if (rateLimitEnabled) { | |
154 | + perTenantLimits.clear(); | |
155 | + perDeviceLimits.clear(); | |
156 | + } | |
109 | 157 | if (schedulerExecutor != null) { |
110 | 158 | schedulerExecutor.shutdownNow(); |
111 | 159 | } | ... | ... |
... | ... | @@ -218,74 +218,90 @@ public class RemoteTransportService extends AbstractTransportService { |
218 | 218 | |
219 | 219 | @Override |
220 | 220 | public void process(SessionInfoProto sessionInfo, SessionEventMsg msg, TransportServiceCallback<Void> callback) { |
221 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
222 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
223 | - .setSessionEvent(msg).build() | |
224 | - ).build(); | |
225 | - send(sessionInfo, toRuleEngineMsg, callback); | |
221 | + if (checkLimits(sessionInfo, callback)) { | |
222 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
223 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
224 | + .setSessionEvent(msg).build() | |
225 | + ).build(); | |
226 | + send(sessionInfo, toRuleEngineMsg, callback); | |
227 | + } | |
226 | 228 | } |
227 | 229 | |
228 | 230 | @Override |
229 | 231 | public void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback<Void> callback) { |
230 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
231 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
232 | - .setPostTelemetry(msg).build() | |
233 | - ).build(); | |
234 | - send(sessionInfo, toRuleEngineMsg, callback); | |
232 | + if (checkLimits(sessionInfo, callback)) { | |
233 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
234 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
235 | + .setPostTelemetry(msg).build() | |
236 | + ).build(); | |
237 | + send(sessionInfo, toRuleEngineMsg, callback); | |
238 | + } | |
235 | 239 | } |
236 | 240 | |
237 | 241 | @Override |
238 | 242 | public void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback<Void> callback) { |
239 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
240 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
241 | - .setPostAttributes(msg).build() | |
242 | - ).build(); | |
243 | - send(sessionInfo, toRuleEngineMsg, callback); | |
243 | + if (checkLimits(sessionInfo, callback)) { | |
244 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
245 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
246 | + .setPostAttributes(msg).build() | |
247 | + ).build(); | |
248 | + send(sessionInfo, toRuleEngineMsg, callback); | |
249 | + } | |
244 | 250 | } |
245 | 251 | |
246 | 252 | @Override |
247 | 253 | public void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback<Void> callback) { |
248 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
249 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
250 | - .setGetAttributes(msg).build() | |
251 | - ).build(); | |
252 | - send(sessionInfo, toRuleEngineMsg, callback); | |
254 | + if (checkLimits(sessionInfo, callback)) { | |
255 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
256 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
257 | + .setGetAttributes(msg).build() | |
258 | + ).build(); | |
259 | + send(sessionInfo, toRuleEngineMsg, callback); | |
260 | + } | |
253 | 261 | } |
254 | 262 | |
255 | 263 | @Override |
256 | 264 | public void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback<Void> callback) { |
257 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
258 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
259 | - .setSubscribeToAttributes(msg).build() | |
260 | - ).build(); | |
261 | - send(sessionInfo, toRuleEngineMsg, callback); | |
265 | + if (checkLimits(sessionInfo, callback)) { | |
266 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
267 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
268 | + .setSubscribeToAttributes(msg).build() | |
269 | + ).build(); | |
270 | + send(sessionInfo, toRuleEngineMsg, callback); | |
271 | + } | |
262 | 272 | } |
263 | 273 | |
264 | 274 | @Override |
265 | 275 | public void process(SessionInfoProto sessionInfo, SubscribeToRPCMsg msg, TransportServiceCallback<Void> callback) { |
266 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
267 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
268 | - .setSubscribeToRPC(msg).build() | |
269 | - ).build(); | |
270 | - send(sessionInfo, toRuleEngineMsg, callback); | |
276 | + if (checkLimits(sessionInfo, callback)) { | |
277 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
278 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
279 | + .setSubscribeToRPC(msg).build() | |
280 | + ).build(); | |
281 | + send(sessionInfo, toRuleEngineMsg, callback); | |
282 | + } | |
271 | 283 | } |
272 | 284 | |
273 | 285 | @Override |
274 | 286 | public void process(SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg msg, TransportServiceCallback<Void> callback) { |
275 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
276 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
277 | - .setToDeviceRPCCallResponse(msg).build() | |
278 | - ).build(); | |
279 | - send(sessionInfo, toRuleEngineMsg, callback); | |
287 | + if (checkLimits(sessionInfo, callback)) { | |
288 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
289 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
290 | + .setToDeviceRPCCallResponse(msg).build() | |
291 | + ).build(); | |
292 | + send(sessionInfo, toRuleEngineMsg, callback); | |
293 | + } | |
280 | 294 | } |
281 | 295 | |
282 | 296 | @Override |
283 | 297 | public void process(SessionInfoProto sessionInfo, ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback) { |
284 | - ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
285 | - TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
286 | - .setToServerRPCCallRequest(msg).build() | |
287 | - ).build(); | |
288 | - send(sessionInfo, toRuleEngineMsg, callback); | |
298 | + if (checkLimits(sessionInfo, callback)) { | |
299 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | |
300 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | |
301 | + .setToServerRPCCallRequest(msg).build() | |
302 | + ).build(); | |
303 | + send(sessionInfo, toRuleEngineMsg, callback); | |
304 | + } | |
289 | 305 | } |
290 | 306 | |
291 | 307 | private static class TransportCallbackAdaptor implements Callback { | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.transport.service; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.EntityType; | |
19 | + | |
20 | +/** | |
21 | + * Created by ashvayka on 22.10.18. | |
22 | + */ | |
23 | +public class TbRateLimitsException extends Exception { | |
24 | + private final EntityType entityType; | |
25 | + | |
26 | + TbRateLimitsException(EntityType entityType) { | |
27 | + this.entityType = entityType; | |
28 | + } | |
29 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.transport.service; | |
17 | + | |
18 | +import io.github.bucket4j.Bandwidth; | |
19 | +import io.github.bucket4j.Bucket4j; | |
20 | +import io.github.bucket4j.local.LocalBucket; | |
21 | +import io.github.bucket4j.local.LocalBucketBuilder; | |
22 | + | |
23 | +import java.time.Duration; | |
24 | + | |
25 | +/** | |
26 | + * Created by ashvayka on 22.10.18. | |
27 | + */ | |
28 | +class TbTransportRateLimits { | |
29 | + private final LocalBucket bucket; | |
30 | + | |
31 | + public TbTransportRateLimits(String limitsConfiguration) { | |
32 | + LocalBucketBuilder builder = Bucket4j.builder(); | |
33 | + boolean initialized = false; | |
34 | + for (String limitSrc : limitsConfiguration.split(",")) { | |
35 | + long capacity = Long.parseLong(limitSrc.split(":")[0]); | |
36 | + long duration = Long.parseLong(limitSrc.split(":")[1]); | |
37 | + builder.addLimit(Bandwidth.simple(capacity, Duration.ofSeconds(duration))); | |
38 | + initialized = true; | |
39 | + } | |
40 | + if (initialized) { | |
41 | + bucket = builder.build(); | |
42 | + } else { | |
43 | + throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration); | |
44 | + } | |
45 | + | |
46 | + | |
47 | + } | |
48 | + | |
49 | + boolean tryConsume() { | |
50 | + return bucket.tryConsume(1); | |
51 | + } | |
52 | + | |
53 | +} | ... | ... |
... | ... | @@ -82,6 +82,7 @@ |
82 | 82 | <elasticsearch.version>5.0.2</elasticsearch.version> |
83 | 83 | <delight-nashorn-sandbox.version>0.1.14</delight-nashorn-sandbox.version> |
84 | 84 | <kafka.version>2.0.0</kafka.version> |
85 | + <bucket4j.version>4.1.1</bucket4j.version> | |
85 | 86 | </properties> |
86 | 87 | |
87 | 88 | <modules> |
... | ... | @@ -778,6 +779,11 @@ |
778 | 779 | <artifactId>delight-nashorn-sandbox</artifactId> |
779 | 780 | <version>${delight-nashorn-sandbox.version}</version> |
780 | 781 | </dependency> |
782 | + <dependency> | |
783 | + <groupId>com.github.vladimir-bukhtoyarov</groupId> | |
784 | + <artifactId>bucket4j-core</artifactId> | |
785 | + <version>${bucket4j.version}</version> | |
786 | + </dependency> | |
781 | 787 | </dependencies> |
782 | 788 | </dependencyManagement> |
783 | 789 | ... | ... |
... | ... | @@ -25,7 +25,7 @@ transport: |
25 | 25 | adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" |
26 | 26 | timeout: "${MQTT_TIMEOUT:10000}" |
27 | 27 | netty: |
28 | - leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}" | |
28 | + leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" | |
29 | 29 | boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}" |
30 | 30 | worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}" |
31 | 31 | max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}" |
... | ... | @@ -43,6 +43,13 @@ transport: |
43 | 43 | key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}" |
44 | 44 | # Type of the key store |
45 | 45 | key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}" |
46 | + sessions: | |
47 | + max_per_tenant: "${TB_TRANSPORT_SESSIONS_MAX_PER_TENANT:1000}" | |
48 | + max_per_device: "${TB_TRANSPORT_SESSIONS_MAX_PER_DEVICE:2}" | |
49 | + rate_limits: | |
50 | + enabled: "${TB_TRANSPORT_RATE_LIMITS_ENABLED:false}" | |
51 | + tenant: "${TB_TRANSPORT_RATE_LIMITS_TENANT:1000:1,20000:60}" | |
52 | + device: "${TB_TRANSPORT_RATE_LIMITS_DEVICE:10:1,300:60}" | |
46 | 53 | |
47 | 54 | #Quota parameters |
48 | 55 | quota: | ... | ... |