Commit c7f282d39385473928f4111d015bc02ce30b07bb
1 parent
3d54384b
Refactoring of the Queue Consumers
Showing
2 changed files
with
159 additions
and
83 deletions
common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2020 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.queue.common; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; | |
21 | +import org.thingsboard.server.queue.TbQueueConsumer; | |
22 | +import org.thingsboard.server.queue.TbQueueMsg; | |
23 | + | |
24 | +import java.io.IOException; | |
25 | +import java.util.ArrayList; | |
26 | +import java.util.Collections; | |
27 | +import java.util.List; | |
28 | +import java.util.Set; | |
29 | +import java.util.concurrent.locks.Lock; | |
30 | +import java.util.concurrent.locks.ReentrantLock; | |
31 | + | |
32 | +@Slf4j | |
33 | +public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> implements TbQueueConsumer<T> { | |
34 | + | |
35 | + private volatile boolean subscribed; | |
36 | + protected volatile Set<TopicPartitionInfo> partitions; | |
37 | + protected final Lock consumerLock = new ReentrantLock(); | |
38 | + | |
39 | + @Getter | |
40 | + private final String topic; | |
41 | + | |
42 | + public AbstractTbQueueConsumerTemplate(String topic) { | |
43 | + this.topic = topic; | |
44 | + } | |
45 | + | |
46 | + @Override | |
47 | + public void subscribe() { | |
48 | + consumerLock.lock(); | |
49 | + try { | |
50 | + partitions = Collections.singleton(new TopicPartitionInfo(topic, null, null, true)); | |
51 | + subscribed = false; | |
52 | + } finally { | |
53 | + consumerLock.unlock(); | |
54 | + } | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + public void subscribe(Set<TopicPartitionInfo> partitions) { | |
59 | + consumerLock.lock(); | |
60 | + try { | |
61 | + this.partitions = partitions; | |
62 | + subscribed = false; | |
63 | + } finally { | |
64 | + consumerLock.unlock(); | |
65 | + } | |
66 | + } | |
67 | + | |
68 | + @Override | |
69 | + public List<T> poll(long durationInMillis) { | |
70 | + if (!subscribed && partitions == null) { | |
71 | + try { | |
72 | + Thread.sleep(durationInMillis); | |
73 | + } catch (InterruptedException e) { | |
74 | + log.debug("Failed to await subscription", e); | |
75 | + } | |
76 | + } else { | |
77 | + consumerLock.lock(); | |
78 | + try { | |
79 | + if (!subscribed) { | |
80 | + doSubscribe(); | |
81 | + subscribed = true; | |
82 | + } | |
83 | + | |
84 | + List<R> records = doPoll(durationInMillis); | |
85 | + if (!records.isEmpty()) { | |
86 | + List<T> result = new ArrayList<>(records.size()); | |
87 | + records.forEach(record -> { | |
88 | + try { | |
89 | + if (record != null) { | |
90 | + result.add(decode(record)); | |
91 | + } | |
92 | + } catch (IOException e) { | |
93 | + log.error("Failed decode record: [{}]", record); | |
94 | + throw new RuntimeException("Failed to decode record: ", e); | |
95 | + } | |
96 | + }); | |
97 | + return result; | |
98 | + } | |
99 | + } finally { | |
100 | + consumerLock.unlock(); | |
101 | + } | |
102 | + } | |
103 | + return Collections.emptyList(); | |
104 | + } | |
105 | + | |
106 | + @Override | |
107 | + public void commit() { | |
108 | + consumerLock.lock(); | |
109 | + try { | |
110 | + doCommit(); | |
111 | + } finally { | |
112 | + consumerLock.unlock(); | |
113 | + } | |
114 | + } | |
115 | + | |
116 | + @Override | |
117 | + public void unsubscribe() { | |
118 | + consumerLock.lock(); | |
119 | + try { | |
120 | + doUnsubscribe(); | |
121 | + } finally { | |
122 | + consumerLock.unlock(); | |
123 | + } | |
124 | + } | |
125 | + | |
126 | + abstract protected List<R> doPoll(long durationInMillis); | |
127 | + | |
128 | + abstract protected T decode(R record) throws IOException; | |
129 | + | |
130 | + abstract protected void doSubscribe(); | |
131 | + | |
132 | + abstract protected void doCommit(); | |
133 | + | |
134 | + abstract protected void doUnsubscribe(); | |
135 | + | |
136 | +} | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.queue.kafka; |
17 | 17 | |
18 | 18 | import lombok.Builder; |
19 | -import lombok.Getter; | |
20 | 19 | import lombok.extern.slf4j.Slf4j; |
21 | 20 | import org.apache.kafka.clients.consumer.ConsumerConfig; |
22 | 21 | import org.apache.kafka.clients.consumer.ConsumerRecord; |
... | ... | @@ -24,8 +23,8 @@ import org.apache.kafka.clients.consumer.ConsumerRecords; |
24 | 23 | import org.apache.kafka.clients.consumer.KafkaConsumer; |
25 | 24 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
26 | 25 | import org.thingsboard.server.queue.TbQueueAdmin; |
27 | -import org.thingsboard.server.queue.TbQueueConsumer; | |
28 | 26 | import org.thingsboard.server.queue.TbQueueMsg; |
27 | +import org.thingsboard.server.queue.common.AbstractTbQueueConsumerTemplate; | |
29 | 28 | |
30 | 29 | import java.io.IOException; |
31 | 30 | import java.time.Duration; |
... | ... | @@ -33,26 +32,17 @@ import java.util.ArrayList; |
33 | 32 | import java.util.Collections; |
34 | 33 | import java.util.List; |
35 | 34 | import java.util.Properties; |
36 | -import java.util.Set; | |
37 | -import java.util.concurrent.locks.Lock; | |
38 | -import java.util.concurrent.locks.ReentrantLock; | |
39 | 35 | import java.util.stream.Collectors; |
40 | 36 | |
41 | 37 | /** |
42 | 38 | * Created by ashvayka on 24.09.18. |
43 | 39 | */ |
44 | 40 | @Slf4j |
45 | -public class TbKafkaConsumerTemplate<T extends TbQueueMsg> implements TbQueueConsumer<T> { | |
41 | +public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQueueConsumerTemplate<ConsumerRecord<String, byte[]>, T> { | |
46 | 42 | |
47 | 43 | private final TbQueueAdmin admin; |
48 | 44 | private final KafkaConsumer<String, byte[]> consumer; |
49 | 45 | private final TbKafkaDecoder<T> decoder; |
50 | - private volatile boolean subscribed; | |
51 | - private volatile Set<TopicPartitionInfo> partitions; | |
52 | - private final Lock consumerLock; | |
53 | - | |
54 | - @Getter | |
55 | - private final String topic; | |
56 | 46 | |
57 | 47 | @Builder |
58 | 48 | private TbKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder, |
... | ... | @@ -60,6 +50,7 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> implements TbQueueCon |
60 | 50 | boolean autoCommit, int autoCommitIntervalMs, |
61 | 51 | int maxPollRecords, |
62 | 52 | TbQueueAdmin admin) { |
53 | + super(topic); | |
63 | 54 | Properties props = settings.toProps(); |
64 | 55 | props.put(ConsumerConfig.CLIENT_ID_CONFIG, clientId); |
65 | 56 | if (groupId != null) { |
... | ... | @@ -75,94 +66,43 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> implements TbQueueCon |
75 | 66 | this.admin = admin; |
76 | 67 | this.consumer = new KafkaConsumer<>(props); |
77 | 68 | this.decoder = decoder; |
78 | - this.topic = topic; | |
79 | - this.consumerLock = new ReentrantLock(); | |
80 | 69 | } |
81 | 70 | |
82 | 71 | @Override |
83 | - public void subscribe() { | |
84 | - consumerLock.lock(); | |
85 | - try { | |
86 | - partitions = Collections.singleton(new TopicPartitionInfo(topic, null, null, true)); | |
87 | - subscribed = false; | |
88 | - } finally { | |
89 | - consumerLock.unlock(); | |
90 | - } | |
72 | + protected void doSubscribe() { | |
73 | + List<String> topicNames = partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); | |
74 | + topicNames.forEach(admin::createTopicIfNotExists); | |
75 | + consumer.subscribe(topicNames); | |
91 | 76 | } |
92 | 77 | |
93 | 78 | @Override |
94 | - public void subscribe(Set<TopicPartitionInfo> partitions) { | |
95 | - consumerLock.lock(); | |
96 | - try { | |
97 | - this.partitions = partitions; | |
98 | - subscribed = false; | |
99 | - } finally { | |
100 | - consumerLock.unlock(); | |
79 | + protected List<ConsumerRecord<String, byte[]>> doPoll(long durationInMillis) { | |
80 | + ConsumerRecords<String, byte[]> records = consumer.poll(Duration.ofMillis(durationInMillis)); | |
81 | + if (records.isEmpty()) { | |
82 | + return Collections.emptyList(); | |
83 | + } else { | |
84 | + List<ConsumerRecord<String, byte[]>> recordList = new ArrayList<>(256); | |
85 | + records.forEach(recordList::add); | |
86 | + return recordList; | |
101 | 87 | } |
102 | 88 | } |
103 | 89 | |
104 | 90 | @Override |
105 | - public List<T> poll(long durationInMillis) { | |
106 | - if (!subscribed && partitions == null) { | |
107 | - try { | |
108 | - Thread.sleep(durationInMillis); | |
109 | - } catch (InterruptedException e) { | |
110 | - log.debug("Failed to await subscription", e); | |
111 | - } | |
112 | - } else { | |
113 | - consumerLock.lock(); | |
114 | - try { | |
115 | - if (!subscribed) { | |
116 | - List<String> topicNames = partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); | |
117 | - topicNames.forEach(admin::createTopicIfNotExists); | |
118 | - consumer.subscribe(topicNames); | |
119 | - subscribed = true; | |
120 | - } | |
121 | - | |
122 | - ConsumerRecords<String, byte[]> records = consumer.poll(Duration.ofMillis(durationInMillis)); | |
123 | - if (records.count() > 0) { | |
124 | - List<T> result = new ArrayList<>(); | |
125 | - records.forEach(record -> { | |
126 | - try { | |
127 | - result.add(decode(record)); | |
128 | - } catch (IOException e) { | |
129 | - log.error("Failed decode record: [{}]", record); | |
130 | - } | |
131 | - }); | |
132 | - return result; | |
133 | - } | |
134 | - } finally { | |
135 | - consumerLock.unlock(); | |
136 | - } | |
137 | - } | |
138 | - return Collections.emptyList(); | |
91 | + public T decode(ConsumerRecord<String, byte[]> record) throws IOException { | |
92 | + return decoder.decode(new KafkaTbQueueMsg(record)); | |
139 | 93 | } |
140 | 94 | |
141 | 95 | @Override |
142 | - public void commit() { | |
143 | - consumerLock.lock(); | |
144 | - try { | |
145 | - consumer.commitAsync(); | |
146 | - } finally { | |
147 | - consumerLock.unlock(); | |
148 | - } | |
96 | + protected void doCommit() { | |
97 | + consumer.commitAsync(); | |
149 | 98 | } |
150 | 99 | |
151 | 100 | @Override |
152 | - public void unsubscribe() { | |
153 | - consumerLock.lock(); | |
154 | - try { | |
155 | - if (consumer != null) { | |
156 | - consumer.unsubscribe(); | |
157 | - consumer.close(); | |
158 | - } | |
159 | - } finally { | |
160 | - consumerLock.unlock(); | |
101 | + protected void doUnsubscribe() { | |
102 | + if (consumer != null) { | |
103 | + consumer.unsubscribe(); | |
104 | + consumer.close(); | |
161 | 105 | } |
162 | 106 | } |
163 | 107 | |
164 | - public T decode(ConsumerRecord<String, byte[]> record) throws IOException { | |
165 | - return decoder.decode(new KafkaTbQueueMsg(record)); | |
166 | - } | |
167 | - | |
168 | 108 | } | ... | ... |