Commit ef35ee8c304d743c55c053a0996ce84793daf40e
1 parent
214a6060
added queue settings to js-executor
Showing
7 changed files
with
224 additions
and
35 deletions
@@ -17,30 +17,34 @@ | @@ -17,30 +17,34 @@ | ||
17 | service-type: "TB_SERVICE_TYPE" | 17 | service-type: "TB_SERVICE_TYPE" |
18 | request_topic: "REMOTE_JS_EVAL_REQUEST_TOPIC" | 18 | request_topic: "REMOTE_JS_EVAL_REQUEST_TOPIC" |
19 | 19 | ||
20 | +js: | ||
21 | + response_poll_interval: "REMOTE_JS_RESPONSE_POLL_INTERVAL_MS" | ||
22 | + | ||
20 | kafka: | 23 | kafka: |
21 | bootstrap: | 24 | bootstrap: |
22 | # Kafka Bootstrap Servers | 25 | # Kafka Bootstrap Servers |
23 | servers: "TB_KAFKA_SERVERS" | 26 | servers: "TB_KAFKA_SERVERS" |
27 | + replication_factor: "TB_QUEUE_KAFKA_REPLICATION_FACTOR" | ||
28 | + topic-properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES" | ||
24 | 29 | ||
25 | pubsub: | 30 | pubsub: |
26 | project_id: "TB_QUEUE_PUBSUB_PROJECT_ID" | 31 | project_id: "TB_QUEUE_PUBSUB_PROJECT_ID" |
27 | service_account: "TB_QUEUE_PUBSUB_SERVICE_ACCOUNT" | 32 | service_account: "TB_QUEUE_PUBSUB_SERVICE_ACCOUNT" |
33 | + queue-properties: "TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES" | ||
28 | 34 | ||
29 | aws_sqs: | 35 | aws_sqs: |
30 | access_key_id: "TB_QUEUE_AWS_SQS_ACCESS_KEY_ID" | 36 | access_key_id: "TB_QUEUE_AWS_SQS_ACCESS_KEY_ID" |
31 | secret_access_key: "TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY" | 37 | secret_access_key: "TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY" |
32 | region: "TB_QUEUE_AWS_SQS_REGION" | 38 | region: "TB_QUEUE_AWS_SQS_REGION" |
39 | + queue-properties: "TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES" | ||
33 | 40 | ||
34 | rabbitmq: | 41 | rabbitmq: |
35 | - exchange_name: "TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME" | ||
36 | host: "TB_QUEUE_RABBIT_MQ_HOST" | 42 | host: "TB_QUEUE_RABBIT_MQ_HOST" |
37 | port: "TB_QUEUE_RABBIT_MQ_PORT" | 43 | port: "TB_QUEUE_RABBIT_MQ_PORT" |
38 | virtual_host: "TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST" | 44 | virtual_host: "TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST" |
39 | username: "TB_QUEUE_RABBIT_MQ_USERNAME" | 45 | username: "TB_QUEUE_RABBIT_MQ_USERNAME" |
40 | password: "TB_QUEUE_RABBIT_MQ_PASSWORD" | 46 | password: "TB_QUEUE_RABBIT_MQ_PASSWORD" |
41 | - automatic_recovery_enabled: "TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED" | ||
42 | - connection_timeout: "TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT" | ||
43 | - handshake_timeout: "TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT" | 47 | + queue-properties: "TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES" |
44 | 48 | ||
45 | logger: | 49 | logger: |
46 | level: "LOGGER_LEVEL" | 50 | level: "LOGGER_LEVEL" |
@@ -15,23 +15,31 @@ | @@ -15,23 +15,31 @@ | ||
15 | # | 15 | # |
16 | 16 | ||
17 | service-type: "kafka" | 17 | service-type: "kafka" |
18 | -request_topic: "js.eval.requests" | 18 | +request_topic: "js_eval.requests" |
19 | + | ||
20 | +js: | ||
21 | + response_poll_interval: "25" | ||
19 | 22 | ||
20 | kafka: | 23 | kafka: |
21 | bootstrap: | 24 | bootstrap: |
22 | # Kafka Bootstrap Servers | 25 | # Kafka Bootstrap Servers |
23 | servers: "localhost:9092" | 26 | servers: "localhost:9092" |
27 | + replication_factor: "1" | ||
28 | + topic-properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600" | ||
29 | + | ||
30 | +pubsub: | ||
31 | + queue-properties: "ackDeadlineInSec:30;messageRetentionInSec:604800" | ||
32 | + | ||
33 | +aws_sqs: | ||
34 | + queue-properties: "VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800" | ||
24 | 35 | ||
25 | rabbitmq: | 36 | rabbitmq: |
26 | - exchange_name: "" | ||
27 | host: "localhost" | 37 | host: "localhost" |
28 | port: "5672" | 38 | port: "5672" |
29 | virtual_host: "/" | 39 | virtual_host: "/" |
30 | - username: "YOUR_USERNAME" | ||
31 | - password: "YOUR_PASSWORD" | ||
32 | - automatic_recovery_enabled: "false" | ||
33 | - connection_timeout: "60000" | ||
34 | - handshake_timeout: "10000" | 40 | + username: "admin" |
41 | + password: "password" | ||
42 | + queue-properties: "x-max-length-bytes:1048576000;x-message-ttl:604800000" | ||
35 | 43 | ||
36 | 44 | ||
37 | logger: | 45 | logger: |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | "dependencies": { | 14 | "dependencies": { |
15 | "config": "^3.2.2", | 15 | "config": "^3.2.2", |
16 | "js-yaml": "^3.12.0", | 16 | "js-yaml": "^3.12.0", |
17 | - "kafkajs": "^1.11.0", | 17 | + "kafkajs": "^1.12.0", |
18 | "@google-cloud/pubsub": "^1.7.1", | 18 | "@google-cloud/pubsub": "^1.7.1", |
19 | "aws-sdk": "^2.663.0", | 19 | "aws-sdk": "^2.663.0", |
20 | "amqplib": "^0.5.5", | 20 | "amqplib": "^0.5.5", |
@@ -26,10 +26,13 @@ const accessKeyId = config.get('aws_sqs.access_key_id'); | @@ -26,10 +26,13 @@ const accessKeyId = config.get('aws_sqs.access_key_id'); | ||
26 | const secretAccessKey = config.get('aws_sqs.secret_access_key'); | 26 | const secretAccessKey = config.get('aws_sqs.secret_access_key'); |
27 | const region = config.get('aws_sqs.region'); | 27 | const region = config.get('aws_sqs.region'); |
28 | const AWS = require('aws-sdk'); | 28 | const AWS = require('aws-sdk'); |
29 | +const queueProperties = config.get('aws_sqs.queue-properties'); | ||
30 | +const poolInterval = config.get('js.response_poll_interval'); | ||
29 | 31 | ||
32 | +let queueAttributes = {FifoQueue: 'true', ContentBasedDeduplication: 'true'}; | ||
30 | let sqsClient; | 33 | let sqsClient; |
31 | -let queueURL; | ||
32 | -let responseTopics = new Map(); | 34 | +let requestQueueURL; |
35 | +let queueUrls = new Map(); | ||
33 | let stopped = false; | 36 | let stopped = false; |
34 | 37 | ||
35 | function AwsSqsProducer() { | 38 | function AwsSqsProducer() { |
@@ -41,11 +44,11 @@ function AwsSqsProducer() { | @@ -41,11 +44,11 @@ function AwsSqsProducer() { | ||
41 | headers: headers | 44 | headers: headers |
42 | }); | 45 | }); |
43 | 46 | ||
44 | - let responseQueueUrl = responseTopics.get(responseTopic); | 47 | + let responseQueueUrl = queueUrls.get(topicToSqsQueueName(responseTopic)); |
45 | 48 | ||
46 | if (!responseQueueUrl) { | 49 | if (!responseQueueUrl) { |
47 | responseQueueUrl = await createQueue(responseTopic); | 50 | responseQueueUrl = await createQueue(responseTopic); |
48 | - responseTopics.set(responseTopic, responseQueueUrl); | 51 | + queueUrls.set(responseTopic, responseQueueUrl); |
49 | } | 52 | } |
50 | 53 | ||
51 | let params = {MessageBody: msgBody, QueueUrl: responseQueueUrl, MessageGroupId: scriptId}; | 54 | let params = {MessageBody: msgBody, QueueUrl: responseQueueUrl, MessageGroupId: scriptId}; |
@@ -69,13 +72,27 @@ function AwsSqsProducer() { | @@ -69,13 +72,27 @@ function AwsSqsProducer() { | ||
69 | 72 | ||
70 | sqsClient = new AWS.SQS({apiVersion: '2012-11-05'}); | 73 | sqsClient = new AWS.SQS({apiVersion: '2012-11-05'}); |
71 | 74 | ||
72 | - queueURL = await createQueue(requestTopic); | 75 | + const queues = await getQueues(); |
76 | + | ||
77 | + queues.forEach(queueUrl => { | ||
78 | + const delimiterPosition = queueUrl.lastIndexOf('/'); | ||
79 | + const queueName = queueUrl.substring(delimiterPosition + 1); | ||
80 | + queueUrls.set(queueName, queueUrl); | ||
81 | + }) | ||
82 | + | ||
83 | + parseQueueProperties(); | ||
84 | + | ||
85 | + requestQueueURL = queueUrls.get(topicToSqsQueueName(requestTopic)); | ||
86 | + if (!requestQueueURL) { | ||
87 | + requestQueueURL = await createQueue(requestTopic); | ||
88 | + } | ||
89 | + | ||
73 | const messageProcessor = new JsInvokeMessageProcessor(new AwsSqsProducer()); | 90 | const messageProcessor = new JsInvokeMessageProcessor(new AwsSqsProducer()); |
74 | 91 | ||
75 | const params = { | 92 | const params = { |
76 | MaxNumberOfMessages: 10, | 93 | MaxNumberOfMessages: 10, |
77 | - QueueUrl: queueURL, | ||
78 | - WaitTimeSeconds: 0.025 | 94 | + QueueUrl: requestQueueURL, |
95 | + WaitTimeSeconds: poolInterval / 1000 | ||
79 | }; | 96 | }; |
80 | while (!stopped) { | 97 | while (!stopped) { |
81 | const messages = await new Promise((resolve, reject) => { | 98 | const messages = await new Promise((resolve, reject) => { |
@@ -100,7 +117,7 @@ function AwsSqsProducer() { | @@ -100,7 +117,7 @@ function AwsSqsProducer() { | ||
100 | }); | 117 | }); |
101 | 118 | ||
102 | const deleteBatch = { | 119 | const deleteBatch = { |
103 | - QueueUrl: queueURL, | 120 | + QueueUrl: requestQueueURL, |
104 | Entries: entries | 121 | Entries: entries |
105 | }; | 122 | }; |
106 | sqsClient.deleteMessageBatch(deleteBatch, function (err, data) { | 123 | sqsClient.deleteMessageBatch(deleteBatch, function (err, data) { |
@@ -120,14 +137,9 @@ function AwsSqsProducer() { | @@ -120,14 +137,9 @@ function AwsSqsProducer() { | ||
120 | })(); | 137 | })(); |
121 | 138 | ||
122 | function createQueue(topic) { | 139 | function createQueue(topic) { |
123 | - let queueName = topic.replace(/\./g, '_') + '.fifo'; | ||
124 | - let queueParams = { | ||
125 | - QueueName: queueName, Attributes: { | ||
126 | - FifoQueue: 'true', | ||
127 | - ContentBasedDeduplication: 'true' | 140 | + let queueName = topicToSqsQueueName(topic); |
141 | + let queueParams = {QueueName: queueName, Attributes: queueAttributes}; | ||
128 | 142 | ||
129 | - } | ||
130 | - }; | ||
131 | return new Promise((resolve, reject) => { | 143 | return new Promise((resolve, reject) => { |
132 | sqsClient.createQueue(queueParams, function (err, data) { | 144 | sqsClient.createQueue(queueParams, function (err, data) { |
133 | if (err) { | 145 | if (err) { |
@@ -139,6 +151,30 @@ function createQueue(topic) { | @@ -139,6 +151,30 @@ function createQueue(topic) { | ||
139 | }); | 151 | }); |
140 | } | 152 | } |
141 | 153 | ||
154 | +function getQueues() { | ||
155 | + return new Promise((resolve, reject) => { | ||
156 | + sqsClient.listQueues(function (err, data) { | ||
157 | + if (err) { | ||
158 | + reject(err); | ||
159 | + } else { | ||
160 | + resolve(data.QueueUrls); | ||
161 | + } | ||
162 | + }); | ||
163 | + }); | ||
164 | +} | ||
165 | + | ||
166 | +function topicToSqsQueueName(topic) { | ||
167 | + return topic.replace(/\./g, '_') + '.fifo'; | ||
168 | +} | ||
169 | + | ||
170 | +function parseQueueProperties() { | ||
171 | + const props = queueProperties.split(';'); | ||
172 | + props.forEach(p => { | ||
173 | + const delimiterPosition = p.indexOf(':'); | ||
174 | + queueAttributes[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); | ||
175 | + }); | ||
176 | +} | ||
177 | + | ||
142 | process.on('exit', () => { | 178 | process.on('exit', () => { |
143 | stopped = true; | 179 | stopped = true; |
144 | logger.info('Aws Sqs client stopped.'); | 180 | logger.info('Aws Sqs client stopped.'); |
@@ -19,13 +19,28 @@ const config = require('config'), | @@ -19,13 +19,28 @@ const config = require('config'), | ||
19 | JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'), | 19 | JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'), |
20 | logger = require('../config/logger')._logger('kafkaTemplate'), | 20 | logger = require('../config/logger')._logger('kafkaTemplate'), |
21 | KafkaJsWinstonLogCreator = require('../config/logger').KafkaJsWinstonLogCreator; | 21 | KafkaJsWinstonLogCreator = require('../config/logger').KafkaJsWinstonLogCreator; |
22 | +const replicationFactor = config.get('kafka.replication_factor'); | ||
23 | +const topicProperties = config.get('kafka.topic-properties'); | ||
22 | 24 | ||
23 | let kafkaClient; | 25 | let kafkaClient; |
26 | +let kafkaAdmin; | ||
24 | let consumer; | 27 | let consumer; |
25 | let producer; | 28 | let producer; |
26 | 29 | ||
30 | +const topics = []; | ||
31 | +const configEntries = []; | ||
32 | + | ||
27 | function KafkaProducer() { | 33 | function KafkaProducer() { |
28 | this.send = async (responseTopic, scriptId, rawResponse, headers) => { | 34 | this.send = async (responseTopic, scriptId, rawResponse, headers) => { |
35 | + | ||
36 | + if (!topics.includes(responseTopic)) { | ||
37 | + let createResponseTopicResult = await createTopic(responseTopic); | ||
38 | + topics.push(responseTopic); | ||
39 | + if (createResponseTopicResult) { | ||
40 | + logger.info('Created new topic: %s', requestTopic); | ||
41 | + } | ||
42 | + } | ||
43 | + | ||
29 | let headersData = headers.data; | 44 | let headersData = headers.data; |
30 | headersData = Object.fromEntries(Object.entries(headersData).map(([key, value]) => [key, Buffer.from(value)])); | 45 | headersData = Object.fromEntries(Object.entries(headersData).map(([key, value]) => [key, Buffer.from(value)])); |
31 | return producer.send( | 46 | return producer.send( |
@@ -47,10 +62,10 @@ function KafkaProducer() { | @@ -47,10 +62,10 @@ function KafkaProducer() { | ||
47 | logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); | 62 | logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); |
48 | 63 | ||
49 | const kafkaBootstrapServers = config.get('kafka.bootstrap.servers'); | 64 | const kafkaBootstrapServers = config.get('kafka.bootstrap.servers'); |
50 | - const kafkaRequestTopic = config.get('request_topic'); | 65 | + const requestTopic = config.get('request_topic'); |
51 | 66 | ||
52 | logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers); | 67 | logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers); |
53 | - logger.info('Kafka Requests Topic: %s', kafkaRequestTopic); | 68 | + logger.info('Kafka Requests Topic: %s', requestTopic); |
54 | 69 | ||
55 | kafkaClient = new Kafka({ | 70 | kafkaClient = new Kafka({ |
56 | brokers: kafkaBootstrapServers.split(','), | 71 | brokers: kafkaBootstrapServers.split(','), |
@@ -58,12 +73,23 @@ function KafkaProducer() { | @@ -58,12 +73,23 @@ function KafkaProducer() { | ||
58 | logCreator: KafkaJsWinstonLogCreator | 73 | logCreator: KafkaJsWinstonLogCreator |
59 | }); | 74 | }); |
60 | 75 | ||
76 | + parseTopicProperties(); | ||
77 | + | ||
78 | + kafkaAdmin = kafkaClient.admin(); | ||
79 | + await kafkaAdmin.connect(); | ||
80 | + | ||
81 | + let createRequestTopicResult = await createTopic(requestTopic); | ||
82 | + | ||
83 | + if (createRequestTopicResult) { | ||
84 | + logger.info('Created new topic: %s', requestTopic); | ||
85 | + } | ||
86 | + | ||
61 | consumer = kafkaClient.consumer({groupId: 'js-executor-group'}); | 87 | consumer = kafkaClient.consumer({groupId: 'js-executor-group'}); |
62 | producer = kafkaClient.producer(); | 88 | producer = kafkaClient.producer(); |
63 | const messageProcessor = new JsInvokeMessageProcessor(new KafkaProducer()); | 89 | const messageProcessor = new JsInvokeMessageProcessor(new KafkaProducer()); |
64 | await consumer.connect(); | 90 | await consumer.connect(); |
65 | await producer.connect(); | 91 | await producer.connect(); |
66 | - await consumer.subscribe({topic: kafkaRequestTopic}); | 92 | + await consumer.subscribe({topic: requestTopic}); |
67 | 93 | ||
68 | logger.info('Started ThingsBoard JavaScript Executor Microservice.'); | 94 | logger.info('Started ThingsBoard JavaScript Executor Microservice.'); |
69 | await consumer.run({ | 95 | await consumer.run({ |
@@ -90,12 +116,37 @@ function KafkaProducer() { | @@ -90,12 +116,37 @@ function KafkaProducer() { | ||
90 | } | 116 | } |
91 | })(); | 117 | })(); |
92 | 118 | ||
119 | +function createTopic(topic) { | ||
120 | + return kafkaAdmin.createTopics({ | ||
121 | + topics: [{ | ||
122 | + topic: topic, | ||
123 | + replicationFactor: replicationFactor, | ||
124 | + configEntries: configEntries | ||
125 | + }] | ||
126 | + }); | ||
127 | +} | ||
128 | + | ||
129 | +function parseTopicProperties() { | ||
130 | + const props = topicProperties.split(';'); | ||
131 | + props.forEach(p => { | ||
132 | + const delimiterPosition = p.indexOf(':'); | ||
133 | + configEntries.push({name: p.substring(0, delimiterPosition), value: p.substring(delimiterPosition + 1)}); | ||
134 | + }); | ||
135 | +} | ||
136 | + | ||
93 | process.on('exit', () => { | 137 | process.on('exit', () => { |
94 | exit(0); | 138 | exit(0); |
95 | }); | 139 | }); |
96 | 140 | ||
97 | async function exit(status) { | 141 | async function exit(status) { |
98 | logger.info('Exiting with status: %d ...', status); | 142 | logger.info('Exiting with status: %d ...', status); |
143 | + | ||
144 | + if (kafkaAdmin) { | ||
145 | + logger.info('Stopping Kafka Admin...'); | ||
146 | + await kafkaAdmin.disconnect(); | ||
147 | + logger.info('Kafka Admin stopped.'); | ||
148 | + } | ||
149 | + | ||
99 | if (consumer) { | 150 | if (consumer) { |
100 | logger.info('Stopping Kafka Consumer...'); | 151 | logger.info('Stopping Kafka Consumer...'); |
101 | let _consumer = consumer; | 152 | let _consumer = consumer; |
@@ -24,11 +24,21 @@ const {PubSub} = require('@google-cloud/pubsub'); | @@ -24,11 +24,21 @@ const {PubSub} = require('@google-cloud/pubsub'); | ||
24 | const projectId = config.get('pubsub.project_id'); | 24 | const projectId = config.get('pubsub.project_id'); |
25 | const credentials = JSON.parse(config.get('pubsub.service_account')); | 25 | const credentials = JSON.parse(config.get('pubsub.service_account')); |
26 | const requestTopic = config.get('request_topic'); | 26 | const requestTopic = config.get('request_topic'); |
27 | +const queueProperties = config.get('pubsub.queue-properties'); | ||
27 | 28 | ||
28 | let pubSubClient; | 29 | let pubSubClient; |
29 | 30 | ||
31 | +const topics = []; | ||
32 | +const subscriptions = []; | ||
33 | +let queueProps = []; | ||
34 | + | ||
30 | function PubSubProducer() { | 35 | function PubSubProducer() { |
31 | this.send = async (responseTopic, scriptId, rawResponse, headers) => { | 36 | this.send = async (responseTopic, scriptId, rawResponse, headers) => { |
37 | + | ||
38 | + if (!(subscriptions.includes(responseTopic) && topics.includes(requestTopic))) { | ||
39 | + await createTopic(requestTopic); | ||
40 | + } | ||
41 | + | ||
32 | let data = JSON.stringify( | 42 | let data = JSON.stringify( |
33 | { | 43 | { |
34 | key: scriptId, | 44 | key: scriptId, |
@@ -45,6 +55,28 @@ function PubSubProducer() { | @@ -45,6 +55,28 @@ function PubSubProducer() { | ||
45 | logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); | 55 | logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); |
46 | pubSubClient = new PubSub({projectId: projectId, credentials: credentials}); | 56 | pubSubClient = new PubSub({projectId: projectId, credentials: credentials}); |
47 | 57 | ||
58 | + parseQueueProperties(); | ||
59 | + | ||
60 | + const topicList = await pubSubClient.getTopics(); | ||
61 | + | ||
62 | + if (topicList) { | ||
63 | + topicList[0].forEach(topic => { | ||
64 | + topics.push(getName(topic.name)); | ||
65 | + }); | ||
66 | + } | ||
67 | + | ||
68 | + const subscriptionList = await pubSubClient.getSubscriptions(); | ||
69 | + | ||
70 | + if (subscriptionList) { | ||
71 | + topicList[0].forEach(sub => { | ||
72 | + subscriptions.push(getName(sub.name)); | ||
73 | + }); | ||
74 | + } | ||
75 | + | ||
76 | + if (!(subscriptions.includes(requestTopic) && topics.includes(requestTopic))) { | ||
77 | + await createTopic(requestTopic); | ||
78 | + } | ||
79 | + | ||
48 | const subscription = pubSubClient.subscription(requestTopic); | 80 | const subscription = pubSubClient.subscription(requestTopic); |
49 | 81 | ||
50 | const messageProcessor = new JsInvokeMessageProcessor(new PubSubProducer()); | 82 | const messageProcessor = new JsInvokeMessageProcessor(new PubSubProducer()); |
@@ -64,6 +96,36 @@ function PubSubProducer() { | @@ -64,6 +96,36 @@ function PubSubProducer() { | ||
64 | } | 96 | } |
65 | })(); | 97 | })(); |
66 | 98 | ||
99 | +async function createTopic(topic) { | ||
100 | + if (!topics.includes(topic)) { | ||
101 | + await pubSubClient.createTopic(topic); | ||
102 | + topics.push(topic); | ||
103 | + logger.info('Created new Pub/Sub topic: %s', topic); | ||
104 | + } | ||
105 | + await createSubscription(topic) | ||
106 | +} | ||
107 | + | ||
108 | +async function createSubscription(topic) { | ||
109 | + if (!subscriptions.includes(topic)) { | ||
110 | + await pubSubClient.topic(topic).createSubscription(topic); | ||
111 | + subscriptions.push(topic); | ||
112 | + logger.info('Created new Pub/Sub subscription: %s', topic); | ||
113 | + } | ||
114 | +} | ||
115 | + | ||
116 | +function parseQueueProperties() { | ||
117 | + const props = queueProperties.split(';'); | ||
118 | + props.forEach(p => { | ||
119 | + const delimiterPosition = p.indexOf(':'); | ||
120 | + queueProps[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); | ||
121 | + }); | ||
122 | +} | ||
123 | + | ||
124 | +function getName(fullName) { | ||
125 | + const delimiterPosition = fullName.lastIndexOf('/'); | ||
126 | + return fullName.substring(delimiterPosition + 1); | ||
127 | +} | ||
128 | + | ||
67 | process.on('exit', () => { | 129 | process.on('exit', () => { |
68 | exit(0); | 130 | exit(0); |
69 | }); | 131 | }); |
@@ -21,7 +21,17 @@ const config = require('config'), | @@ -21,7 +21,17 @@ const config = require('config'), | ||
21 | logger = require('../config/logger')._logger('rabbitmqTemplate'); | 21 | logger = require('../config/logger')._logger('rabbitmqTemplate'); |
22 | 22 | ||
23 | const requestTopic = config.get('request_topic'); | 23 | const requestTopic = config.get('request_topic'); |
24 | +const host = config.get('rabbitmq.host'); | ||
25 | +const port = config.get('rabbitmq.port'); | ||
26 | +const vhost = config.get('rabbitmq.virtual_host'); | ||
27 | +const username = config.get('rabbitmq.username'); | ||
28 | +const password = config.get('rabbitmq.password'); | ||
29 | +const queueProperties = config.get('rabbitmq.queue-properties'); | ||
30 | +const poolInterval = config.get('js.response_poll_interval'); | ||
31 | + | ||
24 | const amqp = require('amqplib/callback_api'); | 32 | const amqp = require('amqplib/callback_api'); |
33 | + | ||
34 | +let queueParams = {durable: false, exclusive: false, autoDelete: false}; | ||
25 | let connection; | 35 | let connection; |
26 | let channel; | 36 | let channel; |
27 | let stopped = false; | 37 | let stopped = false; |
@@ -58,10 +68,11 @@ function RabbitMqProducer() { | @@ -58,10 +68,11 @@ function RabbitMqProducer() { | ||
58 | (async () => { | 68 | (async () => { |
59 | try { | 69 | try { |
60 | logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); | 70 | logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); |
71 | + const url = `amqp://${host}:${port}${vhost}`; | ||
61 | 72 | ||
62 | - amqp.credentials.amqplain('admin', 'password'); | 73 | + amqp.credentials.amqplain(username, password); |
63 | connection = await new Promise((resolve, reject) => { | 74 | connection = await new Promise((resolve, reject) => { |
64 | - amqp.connect('amqp://localhost:5672/', function (err, connection) { | 75 | + amqp.connect(url, function (err, connection) { |
65 | if (err) { | 76 | if (err) { |
66 | reject(err); | 77 | reject(err); |
67 | } else { | 78 | } else { |
@@ -80,6 +91,8 @@ function RabbitMqProducer() { | @@ -80,6 +91,8 @@ function RabbitMqProducer() { | ||
80 | }); | 91 | }); |
81 | }); | 92 | }); |
82 | 93 | ||
94 | + parseQueueProperties(); | ||
95 | + | ||
83 | await createQueue(requestTopic); | 96 | await createQueue(requestTopic); |
84 | 97 | ||
85 | const messageProcessor = new JsInvokeMessageProcessor(new RabbitMqProducer()); | 98 | const messageProcessor = new JsInvokeMessageProcessor(new RabbitMqProducer()); |
@@ -98,6 +111,8 @@ function RabbitMqProducer() { | @@ -98,6 +111,8 @@ function RabbitMqProducer() { | ||
98 | if (message) { | 111 | if (message) { |
99 | messageProcessor.onJsInvokeMessage(message.content.toString('utf8')); | 112 | messageProcessor.onJsInvokeMessage(message.content.toString('utf8')); |
100 | channel.ack(message); | 113 | channel.ack(message); |
114 | + } else { | ||
115 | + await sleep(poolInterval); | ||
101 | } | 116 | } |
102 | } | 117 | } |
103 | } catch (e) { | 118 | } catch (e) { |
@@ -107,10 +122,17 @@ function RabbitMqProducer() { | @@ -107,10 +122,17 @@ function RabbitMqProducer() { | ||
107 | } | 122 | } |
108 | })(); | 123 | })(); |
109 | 124 | ||
125 | +function parseQueueProperties() { | ||
126 | + const props = queueProperties.split(';'); | ||
127 | + props.forEach(p => { | ||
128 | + const delimiterPosition = p.indexOf(':'); | ||
129 | + queueParams[p.substring(0, delimiterPosition)] = p.substring(delimiterPosition + 1); | ||
130 | + }); | ||
131 | +} | ||
132 | + | ||
110 | function createQueue(topic) { | 133 | function createQueue(topic) { |
111 | - let params = {durable: false}; | ||
112 | return new Promise((resolve, reject) => { | 134 | return new Promise((resolve, reject) => { |
113 | - channel.assertQueue(topic, params, function (err, data) { | 135 | + channel.assertQueue(topic, queueParams, function (err, data) { |
114 | if (err) { | 136 | if (err) { |
115 | reject(err); | 137 | reject(err); |
116 | } else { | 138 | } else { |
@@ -120,6 +142,12 @@ function createQueue(topic) { | @@ -120,6 +142,12 @@ function createQueue(topic) { | ||
120 | }); | 142 | }); |
121 | } | 143 | } |
122 | 144 | ||
145 | +function sleep(ms) { | ||
146 | + return new Promise((resolve) => { | ||
147 | + setTimeout(resolve, ms); | ||
148 | + }); | ||
149 | +} | ||
150 | + | ||
123 | process.on('exit', () => { | 151 | process.on('exit', () => { |
124 | exit(0); | 152 | exit(0); |
125 | }); | 153 | }); |
@@ -146,4 +174,4 @@ async function exit(status) { | @@ -146,4 +174,4 @@ async function exit(status) { | ||
146 | } else { | 174 | } else { |
147 | process.exit(status); | 175 | process.exit(status); |
148 | } | 176 | } |
149 | -} | ||
177 | +} |