15 changed files with 314 additions and 23 deletions
@ -0,0 +1,194 @@ |
|||
/* |
|||
* 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. |
|||
*/ |
|||
|
|||
'use strict'; |
|||
|
|||
const config = require('config'), |
|||
JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'), |
|||
logger = require('../config/logger')._logger('serviceBusTemplate'); |
|||
const {ServiceBusClient, ReceiveMode} = require("@azure/service-bus"); |
|||
const azure = require('azure-sb'); |
|||
|
|||
const requestTopic = config.get('request_topic'); |
|||
const namespaceName = config.get('service_bus.namespace_name'); |
|||
const sasKeyName = config.get('service_bus.sas_key_name'); |
|||
const sasKey = config.get('service_bus.sas_key'); |
|||
const queueProperties = config.get('service_bus.queue-properties'); |
|||
|
|||
let sbClient; |
|||
let receiverClient; |
|||
let receiver; |
|||
let serviceBusService; |
|||
|
|||
let queueOptions = {}; |
|||
const queues = []; |
|||
const senderMap = new Map(); |
|||
|
|||
function ServiceBusProducer() { |
|||
this.send = async (responseTopic, scriptId, rawResponse, headers) => { |
|||
if (!queues.includes(requestTopic)) { |
|||
await createQueueIfNotExist(requestTopic); |
|||
queues.push(requestTopic); |
|||
} |
|||
|
|||
let customSender = senderMap.get(responseTopic); |
|||
|
|||
if (!customSender) { |
|||
customSender = new CustomSender(responseTopic); |
|||
senderMap.set(responseTopic, customSender); |
|||
} |
|||
|
|||
let data = { |
|||
key: scriptId, |
|||
data: [...rawResponse], |
|||
headers: headers |
|||
}; |
|||
|
|||
return customSender.send({body: data}); |
|||
} |
|||
} |
|||
|
|||
function CustomSender(topic) { |
|||
this.queueClient = sbClient.createQueueClient(topic); |
|||
this.sender = this.queueClient.createSender(); |
|||
|
|||
this.send = async (message) => { |
|||
return this.sender.send(message); |
|||
} |
|||
} |
|||
|
|||
(async () => { |
|||
try { |
|||
logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); |
|||
|
|||
const connectionString = `Endpoint=sb://${namespaceName}.servicebus.windows.net/;SharedAccessKeyName=${sasKeyName};SharedAccessKey=${sasKey}`; |
|||
sbClient = ServiceBusClient.createFromConnectionString(connectionString); |
|||
serviceBusService = azure.createServiceBusService(connectionString); |
|||
|
|||
parseQueueProperties(); |
|||
|
|||
await new Promise((resolve, reject) => { |
|||
serviceBusService.listQueues((err, data) => { |
|||
if (err) { |
|||
reject(err); |
|||
} else { |
|||
data.forEach(queue => { |
|||
queues.push(queue.QueueName); |
|||
}); |
|||
resolve(); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
if (!queues.includes(requestTopic)) { |
|||
await createQueueIfNotExist(requestTopic); |
|||
queues.push(requestTopic); |
|||
} |
|||
|
|||
receiverClient = sbClient.createQueueClient(requestTopic); |
|||
receiver = receiverClient.createReceiver(ReceiveMode.peekLock); |
|||
|
|||
const messageProcessor = new JsInvokeMessageProcessor(new ServiceBusProducer()); |
|||
|
|||
const messageHandler = async (message) => { |
|||
if (message) { |
|||
messageProcessor.onJsInvokeMessage(message.body); |
|||
await message.complete(); |
|||
} |
|||
}; |
|||
const errorHandler = (error) => { |
|||
logger.error('Failed to receive message from queue.', error); |
|||
}; |
|||
receiver.registerMessageHandler(messageHandler, errorHandler); |
|||
} catch (e) { |
|||
logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message); |
|||
logger.error(e.stack); |
|||
exit(-1); |
|||
} |
|||
})(); |
|||
|
|||
async function createQueueIfNotExist(topic) { |
|||
return new Promise((resolve, reject) => { |
|||
serviceBusService.createQueueIfNotExists(topic, queueOptions, (err) => { |
|||
if (err) { |
|||
reject(err); |
|||
} else { |
|||
resolve(); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function parseQueueProperties() { |
|||
let properties = {}; |
|||
const props = queueProperties.split(';'); |
|||
props.forEach(p => { |
|||
const delimiterPosition = p.indexOf(':'); |
|||
properties[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); |
|||
}); |
|||
queueOptions = { |
|||
MaxSizeInMegabytes: properties['maxSizeInMb'], |
|||
DefaultMessageTimeToLive: `PT${properties['messageTimeToLiveInSec']}S`, |
|||
LockDuration: `PT${properties['lockDurationInSec']}S` |
|||
}; |
|||
} |
|||
|
|||
process.on('exit', () => { |
|||
exit(0); |
|||
}); |
|||
|
|||
async function exit(status) { |
|||
logger.info('Exiting with status: %d ...', status); |
|||
logger.info('Stopping Azure Service Bus resources...') |
|||
if (receiver) { |
|||
try { |
|||
await receiver.close(); |
|||
} catch (e) { |
|||
|
|||
} |
|||
} |
|||
|
|||
if (receiverClient) { |
|||
try { |
|||
await receiverClient.close(); |
|||
} catch (e) { |
|||
|
|||
} |
|||
} |
|||
|
|||
senderMap.forEach((k, v) => { |
|||
try { |
|||
v.sender.close(); |
|||
} catch (e) { |
|||
|
|||
} |
|||
try { |
|||
v.queueClient.close(); |
|||
} catch (e) { |
|||
|
|||
} |
|||
}); |
|||
|
|||
if (sbClient) { |
|||
try { |
|||
sbClient.close(); |
|||
} catch (e) { |
|||
|
|||
} |
|||
} |
|||
logger.info('Azure Service Bus resources stopped.') |
|||
process.exit(status); |
|||
} |
|||
Loading…
Reference in new issue