Browse Source
* 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 queuepull/2560/head
committed by
GitHub
25 changed files with 1074 additions and 10 deletions
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
Loading…
Reference in new issue