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