created Aws Sqs Queue (#2534)
* created Aws Sqs Queue * improvement AwsSqs providers * revert package-lock.json * Aws sqs improvements * Aws sqs improvements * Aws sqs improvements * Aws sqs improvements * aws improvements * aws improvements * aws improvements * added visibility timeout to aws queue
This commit is contained in:
		
							parent
							
								
									c4269023dd
								
							
						
					
					
						commit
						2553cf6b6f
					
				@ -88,7 +88,6 @@ public class RpcController extends BaseController {
 | 
			
		||||
        return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
 | 
			
		||||
        try {
 | 
			
		||||
            JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
 | 
			
		||||
 | 
			
		||||
@ -136,6 +136,15 @@ public abstract class AbstractConsumerService<T extends com.google.protobuf.Gene
 | 
			
		||||
    @PreDestroy
 | 
			
		||||
    public void destroy() {
 | 
			
		||||
        stopped = true;
 | 
			
		||||
 | 
			
		||||
        if (mainConsumer != null) {
 | 
			
		||||
            mainConsumer.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nfConsumer != null) {
 | 
			
		||||
            nfConsumer.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mainConsumerExecutor != null) {
 | 
			
		||||
            mainConsumerExecutor.shutdownNow();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -517,7 +517,7 @@ swagger:
 | 
			
		||||
  version: "${SWAGGER_VERSION:2.0}"
 | 
			
		||||
 | 
			
		||||
queue:
 | 
			
		||||
  type: "${TB_QUEUE_TYPE:in-memory}" # kafka or in-memory
 | 
			
		||||
  type: "${TB_QUEUE_TYPE:in-memory}" # kafka or in-memory or aws-sqs
 | 
			
		||||
  kafka:
 | 
			
		||||
    bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
 | 
			
		||||
    acks: "${TB_KAFKA_ACKS:all}"
 | 
			
		||||
@ -525,6 +525,12 @@ queue:
 | 
			
		||||
    batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
 | 
			
		||||
    linger.ms: "${TB_KAFKA_LINGER_MS:1}"
 | 
			
		||||
    buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
 | 
			
		||||
  aws_sqs:
 | 
			
		||||
    access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
 | 
			
		||||
    secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
 | 
			
		||||
    region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
 | 
			
		||||
    threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}"
 | 
			
		||||
    visibility_timeout: "${TB_QUEUE_AWS_SQS_VISIBILITY_TIMEOUT:30}" #in seconds
 | 
			
		||||
  partitions:
 | 
			
		||||
    hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
 | 
			
		||||
    virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,11 @@
 | 
			
		||||
            <groupId>org.apache.kafka</groupId>
 | 
			
		||||
            <artifactId>kafka-clients</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.amazonaws</groupId>
 | 
			
		||||
            <artifactId>aws-java-sdk-sqs</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework</groupId>
 | 
			
		||||
            <artifactId>spring-context-support</artifactId>
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue;
 | 
			
		||||
 | 
			
		||||
import com.google.protobuf.InvalidProtocolBufferException;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsg;
 | 
			
		||||
 | 
			
		||||
public interface TbQueueMsgDecoder<T extends TbQueueMsg> {
 | 
			
		||||
 | 
			
		||||
    T decode(TbQueueMsg msg) throws InvalidProtocolBufferException;
 | 
			
		||||
}
 | 
			
		||||
@ -25,4 +25,5 @@ public interface TbQueueProducer<T extends TbQueueMsg> {
 | 
			
		||||
 | 
			
		||||
    void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback);
 | 
			
		||||
 | 
			
		||||
    void stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -92,6 +92,8 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
 | 
			
		||||
                    List<Response> responses = responseTemplate.poll(pollInterval);
 | 
			
		||||
                    if (responses.size() > 0) {
 | 
			
		||||
                        log.trace("Polling responses completed, consumer records count [{}]", responses.size());
 | 
			
		||||
                    } else {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    responses.forEach(response -> {
 | 
			
		||||
                        log.trace("Received response to Kafka Template request: {}", response);
 | 
			
		||||
@ -145,6 +147,15 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
 | 
			
		||||
    @Override
 | 
			
		||||
    public void stop() {
 | 
			
		||||
        stopped = true;
 | 
			
		||||
 | 
			
		||||
        if (responseTemplate != null) {
 | 
			
		||||
            responseTemplate.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (requestTemplate != null) {
 | 
			
		||||
            requestTemplate.stop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (internalExecutor) {
 | 
			
		||||
            executor.shutdownNow();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -18,12 +18,12 @@ package org.thingsboard.server.queue.common;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.kafka.common.errors.InterruptException;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueHandler;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueProducer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueResponseTemplate;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
@ -87,6 +87,10 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response
 | 
			
		||||
                    }
 | 
			
		||||
                    List<Request> requests = requestTemplate.poll(pollInterval);
 | 
			
		||||
 | 
			
		||||
                    if (requests.isEmpty()) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    requests.forEach(request -> {
 | 
			
		||||
                        long currentTime = System.currentTimeMillis();
 | 
			
		||||
                        long requestTime = bytesToLong(request.getHeaders().get(REQUEST_TIME));
 | 
			
		||||
@ -147,6 +151,12 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response
 | 
			
		||||
 | 
			
		||||
    public void stop() {
 | 
			
		||||
        stopped = true;
 | 
			
		||||
        if (requestTemplate != null) {
 | 
			
		||||
            requestTemplate.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
        if (responseTemplate != null) {
 | 
			
		||||
            responseTemplate.stop();
 | 
			
		||||
        }
 | 
			
		||||
        if (timeoutExecutor != null) {
 | 
			
		||||
            timeoutExecutor.shutdownNow();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,9 @@ import org.apache.kafka.clients.consumer.ConsumerConfig;
 | 
			
		||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
 | 
			
		||||
import org.apache.kafka.clients.consumer.ConsumerRecords;
 | 
			
		||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
@ -96,7 +96,7 @@ public class TBKafkaConsumerTemplate<T extends TbQueueMsg> implements TbQueueCon
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!subscribed) {
 | 
			
		||||
                List<String> topicNames = partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList());
 | 
			
		||||
                topicNames.forEach(this::createTopicIfNotExists);
 | 
			
		||||
                topicNames.forEach(admin::createTopicIfNotExists);
 | 
			
		||||
                consumer.subscribe(topicNames);
 | 
			
		||||
                subscribed = true;
 | 
			
		||||
            }
 | 
			
		||||
@ -130,7 +130,4 @@ public class TBKafkaConsumerTemplate<T extends TbQueueMsg> implements TbQueueCon
 | 
			
		||||
        return decoder.decode(new KafkaTbQueueMsg(record));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createTopicIfNotExists(String topic) {
 | 
			
		||||
        admin.createTopicIfNotExists(topic);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -84,4 +84,9 @@ public class TBKafkaProducerTemplate<T extends TbQueueMsg> implements TbQueuePro
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void stop() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -50,4 +50,9 @@ public class InMemoryTbQueueProducer<T extends TbQueueMsg> implements TbQueuePro
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void stop() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,127 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.provider;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.ServiceType;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueCoreSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueProducer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueRuleEngineSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueTransportApiSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueTransportNotificationSettings;
 | 
			
		||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.PartitionService;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsSettings;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='monolith'")
 | 
			
		||||
public class AwsSqsMonolithQueueProvider implements TbCoreQueueProvider, TbRuleEngineQueueProvider {
 | 
			
		||||
 | 
			
		||||
    private final PartitionService partitionService;
 | 
			
		||||
    private final TbQueueCoreSettings coreSettings;
 | 
			
		||||
    private final TbServiceInfoProvider serviceInfoProvider;
 | 
			
		||||
    private final TbQueueRuleEngineSettings ruleEngineSettings;
 | 
			
		||||
    private final TbQueueTransportApiSettings transportApiSettings;
 | 
			
		||||
    private final TbQueueTransportNotificationSettings transportNotificationSettings;
 | 
			
		||||
    private final TbAwsSqsSettings sqsSettings;
 | 
			
		||||
    private final TbQueueAdmin admin;
 | 
			
		||||
 | 
			
		||||
    public AwsSqsMonolithQueueProvider(PartitionService partitionService, TbQueueCoreSettings coreSettings,
 | 
			
		||||
                                       TbQueueRuleEngineSettings ruleEngineSettings,
 | 
			
		||||
                                       TbServiceInfoProvider serviceInfoProvider,
 | 
			
		||||
                                       TbQueueTransportApiSettings transportApiSettings,
 | 
			
		||||
                                       TbQueueTransportNotificationSettings transportNotificationSettings,
 | 
			
		||||
                                       TbAwsSqsSettings sqsSettings) {
 | 
			
		||||
        this.partitionService = partitionService;
 | 
			
		||||
        this.coreSettings = coreSettings;
 | 
			
		||||
        this.serviceInfoProvider = serviceInfoProvider;
 | 
			
		||||
        this.ruleEngineSettings = ruleEngineSettings;
 | 
			
		||||
        this.transportApiSettings = transportApiSettings;
 | 
			
		||||
        this.transportNotificationSettings = transportNotificationSettings;
 | 
			
		||||
        this.sqsSettings = sqsSettings;
 | 
			
		||||
        admin = new TbAwsSqsAdmin(sqsSettings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToTransportMsg>> getTransportNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, transportNotificationSettings.getNotificationsTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> getRuleEngineMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, ruleEngineSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> getRuleEngineNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, ruleEngineSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreMsg>> getTbCoreMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> getTbCoreNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> getToRuleEngineMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, ruleEngineSettings.getTopic(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> getToRuleEngineNotificationsMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings,
 | 
			
		||||
                partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToCoreMsg>> getToCoreMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, coreSettings.getTopic(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> getToCoreNotificationsMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings,
 | 
			
		||||
                partitionService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg>> getTransportApiRequestConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, transportApiSettings.getRequestsTopic(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.TransportApiResponseMsg>> getTransportApiResponseProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, transportApiSettings.getResponsesTopic());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,117 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.provider;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.ServiceType;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueCoreSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueProducer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueRuleEngineSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueTransportApiSettings;
 | 
			
		||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.PartitionService;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsSettings;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-core'")
 | 
			
		||||
public class AwsSqsTbCoreQueueProvider implements TbCoreQueueProvider {
 | 
			
		||||
 | 
			
		||||
    private final TbAwsSqsSettings sqsSettings;
 | 
			
		||||
    private final TbQueueRuleEngineSettings ruleEngineSettings;
 | 
			
		||||
    private final TbQueueCoreSettings coreSettings;
 | 
			
		||||
    private final TbQueueTransportApiSettings transportApiSettings;
 | 
			
		||||
    private final PartitionService partitionService;
 | 
			
		||||
    private final TbServiceInfoProvider serviceInfoProvider;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private final TbQueueAdmin admin;
 | 
			
		||||
 | 
			
		||||
    public AwsSqsTbCoreQueueProvider(TbAwsSqsSettings sqsSettings,
 | 
			
		||||
                                     TbQueueCoreSettings coreSettings,
 | 
			
		||||
                                     TbQueueTransportApiSettings transportApiSettings,
 | 
			
		||||
                                     TbQueueRuleEngineSettings ruleEngineSettings,
 | 
			
		||||
                                     PartitionService partitionService,
 | 
			
		||||
                                     TbServiceInfoProvider serviceInfoProvider) {
 | 
			
		||||
        this.sqsSettings = sqsSettings;
 | 
			
		||||
        this.coreSettings = coreSettings;
 | 
			
		||||
        this.transportApiSettings = transportApiSettings;
 | 
			
		||||
        this.ruleEngineSettings = ruleEngineSettings;
 | 
			
		||||
        this.partitionService = partitionService;
 | 
			
		||||
        this.serviceInfoProvider = serviceInfoProvider;
 | 
			
		||||
        this.admin = new TbAwsSqsAdmin(sqsSettings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> getTransportNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> getRuleEngineMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> getRuleEngineNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, ruleEngineSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<ToCoreMsg>> getTbCoreMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> getTbCoreNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> getToCoreMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, coreSettings.getTopic(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> getToCoreNotificationsMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings,
 | 
			
		||||
                partitionService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> getTransportApiRequestConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, transportApiSettings.getRequestsTopic(), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> getTransportApiResponseProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,97 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.provider;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.ServiceType;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueCoreSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueProducer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueRuleEngineSettings;
 | 
			
		||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.PartitionService;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsSettings;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-rule-engine'")
 | 
			
		||||
public class AwsSqsTbRuleEngineQueueProvider implements TbRuleEngineQueueProvider {
 | 
			
		||||
 | 
			
		||||
    private final PartitionService partitionService;
 | 
			
		||||
    private final TbQueueCoreSettings coreSettings;
 | 
			
		||||
    private final TbServiceInfoProvider serviceInfoProvider;
 | 
			
		||||
    private final TbQueueRuleEngineSettings ruleEngineSettings;
 | 
			
		||||
    private final TbAwsSqsSettings sqsSettings;
 | 
			
		||||
    private final TbQueueAdmin admin;
 | 
			
		||||
 | 
			
		||||
    public AwsSqsTbRuleEngineQueueProvider(PartitionService partitionService, TbQueueCoreSettings coreSettings,
 | 
			
		||||
                                           TbQueueRuleEngineSettings ruleEngineSettings,
 | 
			
		||||
                                           TbServiceInfoProvider serviceInfoProvider,
 | 
			
		||||
                                           TbAwsSqsSettings sqsSettings) {
 | 
			
		||||
        this.partitionService = partitionService;
 | 
			
		||||
        this.coreSettings = coreSettings;
 | 
			
		||||
        this.serviceInfoProvider = serviceInfoProvider;
 | 
			
		||||
        this.ruleEngineSettings = ruleEngineSettings;
 | 
			
		||||
        this.sqsSettings = sqsSettings;
 | 
			
		||||
        admin = new TbAwsSqsAdmin(sqsSettings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> getTransportNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<ToRuleEngineMsg>> getRuleEngineMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> getRuleEngineNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, ruleEngineSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<ToCoreMsg>> getTbCoreMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreNotificationMsg>> getTbCoreNotificationsMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, coreSettings.getTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> getToRuleEngineMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, ruleEngineSettings.getTopic(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToRuleEngineNotificationMsg>> getToRuleEngineNotificationsMsgConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings,
 | 
			
		||||
                partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,93 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.provider;
 | 
			
		||||
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.gen.transport.TransportProtos;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueProducer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueRequestTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueTransportApiSettings;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueTransportNotificationSettings;
 | 
			
		||||
import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsConsumerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate;
 | 
			
		||||
import org.thingsboard.server.queue.sqs.TbAwsSqsSettings;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && ('${service.type:null}'=='monolith' || '${service.type:null}'=='tb-transport')")
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class AwsSqsTransportQueueProvider implements TbTransportQueueProvider {
 | 
			
		||||
    private final TbQueueTransportApiSettings transportApiSettings;
 | 
			
		||||
    private final TbQueueTransportNotificationSettings transportNotificationSettings;
 | 
			
		||||
    private final TbAwsSqsSettings sqsSettings;
 | 
			
		||||
    private final TbQueueAdmin admin;
 | 
			
		||||
    private final TbServiceInfoProvider serviceInfoProvider;
 | 
			
		||||
 | 
			
		||||
    public AwsSqsTransportQueueProvider(TbQueueTransportApiSettings transportApiSettings,
 | 
			
		||||
                                        TbQueueTransportNotificationSettings transportNotificationSettings,
 | 
			
		||||
                                        TbAwsSqsSettings sqsSettings,
 | 
			
		||||
                                        TbServiceInfoProvider serviceInfoProvider) {
 | 
			
		||||
        this.transportApiSettings = transportApiSettings;
 | 
			
		||||
        this.transportNotificationSettings = transportNotificationSettings;
 | 
			
		||||
        this.sqsSettings = sqsSettings;
 | 
			
		||||
        admin = new TbAwsSqsAdmin(sqsSettings);
 | 
			
		||||
        this.serviceInfoProvider = serviceInfoProvider;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueRequestTemplate<TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg>, TbProtoQueueMsg<TransportProtos.TransportApiResponseMsg>> getTransportApiRequestTemplate() {
 | 
			
		||||
        TbAwsSqsProducerTemplate<TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg>> producerTemplate =
 | 
			
		||||
                new TbAwsSqsProducerTemplate<>(admin, sqsSettings, transportApiSettings.getRequestsTopic());
 | 
			
		||||
 | 
			
		||||
        TbAwsSqsConsumerTemplate<TbProtoQueueMsg<TransportProtos.TransportApiResponseMsg>> consumerTemplate =
 | 
			
		||||
                new TbAwsSqsConsumerTemplate<>(admin, sqsSettings,
 | 
			
		||||
                        transportApiSettings.getResponsesTopic() + "_" + serviceInfoProvider.getServiceId(),
 | 
			
		||||
                        msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
 | 
			
		||||
        DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder
 | 
			
		||||
                <TbProtoQueueMsg<TransportProtos.TransportApiRequestMsg>, TbProtoQueueMsg<TransportProtos.TransportApiResponseMsg>> templateBuilder = DefaultTbQueueRequestTemplate.builder();
 | 
			
		||||
        templateBuilder.queueAdmin(admin);
 | 
			
		||||
        templateBuilder.requestTemplate(producerTemplate);
 | 
			
		||||
        templateBuilder.responseTemplate(consumerTemplate);
 | 
			
		||||
        templateBuilder.maxPendingRequests(transportApiSettings.getMaxPendingRequests());
 | 
			
		||||
        templateBuilder.maxRequestTimeout(transportApiSettings.getMaxRequestsTimeout());
 | 
			
		||||
        templateBuilder.pollInterval(transportApiSettings.getResponsePollInterval());
 | 
			
		||||
        return templateBuilder.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> getRuleEngineMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, transportApiSettings.getRequestsTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToCoreMsg>> getTbCoreMsgProducer() {
 | 
			
		||||
        return new TbAwsSqsProducerTemplate<>(admin, sqsSettings, transportApiSettings.getRequestsTopic());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToTransportMsg>> getTransportNotificationsConsumer() {
 | 
			
		||||
        return new TbAwsSqsConsumerTemplate<>(admin, sqsSettings, transportNotificationSettings.getNotificationsTopic() + "_" + serviceInfoProvider.getServiceId(),
 | 
			
		||||
                msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.sqs;
 | 
			
		||||
 | 
			
		||||
import com.amazonaws.http.SdkHttpMetadata;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsgMetadata;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class AwsSqsTbQueueMsgMetadata implements TbQueueMsgMetadata {
 | 
			
		||||
 | 
			
		||||
    private final SdkHttpMetadata metadata;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,65 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.sqs;
 | 
			
		||||
 | 
			
		||||
import com.amazonaws.auth.AWSCredentials;
 | 
			
		||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
 | 
			
		||||
import com.amazonaws.auth.BasicAWSCredentials;
 | 
			
		||||
import com.amazonaws.services.sqs.AmazonSQS;
 | 
			
		||||
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
 | 
			
		||||
import com.amazonaws.services.sqs.model.CreateQueueRequest;
 | 
			
		||||
import com.amazonaws.services.sqs.model.QueueAttributeName;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class TbAwsSqsAdmin implements TbQueueAdmin {
 | 
			
		||||
 | 
			
		||||
    private final TbAwsSqsSettings sqsSettings;
 | 
			
		||||
    private final Map<String, String> attributes = new HashMap<>();
 | 
			
		||||
    private final AWSStaticCredentialsProvider credProvider;
 | 
			
		||||
 | 
			
		||||
    public TbAwsSqsAdmin(TbAwsSqsSettings sqsSettings) {
 | 
			
		||||
        this.sqsSettings = sqsSettings;
 | 
			
		||||
 | 
			
		||||
        AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());
 | 
			
		||||
        this.credProvider = new AWSStaticCredentialsProvider(awsCredentials);
 | 
			
		||||
 | 
			
		||||
        attributes.put("FifoQueue", "true");
 | 
			
		||||
        attributes.put("ContentBasedDeduplication", "true");
 | 
			
		||||
        attributes.put(QueueAttributeName.VisibilityTimeout.toString(), sqsSettings.getVisibilityTimeout());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void createTopicIfNotExists(String topic) {
 | 
			
		||||
        AmazonSQS sqsClient = AmazonSQSClientBuilder.standard()
 | 
			
		||||
                .withCredentials(credProvider)
 | 
			
		||||
                .withRegion(sqsSettings.getRegion())
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        final CreateQueueRequest createQueueRequest =
 | 
			
		||||
                new CreateQueueRequest(topic.replaceAll("\\.", "_") + ".fifo")
 | 
			
		||||
                        .withAttributes(attributes);
 | 
			
		||||
        try {
 | 
			
		||||
            sqsClient.createQueue(createQueueRequest);
 | 
			
		||||
        } finally {
 | 
			
		||||
            if (sqsClient != null) {
 | 
			
		||||
                sqsClient.shutdown();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,239 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.sqs;
 | 
			
		||||
 | 
			
		||||
import com.amazonaws.auth.AWSCredentials;
 | 
			
		||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
 | 
			
		||||
import com.amazonaws.auth.BasicAWSCredentials;
 | 
			
		||||
import com.amazonaws.services.sqs.AmazonSQS;
 | 
			
		||||
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
 | 
			
		||||
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry;
 | 
			
		||||
import com.amazonaws.services.sqs.model.Message;
 | 
			
		||||
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
 | 
			
		||||
import com.google.common.reflect.TypeToken;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import com.google.common.util.concurrent.ListeningExecutorService;
 | 
			
		||||
import com.google.common.util.concurrent.MoreExecutors;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import com.google.protobuf.InvalidProtocolBufferException;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.util.CollectionUtils;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueConsumer;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsgDecoder;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsgHeaders;
 | 
			
		||||
import org.thingsboard.server.queue.common.DefaultTbQueueMsgHeaders;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArrayList;
 | 
			
		||||
import java.util.concurrent.ExecutionException;
 | 
			
		||||
import java.util.concurrent.Executors;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class TbAwsSqsConsumerTemplate<T extends TbQueueMsg> implements TbQueueConsumer<T> {
 | 
			
		||||
 | 
			
		||||
    private static final int MAX_NUM_MSGS = 10;
 | 
			
		||||
 | 
			
		||||
    private final Gson gson = new Gson();
 | 
			
		||||
    private final TbQueueAdmin admin;
 | 
			
		||||
    private final AmazonSQS sqsClient;
 | 
			
		||||
    private final String topic;
 | 
			
		||||
    private final TbQueueMsgDecoder<T> decoder;
 | 
			
		||||
    private final TbAwsSqsSettings sqsSettings;
 | 
			
		||||
 | 
			
		||||
    private final List<AwsSqsMsgWrapper> pendingMessages = new CopyOnWriteArrayList<>();
 | 
			
		||||
    private volatile Set<String> queueUrls;
 | 
			
		||||
    private volatile Set<TopicPartitionInfo> partitions;
 | 
			
		||||
    private ListeningExecutorService consumerExecutor;
 | 
			
		||||
    private volatile boolean subscribed;
 | 
			
		||||
    private volatile boolean stopped = false;
 | 
			
		||||
 | 
			
		||||
    public TbAwsSqsConsumerTemplate(TbQueueAdmin admin, TbAwsSqsSettings sqsSettings, String topic, TbQueueMsgDecoder<T> decoder) {
 | 
			
		||||
        this.admin = admin;
 | 
			
		||||
        this.decoder = decoder;
 | 
			
		||||
        this.topic = topic;
 | 
			
		||||
        this.sqsSettings = sqsSettings;
 | 
			
		||||
 | 
			
		||||
        AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());
 | 
			
		||||
        AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials);
 | 
			
		||||
 | 
			
		||||
        this.sqsClient = AmazonSQSClientBuilder.standard()
 | 
			
		||||
                .withCredentials(credProvider)
 | 
			
		||||
                .withRegion(sqsSettings.getRegion())
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getTopic() {
 | 
			
		||||
        return topic;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void subscribe() {
 | 
			
		||||
        partitions = Collections.singleton(new TopicPartitionInfo(topic, null, null, true));
 | 
			
		||||
        subscribed = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void subscribe(Set<TopicPartitionInfo> partitions) {
 | 
			
		||||
        this.partitions = partitions;
 | 
			
		||||
        subscribed = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void unsubscribe() {
 | 
			
		||||
        stopped = true;
 | 
			
		||||
 | 
			
		||||
        if (sqsClient != null) {
 | 
			
		||||
            sqsClient.shutdown();
 | 
			
		||||
        }
 | 
			
		||||
        if (consumerExecutor != null) {
 | 
			
		||||
            consumerExecutor.shutdownNow();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<T> poll(long durationInMillis) {
 | 
			
		||||
        if (!subscribed && partitions == null) {
 | 
			
		||||
            try {
 | 
			
		||||
                Thread.sleep(durationInMillis);
 | 
			
		||||
            } catch (InterruptedException e) {
 | 
			
		||||
                log.debug("Failed to await subscription", e);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!subscribed) {
 | 
			
		||||
                List<String> topicNames = partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList());
 | 
			
		||||
                queueUrls = topicNames.stream().map(this::getQueueUrl).collect(Collectors.toSet());
 | 
			
		||||
                consumerExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(queueUrls.size() * sqsSettings.getThreadsPerTopic() + 1));
 | 
			
		||||
                subscribed = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!pendingMessages.isEmpty()) {
 | 
			
		||||
                log.warn("Present {} non committed messages.", pendingMessages.size());
 | 
			
		||||
                return Collections.emptyList();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            List<ListenableFuture<List<Message>>> futureList = queueUrls
 | 
			
		||||
                    .stream()
 | 
			
		||||
                    .map(url -> poll(url, (int) TimeUnit.MILLISECONDS.toSeconds(durationInMillis)))
 | 
			
		||||
                    .collect(Collectors.toList());
 | 
			
		||||
            ListenableFuture<List<List<Message>>> futureResult = Futures.allAsList(futureList);
 | 
			
		||||
            try {
 | 
			
		||||
                return futureResult.get().stream()
 | 
			
		||||
                        .flatMap(List::stream)
 | 
			
		||||
                        .map(msg -> {
 | 
			
		||||
                            try {
 | 
			
		||||
                                return decode(msg);
 | 
			
		||||
                            } catch (IOException e) {
 | 
			
		||||
                                log.error("Failed to decode message: [{}]", msg);
 | 
			
		||||
                                return null;
 | 
			
		||||
                            }
 | 
			
		||||
                        }).filter(Objects::nonNull)
 | 
			
		||||
                        .collect(Collectors.toList());
 | 
			
		||||
            } catch (InterruptedException | ExecutionException e) {
 | 
			
		||||
                if (stopped) {
 | 
			
		||||
                    log.info("[{}] Aws SQS consumer is stopped.", topic);
 | 
			
		||||
                } else {
 | 
			
		||||
                    log.error("Failed to pool messages.",  e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return Collections.emptyList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<List<Message>> poll(String url, int waitTimeSeconds) {
 | 
			
		||||
        List<ListenableFuture<List<Message>>> result = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < sqsSettings.getThreadsPerTopic(); i++) {
 | 
			
		||||
            result.add(consumerExecutor.submit(() -> {
 | 
			
		||||
                ReceiveMessageRequest request = new ReceiveMessageRequest();
 | 
			
		||||
                request
 | 
			
		||||
                        .withWaitTimeSeconds(waitTimeSeconds)
 | 
			
		||||
                        .withMessageAttributeNames("headers")
 | 
			
		||||
                        .withQueueUrl(url)
 | 
			
		||||
                        .withMaxNumberOfMessages(MAX_NUM_MSGS);
 | 
			
		||||
                return sqsClient.receiveMessage(request).getMessages();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
        return Futures.transform(Futures.allAsList(result), list -> {
 | 
			
		||||
            if (!CollectionUtils.isEmpty(list)) {
 | 
			
		||||
                return list.stream()
 | 
			
		||||
                        .flatMap(messageList -> {
 | 
			
		||||
                            if (!messageList.isEmpty()) {
 | 
			
		||||
                                this.pendingMessages.add(new AwsSqsMsgWrapper(url, messageList));
 | 
			
		||||
                                return messageList.stream();
 | 
			
		||||
                            }
 | 
			
		||||
                            return Stream.empty();
 | 
			
		||||
                        })
 | 
			
		||||
                        .collect(Collectors.toList());
 | 
			
		||||
            }
 | 
			
		||||
            return Collections.emptyList();
 | 
			
		||||
        }, consumerExecutor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void commit() {
 | 
			
		||||
        pendingMessages.forEach(msg ->
 | 
			
		||||
                consumerExecutor.submit(() -> {
 | 
			
		||||
                    List<DeleteMessageBatchRequestEntry> entries = msg.getMessages()
 | 
			
		||||
                            .stream()
 | 
			
		||||
                            .map(message -> new DeleteMessageBatchRequestEntry(message.getMessageId(), message.getReceiptHandle()))
 | 
			
		||||
                            .collect(Collectors.toList());
 | 
			
		||||
                    sqsClient.deleteMessageBatch(msg.getUrl(), entries);
 | 
			
		||||
                }));
 | 
			
		||||
 | 
			
		||||
        pendingMessages.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public T decode(Message message) throws InvalidProtocolBufferException {
 | 
			
		||||
        TbAwsSqsMsg msg = gson.fromJson(message.getBody(), TbAwsSqsMsg.class);
 | 
			
		||||
        TbQueueMsgHeaders headers = new DefaultTbQueueMsgHeaders();
 | 
			
		||||
        Map<String, byte[]> headerMap = gson.fromJson(message.getMessageAttributes().get("headers").getStringValue(), new TypeToken<Map<String, byte[]>>() {
 | 
			
		||||
        }.getType());
 | 
			
		||||
        headerMap.forEach(headers::put);
 | 
			
		||||
        msg.setHeaders(headers);
 | 
			
		||||
        return decoder.decode(msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
    private static class AwsSqsMsgWrapper {
 | 
			
		||||
        private final String url;
 | 
			
		||||
        private final List<Message> messages;
 | 
			
		||||
 | 
			
		||||
        public AwsSqsMsgWrapper(String url, List<Message> messages) {
 | 
			
		||||
            this.url = url;
 | 
			
		||||
            this.messages = messages;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String getQueueUrl(String topic) {
 | 
			
		||||
        admin.createTopicIfNotExists(topic);
 | 
			
		||||
        return sqsClient.getQueueUrl(topic.replaceAll("\\.", "_") + ".fifo").getQueueUrl();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,38 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.sqs;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.annotations.Expose;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsgHeaders;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class TbAwsSqsMsg implements TbQueueMsg {
 | 
			
		||||
    private final UUID key;
 | 
			
		||||
    private final byte[] data;
 | 
			
		||||
 | 
			
		||||
    public TbAwsSqsMsg(UUID key, byte[] data) {
 | 
			
		||||
        this.key = key;
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Expose(serialize = false, deserialize = false)
 | 
			
		||||
    private TbQueueMsgHeaders headers;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,127 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.sqs;
 | 
			
		||||
 | 
			
		||||
import com.amazonaws.auth.AWSCredentials;
 | 
			
		||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
 | 
			
		||||
import com.amazonaws.auth.BasicAWSCredentials;
 | 
			
		||||
import com.amazonaws.services.sqs.AmazonSQS;
 | 
			
		||||
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
 | 
			
		||||
import com.amazonaws.services.sqs.model.MessageAttributeValue;
 | 
			
		||||
import com.amazonaws.services.sqs.model.SendMessageRequest;
 | 
			
		||||
import com.amazonaws.services.sqs.model.SendMessageResult;
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import com.google.common.util.concurrent.ListeningExecutorService;
 | 
			
		||||
import com.google.common.util.concurrent.MoreExecutors;
 | 
			
		||||
import com.google.gson.Gson;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueAdmin;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueCallback;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueMsg;
 | 
			
		||||
import org.thingsboard.server.queue.TbQueueProducer;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap;
 | 
			
		||||
import java.util.concurrent.Executors;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class TbAwsSqsProducerTemplate<T extends TbQueueMsg> implements TbQueueProducer<T> {
 | 
			
		||||
    private final String defaultTopic;
 | 
			
		||||
    private final AmazonSQS sqsClient;
 | 
			
		||||
    private final Gson gson = new Gson();
 | 
			
		||||
    private final Map<String, String> queueUrlMap = new ConcurrentHashMap<>();
 | 
			
		||||
    private final TbQueueAdmin admin;
 | 
			
		||||
    private ListeningExecutorService producerExecutor;
 | 
			
		||||
 | 
			
		||||
    public TbAwsSqsProducerTemplate(TbQueueAdmin admin, TbAwsSqsSettings sqsSettings, String defaultTopic) {
 | 
			
		||||
        this.admin = admin;
 | 
			
		||||
        this.defaultTopic = defaultTopic;
 | 
			
		||||
 | 
			
		||||
        AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());
 | 
			
		||||
        AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials);
 | 
			
		||||
 | 
			
		||||
        this.sqsClient = AmazonSQSClientBuilder.standard()
 | 
			
		||||
                .withCredentials(credProvider)
 | 
			
		||||
                .withRegion(sqsSettings.getRegion())
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        producerExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void init() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String getDefaultTopic() {
 | 
			
		||||
        return defaultTopic;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) {
 | 
			
		||||
        SendMessageRequest sendMsgRequest = new SendMessageRequest();
 | 
			
		||||
        sendMsgRequest.withQueueUrl(getQueueUrl(tpi.getFullTopicName()));
 | 
			
		||||
        sendMsgRequest.withMessageBody(gson.toJson(new TbAwsSqsMsg(msg.getKey(), msg.getData())));
 | 
			
		||||
 | 
			
		||||
        Map<String, MessageAttributeValue> attributes = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
        attributes.put("headers", new MessageAttributeValue()
 | 
			
		||||
                .withStringValue(gson.toJson(msg.getHeaders().getData()))
 | 
			
		||||
                .withDataType("String"));
 | 
			
		||||
 | 
			
		||||
        sendMsgRequest.withMessageAttributes(attributes);
 | 
			
		||||
        sendMsgRequest.withMessageGroupId(msg.getKey().toString());
 | 
			
		||||
        ListenableFuture<SendMessageResult> future = producerExecutor.submit(() -> sqsClient.sendMessage(sendMsgRequest));
 | 
			
		||||
 | 
			
		||||
        Futures.addCallback(future, new FutureCallback<SendMessageResult>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(SendMessageResult result) {
 | 
			
		||||
                if (callback != null) {
 | 
			
		||||
                    callback.onSuccess(new AwsSqsTbQueueMsgMetadata(result.getSdkHttpMetadata()));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Throwable t) {
 | 
			
		||||
                if (callback != null) {
 | 
			
		||||
                    callback.onFailure(t);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void stop() {
 | 
			
		||||
        if (producerExecutor != null) {
 | 
			
		||||
            producerExecutor.shutdownNow();
 | 
			
		||||
        }
 | 
			
		||||
        if (sqsClient != null) {
 | 
			
		||||
            sqsClient.shutdown();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String getQueueUrl(String topic) {
 | 
			
		||||
        return queueUrlMap.computeIfAbsent(topic, k -> {
 | 
			
		||||
            admin.createTopicIfNotExists(topic);
 | 
			
		||||
            return sqsClient.getQueueUrl(topic.replaceAll("\\.", "_") + ".fifo").getQueueUrl();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,44 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2020 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.queue.sqs;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@ConditionalOnExpression("'${queue.type:null}'=='aws-sqs'")
 | 
			
		||||
@Component
 | 
			
		||||
@Data
 | 
			
		||||
public class TbAwsSqsSettings {
 | 
			
		||||
 | 
			
		||||
    @Value("${queue.aws_sqs.access_key_id}")
 | 
			
		||||
    private String accessKeyId;
 | 
			
		||||
 | 
			
		||||
    @Value("${queue.aws_sqs.secret_access_key}")
 | 
			
		||||
    private String secretAccessKey;
 | 
			
		||||
 | 
			
		||||
    @Value("${queue.aws_sqs.region}")
 | 
			
		||||
    private String region;
 | 
			
		||||
 | 
			
		||||
    @Value("${queue.aws_sqs.threads_per_topic}")
 | 
			
		||||
    private int threadsPerTopic;
 | 
			
		||||
 | 
			
		||||
    @Value("${queue.aws_sqs.visibility_timeout}")
 | 
			
		||||
    private String visibilityTimeout;
 | 
			
		||||
}
 | 
			
		||||
@ -138,6 +138,9 @@ public class DefaultTransportService implements TransportService {
 | 
			
		||||
            while (!stopped) {
 | 
			
		||||
                try {
 | 
			
		||||
                    List<TbProtoQueueMsg<ToTransportMsg>> records = transportNotificationsConsumer.poll(notificationsPollDuration);
 | 
			
		||||
                    if (records.size() == 0) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    records.forEach(record -> {
 | 
			
		||||
                        try {
 | 
			
		||||
                            ToTransportMsg toTransportMsg = record.getValue();
 | 
			
		||||
@ -170,6 +173,10 @@ public class DefaultTransportService implements TransportService {
 | 
			
		||||
            perDeviceLimits.clear();
 | 
			
		||||
        }
 | 
			
		||||
        stopped = true;
 | 
			
		||||
 | 
			
		||||
        if (transportNotificationsConsumer != null) {
 | 
			
		||||
            transportNotificationsConsumer.unsubscribe();
 | 
			
		||||
        }
 | 
			
		||||
        if (schedulerExecutor != null) {
 | 
			
		||||
            schedulerExecutor.shutdownNow();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								pom.xml
									
									
									
									
									
								
							@ -92,6 +92,7 @@
 | 
			
		||||
        <fst.version>2.57</fst.version>
 | 
			
		||||
        <antlr.version>2.7.7</antlr.version>
 | 
			
		||||
        <snakeyaml.version>1.23</snakeyaml.version>
 | 
			
		||||
        <amazonaws.sqs.version>1.11.747</amazonaws.sqs.version>
 | 
			
		||||
        <passay.version>1.5.0</passay.version>
 | 
			
		||||
        <ua-parser.version>1.4.3</ua-parser.version>
 | 
			
		||||
    </properties>
 | 
			
		||||
@ -886,6 +887,11 @@
 | 
			
		||||
                <artifactId>jts-core</artifactId>
 | 
			
		||||
                <version>${jts.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
            <groupId>com.amazonaws</groupId>
 | 
			
		||||
            <artifactId>aws-java-sdk-sqs</artifactId>
 | 
			
		||||
            <version>${amazonaws.sqs.version}</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.passay</groupId>
 | 
			
		||||
                <artifactId>passay</artifactId>
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@
 | 
			
		||||
    <properties>
 | 
			
		||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
			
		||||
        <main.dir>${basedir}/../..</main.dir>
 | 
			
		||||
        <aws.sdk.version>1.11.323</aws.sdk.version>
 | 
			
		||||
        <aws.sdk.version>1.11.747</aws.sdk.version>
 | 
			
		||||
        <pubsub.client.version>1.83.0</pubsub.client.version>
 | 
			
		||||
        <google.common.protos.version>1.16.0</google.common.protos.version>
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ transport:
 | 
			
		||||
    max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}"
 | 
			
		||||
 | 
			
		||||
queue:
 | 
			
		||||
  type: "${TB_QUEUE_TYPE:kafka}" # kafka or ?
 | 
			
		||||
  type: "${TB_QUEUE_TYPE:kafka}" # kafka or aws-sqs
 | 
			
		||||
  kafka:
 | 
			
		||||
    bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
 | 
			
		||||
    acks: "${TB_KAFKA_ACKS:all}"
 | 
			
		||||
@ -71,6 +71,10 @@ queue:
 | 
			
		||||
    batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
 | 
			
		||||
    linger.ms: "${TB_KAFKA_LINGER_MS:1}"
 | 
			
		||||
    buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
 | 
			
		||||
  aws_sqs:
 | 
			
		||||
    access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
 | 
			
		||||
    secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
 | 
			
		||||
    region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
 | 
			
		||||
  partitions:
 | 
			
		||||
    hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}"
 | 
			
		||||
    virtual_nodes_size: "${TB_QUEUE_PARTITIONS_VIRTUAL_NODES_SIZE:16}"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user