Commit b25ea1e6136b0a6b45b047f0ec5b663c3787ff42
Committed by
Andrew Shvayka
1 parent
85b63c5b
rest api call node added parallel processing logic
Showing
2 changed files
with
51 additions
and
7 deletions
... | ... | @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.springframework.http.HttpEntity; |
24 | 24 | import org.springframework.http.HttpHeaders; |
25 | 25 | import org.springframework.http.HttpMethod; |
26 | +import org.springframework.http.HttpStatus; | |
26 | 27 | import org.springframework.http.ResponseEntity; |
27 | 28 | import org.springframework.http.client.Netty4ClientHttpRequestFactory; |
28 | 29 | import org.springframework.util.concurrent.ListenableFuture; |
... | ... | @@ -37,6 +38,8 @@ import org.thingsboard.server.common.msg.TbMsg; |
37 | 38 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
38 | 39 | |
39 | 40 | import javax.net.ssl.SSLException; |
41 | +import java.util.Deque; | |
42 | +import java.util.concurrent.ConcurrentLinkedDeque; | |
40 | 43 | import java.util.concurrent.TimeUnit; |
41 | 44 | |
42 | 45 | @Data |
... | ... | @@ -50,21 +53,24 @@ class TbHttpClient { |
50 | 53 | private static final String ERROR_BODY = "error_body"; |
51 | 54 | |
52 | 55 | private final TbRestApiCallNodeConfiguration config; |
53 | - private final boolean useRedisQueueForMsgPersistence; | |
54 | 56 | |
55 | 57 | private EventLoopGroup eventLoopGroup; |
56 | 58 | private AsyncRestTemplate httpClient; |
59 | + private Deque<ListenableFuture<ResponseEntity<String>>> pendingFutures; | |
57 | 60 | |
58 | 61 | TbHttpClient(TbRestApiCallNodeConfiguration config) throws TbNodeException { |
59 | 62 | try { |
60 | 63 | this.config = config; |
61 | - this.useRedisQueueForMsgPersistence = config.isUseRedisQueueForMsgPersistence(); | |
64 | + if (config.getMaxParallelRequestsCount() > 0) { | |
65 | + pendingFutures = new ConcurrentLinkedDeque<>(); | |
66 | + } | |
62 | 67 | if (config.isUseSimpleClientHttpFactory()) { |
63 | 68 | httpClient = new AsyncRestTemplate(); |
64 | 69 | } else { |
65 | 70 | this.eventLoopGroup = new NioEventLoopGroup(); |
66 | 71 | Netty4ClientHttpRequestFactory nettyFactory = new Netty4ClientHttpRequestFactory(this.eventLoopGroup); |
67 | 72 | nettyFactory.setSslContext(SslContextBuilder.forClient().build()); |
73 | + nettyFactory.setReadTimeout(config.getReadTimeoutMs()); | |
68 | 74 | httpClient = new AsyncRestTemplate(nettyFactory); |
69 | 75 | } |
70 | 76 | } catch (SSLException e) { |
... | ... | @@ -89,8 +95,12 @@ class TbHttpClient { |
89 | 95 | future.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() { |
90 | 96 | @Override |
91 | 97 | public void onFailure(Throwable throwable) { |
92 | - if (useRedisQueueForMsgPersistence) { | |
93 | - queueProcessor.pushOnFailure(msg); | |
98 | + if (config.isUseRedisQueueForMsgPersistence()) { | |
99 | + if (throwable instanceof HttpClientErrorException) { | |
100 | + processHttpClientError(((HttpClientErrorException) throwable).getStatusCode(), msg, queueProcessor); | |
101 | + } else { | |
102 | + queueProcessor.pushOnFailure(msg); | |
103 | + } | |
94 | 104 | } |
95 | 105 | TbMsg next = processException(ctx, msg, throwable); |
96 | 106 | ctx.tellFailure(next, throwable); |
... | ... | @@ -99,20 +109,23 @@ class TbHttpClient { |
99 | 109 | @Override |
100 | 110 | public void onSuccess(ResponseEntity<String> responseEntity) { |
101 | 111 | if (responseEntity.getStatusCode().is2xxSuccessful()) { |
102 | - if (useRedisQueueForMsgPersistence) { | |
112 | + if (config.isUseRedisQueueForMsgPersistence()) { | |
103 | 113 | queueProcessor.resetCounter(); |
104 | 114 | } |
105 | 115 | TbMsg next = processResponse(ctx, msg, responseEntity); |
106 | 116 | ctx.tellNext(next, TbRelationTypes.SUCCESS); |
107 | 117 | } else { |
108 | - if (useRedisQueueForMsgPersistence) { | |
109 | - queueProcessor.pushOnFailure(msg); | |
118 | + if (config.isUseRedisQueueForMsgPersistence()) { | |
119 | + processHttpClientError(responseEntity.getStatusCode(), msg, queueProcessor); | |
110 | 120 | } |
111 | 121 | TbMsg next = processFailureResponse(ctx, msg, responseEntity); |
112 | 122 | ctx.tellNext(next, TbRelationTypes.FAILURE); |
113 | 123 | } |
114 | 124 | } |
115 | 125 | }); |
126 | + if (pendingFutures != null) { | |
127 | + processParallelRequests(future); | |
128 | + } | |
116 | 129 | } |
117 | 130 | |
118 | 131 | private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity<String> response) { |
... | ... | @@ -150,4 +163,31 @@ class TbHttpClient { |
150 | 163 | config.getHeaders().forEach((k, v) -> headers.add(TbNodeUtils.processPattern(k, metaData), TbNodeUtils.processPattern(v, metaData))); |
151 | 164 | return headers; |
152 | 165 | } |
166 | + | |
167 | + private void processParallelRequests(ListenableFuture<ResponseEntity<String>> future) { | |
168 | + pendingFutures.add(future); | |
169 | + if (pendingFutures.size() > config.getMaxParallelRequestsCount()) { | |
170 | + for (int i = 0; i < config.getMaxParallelRequestsCount(); i++) { | |
171 | + try { | |
172 | + ListenableFuture<ResponseEntity<String>> pendingFuture = pendingFutures.removeFirst(); | |
173 | + try { | |
174 | + pendingFuture.get(config.getReadTimeoutMs(), TimeUnit.MILLISECONDS); | |
175 | + } catch (Exception e) { | |
176 | + log.warn("Timeout during waiting for reply!", e); | |
177 | + pendingFuture.cancel(true); | |
178 | + } | |
179 | + } catch (Exception e) { | |
180 | + log.warn("Failure during waiting for reply!", e); | |
181 | + } | |
182 | + } | |
183 | + } | |
184 | + } | |
185 | + | |
186 | + private void processHttpClientError(HttpStatus statusCode, TbMsg msg, TbRedisQueueProcessor queueProcessor) { | |
187 | + if (statusCode.is4xxClientError()) { | |
188 | + log.warn("[{}] Client error during message delivering!", msg); | |
189 | + } else { | |
190 | + queueProcessor.pushOnFailure(msg); | |
191 | + } | |
192 | + } | |
153 | 193 | } | ... | ... |
... | ... | @@ -28,6 +28,8 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA |
28 | 28 | private String requestMethod; |
29 | 29 | private Map<String, String> headers; |
30 | 30 | private boolean useSimpleClientHttpFactory; |
31 | + private int readTimeoutMs; | |
32 | + private int maxParallelRequestsCount; | |
31 | 33 | private boolean useRedisQueueForMsgPersistence; |
32 | 34 | private boolean trimQueue; |
33 | 35 | private int maxQueueSize; |
... | ... | @@ -39,6 +41,8 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration<TbRestA |
39 | 41 | configuration.setRequestMethod("POST"); |
40 | 42 | configuration.setHeaders(Collections.emptyMap()); |
41 | 43 | configuration.setUseSimpleClientHttpFactory(false); |
44 | + configuration.setReadTimeoutMs(0); | |
45 | + configuration.setMaxParallelRequestsCount(0); | |
42 | 46 | configuration.setUseRedisQueueForMsgPersistence(false); |
43 | 47 | configuration.setTrimQueue(false); |
44 | 48 | return configuration; | ... | ... |