This is an automated email from the ASF dual-hosted git repository.
rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new c643667761 [IOTDB-3251] ForwardTrigger: support MQTT/HTTP data
forwarding (#5870)
c643667761 is described below
commit c64366776113cccb67374b4b86d65935bc4a07e2
Author: 刘威 <[email protected]>
AuthorDate: Sun May 29 15:56:10 2022 +0800
[IOTDB-3251] ForwardTrigger: support MQTT/HTTP data forwarding (#5870)
Co-authored-by: gongning <[email protected]>
---
.../org/apache/iotdb/trigger/AlertingExample.java | 5 +-
.../org/apache/iotdb/trigger/TriggerExample.java | 4 +-
integration/pom.xml | 6 +
.../db/engine/trigger/example/Accumulator.java | 13 +-
.../iotdb/db/engine/trigger/example/Counter.java | 13 +-
.../db/integration/IoTDBTriggerForwardIT.java | 305 +++++++++++++++++++++
.../resources/conf/iotdb-engine.properties | 18 +-
.../java/org/apache/iotdb/db/conf/IoTDBConfig.java | 65 +++++
.../org/apache/iotdb/db/conf/IoTDBDescriptor.java | 31 +++
.../iotdb/db/engine/trigger/api/Trigger.java | 37 +--
.../db/engine/trigger/builtin/ForwardTrigger.java | 229 ++++++++++++++++
.../db/engine/trigger/executor/TriggerEngine.java | 14 +-
.../engine/trigger/executor/TriggerExecutor.java | 43 +--
.../iotdb/db/engine/trigger/sink/api/Event.java | 8 +-
.../iotdb/db/engine/trigger/sink/api/Handler.java | 5 +
.../engine/trigger/sink/forward/ForwardEvent.java | 70 +++++
.../HTTPForwardConfiguration.java} | 31 ++-
.../{api/Event.java => http/HTTPForwardEvent.java} | 12 +-
.../trigger/sink/http/HTTPForwardHandler.java | 133 +++++++++
.../sink/mqtt/MQTTForwardConfiguration.java | 137 +++++++++
.../{api/Event.java => mqtt/MQTTForwardEvent.java} | 12 +-
.../trigger/sink/mqtt/MQTTForwardHandler.java | 97 +++++++
.../db/engine/trigger/utils/BatchHandlerQueue.java | 150 ++++++++++
.../engine/trigger/utils/HTTPConnectionPool.java | 49 ++++
.../trigger/utils/MQTTConnectionFactory.java | 115 ++++++++
.../engine/trigger/utils/MQTTConnectionPool.java | 79 ++++++
.../db/protocol/mqtt/JSONPayloadFormatter.java | 30 +-
.../metadata/idtable/trigger_example/Counter.java | 13 +-
28 files changed, 1644 insertions(+), 80 deletions(-)
diff --git
a/example/trigger/src/main/java/org/apache/iotdb/trigger/AlertingExample.java
b/example/trigger/src/main/java/org/apache/iotdb/trigger/AlertingExample.java
index 500caf2b92..48975a1dd8 100644
---
a/example/trigger/src/main/java/org/apache/iotdb/trigger/AlertingExample.java
+++
b/example/trigger/src/main/java/org/apache/iotdb/trigger/AlertingExample.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.trigger;
+import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.engine.trigger.api.Trigger;
import org.apache.iotdb.db.engine.trigger.api.TriggerAttributes;
import
org.apache.iotdb.db.engine.trigger.sink.alertmanager.AlertManagerConfiguration;
@@ -71,7 +72,7 @@ public class AlertingExample implements Trigger {
}
@Override
- public Double fire(long timestamp, Double value) throws Exception {
+ public Double fire(long timestamp, Double value, PartialPath path) throws
Exception {
if (value > 100.0) {
labels.put("value", String.valueOf(value));
labels.put("severity", "critical");
@@ -88,7 +89,7 @@ public class AlertingExample implements Trigger {
}
@Override
- public double[] fire(long[] timestamps, double[] values) throws Exception {
+ public double[] fire(long[] timestamps, double[] values, PartialPath path)
throws Exception {
for (double value : values) {
if (value > 100.0) {
labels.put("value", String.valueOf(value));
diff --git
a/example/trigger/src/main/java/org/apache/iotdb/trigger/TriggerExample.java
b/example/trigger/src/main/java/org/apache/iotdb/trigger/TriggerExample.java
index c6565172d5..7e1a6155a7 100644
--- a/example/trigger/src/main/java/org/apache/iotdb/trigger/TriggerExample.java
+++ b/example/trigger/src/main/java/org/apache/iotdb/trigger/TriggerExample.java
@@ -99,14 +99,14 @@ public class TriggerExample implements Trigger {
}
@Override
- public Double fire(long timestamp, Double value) throws Exception {
+ public Double fire(long timestamp, Double value, PartialPath path) throws
Exception {
tryOpenSinksFirstOnFire();
windowEvaluationHandler.collect(timestamp, value);
return value;
}
@Override
- public double[] fire(long[] timestamps, double[] values) throws Exception {
+ public double[] fire(long[] timestamps, double[] values, PartialPath path)
throws Exception {
tryOpenSinksFirstOnFire();
for (int i = 0; i < timestamps.length; ++i) {
windowEvaluationHandler.collect(timestamps[i], values[i]);
diff --git a/integration/pom.xml b/integration/pom.xml
index 08537c32a6..2e572d63fc 100644
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -60,6 +60,12 @@
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>${awaitility.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<dependencyManagement>
<dependencies>
diff --git
a/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Accumulator.java
b/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Accumulator.java
index dc2bcb1b07..c2d662c463 100644
---
a/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Accumulator.java
+++
b/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Accumulator.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.engine.trigger.example;
+import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.engine.trigger.api.Trigger;
import org.apache.iotdb.db.engine.trigger.api.TriggerAttributes;
import org.apache.iotdb.tsfile.exception.NotImplementedException;
@@ -48,36 +49,36 @@ public class Accumulator implements Trigger {
}
@Override
- public Integer fire(long timestamp, Integer value) {
+ public Integer fire(long timestamp, Integer value, PartialPath path) {
accumulator += value;
return value;
}
@Override
- public Long fire(long timestamp, Long value) {
+ public Long fire(long timestamp, Long value, PartialPath path) {
accumulator += value;
return value;
}
@Override
- public Float fire(long timestamp, Float value) {
+ public Float fire(long timestamp, Float value, PartialPath path) {
accumulator += value;
return value;
}
@Override
- public Double fire(long timestamp, Double value) {
+ public Double fire(long timestamp, Double value, PartialPath path) {
accumulator += value;
return value;
}
@Override
- public Boolean fire(long timestamp, Boolean value) {
+ public Boolean fire(long timestamp, Boolean value, PartialPath path) {
throw new NotImplementedException();
}
@Override
- public Binary fire(long timestamp, Binary value) {
+ public Binary fire(long timestamp, Binary value, PartialPath path) {
throw new NotImplementedException();
}
diff --git
a/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Counter.java
b/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Counter.java
index 6281e8c7b6..7f52620c20 100644
---
a/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Counter.java
+++
b/integration/src/test/java/org/apache/iotdb/db/engine/trigger/example/Counter.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.engine.trigger.example;
+import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.engine.trigger.api.Trigger;
import org.apache.iotdb.db.engine.trigger.api.TriggerAttributes;
import org.apache.iotdb.tsfile.utils.Binary;
@@ -47,37 +48,37 @@ public class Counter implements Trigger {
}
@Override
- public Integer fire(long timestamp, Integer value) {
+ public Integer fire(long timestamp, Integer value, PartialPath path) {
++counter;
return value;
}
@Override
- public Long fire(long timestamp, Long value) {
+ public Long fire(long timestamp, Long value, PartialPath path) {
++counter;
return value;
}
@Override
- public Float fire(long timestamp, Float value) {
+ public Float fire(long timestamp, Float value, PartialPath path) {
++counter;
return value;
}
@Override
- public Double fire(long timestamp, Double value) {
+ public Double fire(long timestamp, Double value, PartialPath path) {
++counter;
return value;
}
@Override
- public Boolean fire(long timestamp, Boolean value) {
+ public Boolean fire(long timestamp, Boolean value, PartialPath path) {
++counter;
return value;
}
@Override
- public Binary fire(long timestamp, Binary value) {
+ public Binary fire(long timestamp, Binary value, PartialPath path) {
++counter;
return value;
}
diff --git
a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTriggerForwardIT.java
b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTriggerForwardIT.java
new file mode 100644
index 0000000000..3473752a66
--- /dev/null
+++
b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBTriggerForwardIT.java
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.integration;
+
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.engine.trigger.sink.forward.ForwardEvent;
+import org.apache.iotdb.db.service.IoTDB;
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.iotdb.jdbc.Config;
+import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+
+import com.google.common.collect.Lists;
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.sun.net.httpserver.HttpServer;
+import io.moquette.BrokerConstants;
+import io.moquette.broker.Server;
+import io.moquette.broker.config.IConfig;
+import io.moquette.broker.config.MemoryConfig;
+import io.moquette.interception.AbstractInterceptHandler;
+import io.moquette.interception.InterceptHandler;
+import io.moquette.interception.messages.InterceptPublishMessage;
+import org.apache.http.HttpStatus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.fail;
+
+public class IoTDBTriggerForwardIT {
+ private volatile long count = 0;
+ private volatile Exception exception = null;
+
+ private HttpServer httpServer;
+ private Server mqttServer;
+
+ private final Gson gson = new Gson();
+ private final AtomicLong resultCount = new AtomicLong(0);
+
+ private final Thread dataGenerator =
+ new Thread() {
+ @Override
+ public void run() {
+ try (Connection connection =
+ DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root",
"root");
+ Statement statement = connection.createStatement()) {
+
+ do {
+ ++count;
+ statement.execute(
+ String.format(
+ "insert into
root.vehicle.a.b.c.d1(timestamp,s1,s2,s3,s4,s5,s6)
values(%d,%d,%d,%d,%d,%s,'%d')",
+ count, count, count, count, count, count % 2 == 0 ?
"true" : "false", count));
+ } while (!isInterrupted());
+ } catch (Exception e) {
+ exception = e;
+ }
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ EnvironmentUtils.envSetUp();
+ createTimeseries();
+ Class.forName(Config.JDBC_DRIVER_NAME);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ EnvironmentUtils.cleanEnv();
+ }
+
+ @Test
+ public void testForwardHTTPTrigger() throws InterruptedException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ startHTTPService();
+ statement.execute(
+ "create trigger trigger_forward_http_before before insert on
root.vehicle.a.b.c.d1.s1 "
+ + "as
'org.apache.iotdb.db.engine.trigger.builtin.ForwardTrigger' "
+ + "with ('protocol' = 'http', 'endpoint' =
'http://127.0.0.1:8080/')");
+ statement.execute(
+ "create trigger trigger_forward_http_after after insert on
root.vehicle.a.b.c.d1.s2 "
+ + "as
'org.apache.iotdb.db.engine.trigger.builtin.ForwardTrigger' "
+ + "with ('protocol' = 'http', 'endpoint' =
'http://127.0.0.1:8080/')");
+ startDataGenerator();
+ waitCountIncreaseBy(500);
+ stopDataGenerator();
+ // ensure no exception occurs when inserting data
+ if (exception != null) {
+ fail(exception.getMessage());
+ }
+
+ await().atMost(1, TimeUnit.MINUTES).until(() -> 2 * count ==
resultCount.get());
+ if (exception != null) {
+ fail(exception.getMessage());
+ }
+ } catch (Exception e) {
+ fail(e.getMessage());
+ } finally {
+ if (httpServer != null) {
+ httpServer.stop(0);
+ }
+ stopDataGenerator();
+ }
+ }
+
+ @Test
+ public void testForwardMQTTTrigger() throws InterruptedException {
+ try (Connection connection =
+ DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ startMQTTService();
+ statement.execute(
+ "create trigger trigger_forward_mqtt_before before insert on
root.vehicle.a.b.c.d1.s3 "
+ + "as
'org.apache.iotdb.db.engine.trigger.builtin.ForwardTrigger' "
+ + "with ('protocol' = 'mqtt', 'host' = '127.0.0.1', 'port' =
'1884',"
+ + " 'username' = 'root', 'password' = 'root', 'topic' =
'mqtt-test')");
+ statement.execute(
+ "create trigger trigger_forward_mqtt_after after insert on
root.vehicle.a.b.c.d1.s4 "
+ + "as
'org.apache.iotdb.db.engine.trigger.builtin.ForwardTrigger' "
+ + "with ('protocol' = 'mqtt', 'host' = '127.0.0.1', 'port' =
'1884',"
+ + " 'username' = 'root', 'password' = 'root', 'topic' =
'mqtt-test')");
+ startDataGenerator();
+ waitCountIncreaseBy(500);
+ stopDataGenerator();
+ // ensure no exception occurs when inserting data
+ if (exception != null) {
+ fail(exception.getMessage());
+ }
+
+ await().atMost(1, TimeUnit.MINUTES).until(() -> 2 * count ==
resultCount.get());
+ if (exception != null) {
+ fail(exception.getMessage());
+ }
+ } catch (SQLException | InterruptedException | IOException e) {
+ fail(e.getMessage());
+ } finally {
+ stopDataGenerator();
+ if (mqttServer != null) {
+ mqttServer.stopServer();
+ }
+ }
+ }
+
+ private void createTimeseries() throws MetadataException {
+ IoTDB.schemaProcessor.createTimeseries(
+ new PartialPath("root.vehicle.a.b.c.d1.s1"),
+ TSDataType.INT32,
+ TSEncoding.PLAIN,
+ CompressionType.UNCOMPRESSED,
+ null);
+ IoTDB.schemaProcessor.createTimeseries(
+ new PartialPath("root.vehicle.a.b.c.d1.s2"),
+ TSDataType.INT64,
+ TSEncoding.PLAIN,
+ CompressionType.UNCOMPRESSED,
+ null);
+ IoTDB.schemaProcessor.createTimeseries(
+ new PartialPath("root.vehicle.a.b.c.d1.s3"),
+ TSDataType.FLOAT,
+ TSEncoding.PLAIN,
+ CompressionType.UNCOMPRESSED,
+ null);
+ IoTDB.schemaProcessor.createTimeseries(
+ new PartialPath("root.vehicle.a.b.c.d1.s4"),
+ TSDataType.DOUBLE,
+ TSEncoding.PLAIN,
+ CompressionType.UNCOMPRESSED,
+ null);
+ IoTDB.schemaProcessor.createTimeseries(
+ new PartialPath("root.vehicle.a.b.c.d1.s5"),
+ TSDataType.BOOLEAN,
+ TSEncoding.PLAIN,
+ CompressionType.UNCOMPRESSED,
+ null);
+ IoTDB.schemaProcessor.createTimeseries(
+ new PartialPath("root.vehicle.a.b.c.d1.s6"),
+ TSDataType.TEXT,
+ TSEncoding.PLAIN,
+ CompressionType.UNCOMPRESSED,
+ null);
+ }
+
+ private void startDataGenerator() {
+ dataGenerator.start();
+ }
+
+ private void stopDataGenerator() throws InterruptedException {
+ if (!dataGenerator.isInterrupted()) {
+ dataGenerator.interrupt();
+ }
+ dataGenerator.join();
+ }
+
+ private void waitCountIncreaseBy(final long increment) throws
InterruptedException {
+ final long previous = count;
+ while (count - previous < increment) {
+ Thread.sleep(100);
+ }
+ }
+
+ private void startHTTPService() throws IOException {
+ httpServer = HttpServer.create(new InetSocketAddress(8080), 0);
+ httpServer.createContext(
+ "/",
+ exchange -> {
+ String entity = "";
+ try {
+ InputStream in = exchange.getRequestBody();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] b = new byte[1024 * 8];
+ int len;
+ while ((len = in.read(b)) != -1) {
+ out.write(b, 0, len);
+ }
+ entity = out.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (!checkPayload(entity)) {
+ exception = new IOException("HTTP forward payload error");
+ }
+ JsonArray receiveData = gson.fromJson(entity, JsonArray.class);
+ resultCount.addAndGet(receiveData.size());
+
+ exchange.sendResponseHeaders(HttpStatus.SC_OK, -1);
+ });
+ httpServer.start();
+ }
+
+ private void startMQTTService() throws IOException {
+ Properties properties = new Properties();
+ properties.setProperty(BrokerConstants.HOST_PROPERTY_NAME, "0.0.0.0");
+ properties.setProperty(BrokerConstants.PORT_PROPERTY_NAME, "1884");
+
properties.setProperty(BrokerConstants.BROKER_INTERCEPTOR_THREAD_POOL_SIZE,
"1");
+ IConfig config = new MemoryConfig(properties);
+
+ List<InterceptHandler> handlers = Lists.newArrayList(new
ForwardTestHandler());
+
+ mqttServer = new Server();
+ mqttServer.startServer(config, handlers);
+ }
+
+ private class ForwardTestHandler extends AbstractInterceptHandler {
+ @Override
+ public String getID() {
+ return "forward-test-handler";
+ }
+
+ @Override
+ public void onPublish(InterceptPublishMessage msg) {
+ String payload = msg.getPayload().toString(StandardCharsets.UTF_8);
+ if (!checkPayload(payload)) {
+ exception = new IOException("MQTT forward payload error");
+ }
+ JsonArray receiveData = gson.fromJson(payload, JsonArray.class);
+ resultCount.addAndGet(receiveData.size());
+ }
+ }
+
+ private boolean checkPayload(String payload) {
+ return payload.matches(ForwardEvent.PAYLOADS_FORMATTER_REGEX);
+ }
+}
diff --git a/server/src/assembly/resources/conf/iotdb-engine.properties
b/server/src/assembly/resources/conf/iotdb-engine.properties
index d551acf77d..97c4335e49 100644
--- a/server/src/assembly/resources/conf/iotdb-engine.properties
+++ b/server/src/assembly/resources/conf/iotdb-engine.properties
@@ -1037,4 +1037,20 @@ timestamp_precision=ms
# The cache size for schema page in one schema file
# A bigger cache makes it faster but costs more space and more volatile when
evicts item from cache
# Datatype: int
-# page_cache_in_schema_file=1024
\ No newline at end of file
+# page_cache_in_schema_file=1024
+
+####################
+### Trigger Forward
+####################
+# Number of queues per forwarding trigger
+trigger_forward_max_queue_number=8
+# The length of one of the queues per forwarding trigger
+trigger_forward_max_size_per_queue=2000
+# Trigger forwarding data size per batch
+trigger_forward_batch_size=50
+# Trigger HTTP forward pool size
+trigger_forward_http_pool_size=200
+# Trigger HTTP forward pool max connection for per route
+trigger_forward_http_pool_max_per_route=20
+# Trigger MQTT forward pool size
+trigger_forward_mqtt_pool_size=4
\ No newline at end of file
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index 4d3be15291..9087355e00 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -920,6 +920,23 @@ public class IoTDBConfig {
/** Cache expire time of user and role */
private int authorCacheExpireTime = 30;
+ /** Number of queues per forwarding trigger */
+ private int triggerForwardMaxQueueNumber = 8;
+ /** The length of one of the queues per forwarding trigger */
+ private int triggerForwardMaxSizePerQueue = 2000;
+
+ /** Trigger forwarding data size per batch */
+ private int triggerForwardBatchSize = 50;
+
+ /** Trigger HTTP forward pool size */
+ private int triggerForwardHTTPPoolSize = 200;
+
+ /** Trigger HTTP forward pool max connection for per route */
+ private int triggerForwardHTTPPOOLMaxPerRoute = 20;
+
+ /** Trigger MQTT forward pool size */
+ private int triggerForwardMQTTPoolSize = 4;
+
IoTDBConfig() {}
public float getUdfMemoryBudgetInMB() {
@@ -2863,4 +2880,52 @@ public class IoTDBConfig {
public void setAuthorCacheExpireTime(int authorCacheExpireTime) {
this.authorCacheExpireTime = authorCacheExpireTime;
}
+
+ public int getTriggerForwardMaxQueueNumber() {
+ return triggerForwardMaxQueueNumber;
+ }
+
+ public void setTriggerForwardMaxQueueNumber(int
triggerForwardMaxQueueNumber) {
+ this.triggerForwardMaxQueueNumber = triggerForwardMaxQueueNumber;
+ }
+
+ public int getTriggerForwardMaxSizePerQueue() {
+ return triggerForwardMaxSizePerQueue;
+ }
+
+ public void setTriggerForwardMaxSizePerQueue(int
triggerForwardMaxSizePerQueue) {
+ this.triggerForwardMaxSizePerQueue = triggerForwardMaxSizePerQueue;
+ }
+
+ public int getTriggerForwardBatchSize() {
+ return triggerForwardBatchSize;
+ }
+
+ public void setTriggerForwardBatchSize(int triggerForwardBatchSize) {
+ this.triggerForwardBatchSize = triggerForwardBatchSize;
+ }
+
+ public int getTriggerForwardHTTPPoolSize() {
+ return triggerForwardHTTPPoolSize;
+ }
+
+ public void setTriggerForwardHTTPPoolSize(int triggerForwardHTTPPoolSize) {
+ this.triggerForwardHTTPPoolSize = triggerForwardHTTPPoolSize;
+ }
+
+ public int getTriggerForwardHTTPPOOLMaxPerRoute() {
+ return triggerForwardHTTPPOOLMaxPerRoute;
+ }
+
+ public void setTriggerForwardHTTPPOOLMaxPerRoute(int
triggerForwardHTTPPOOLMaxPerRoute) {
+ this.triggerForwardHTTPPOOLMaxPerRoute = triggerForwardHTTPPOOLMaxPerRoute;
+ }
+
+ public int getTriggerForwardMQTTPoolSize() {
+ return triggerForwardMQTTPoolSize;
+ }
+
+ public void setTriggerForwardMQTTPoolSize(int triggerForwardMQTTPoolSize) {
+ this.triggerForwardMQTTPoolSize = triggerForwardMQTTPoolSize;
+ }
}
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index f93f78331d..9db6ea1774 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -1514,6 +1514,37 @@ public class IoTDBDescriptor {
if (tlogBufferSize > 0) {
conf.setTlogBufferSize(tlogBufferSize);
}
+
+ conf.setTriggerForwardMaxQueueNumber(
+ Integer.parseInt(
+ properties.getProperty(
+ "trigger_forward_max_queue_number",
+ Integer.toString(conf.getTriggerForwardMaxQueueNumber()))));
+ conf.setTriggerForwardMaxSizePerQueue(
+ Integer.parseInt(
+ properties.getProperty(
+ "trigger_forward_max_size_per_queue",
+ Integer.toString(conf.getTriggerForwardMaxSizePerQueue()))));
+ conf.setTriggerForwardBatchSize(
+ Integer.parseInt(
+ properties.getProperty(
+ "trigger_forward_batch_size",
+ Integer.toString(conf.getTriggerForwardBatchSize()))));
+ conf.setTriggerForwardHTTPPoolSize(
+ Integer.parseInt(
+ properties.getProperty(
+ "trigger_forward_http_pool_size",
+ Integer.toString(conf.getTriggerForwardHTTPPoolSize()))));
+ conf.setTriggerForwardHTTPPOOLMaxPerRoute(
+ Integer.parseInt(
+ properties.getProperty(
+ "trigger_forward_http_pool_max_per_route",
+
Integer.toString(conf.getTriggerForwardHTTPPOOLMaxPerRoute()))));
+ conf.setTriggerForwardMQTTPoolSize(
+ Integer.parseInt(
+ properties.getProperty(
+ "trigger_forward_mqtt_pool_size",
+ Integer.toString(conf.getTriggerForwardMQTTPoolSize()))));
}
private void loadCQProps(Properties properties) {
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/api/Trigger.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/api/Trigger.java
index 9a2b9f16b4..48f9c227f3 100644
--- a/server/src/main/java/org/apache/iotdb/db/engine/trigger/api/Trigger.java
+++ b/server/src/main/java/org/apache/iotdb/db/engine/trigger/api/Trigger.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.engine.trigger.api;
+import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.tsfile.utils.Binary;
/** User Guide: docs/UserGuide/Operation Manual/Triggers.md */
@@ -37,79 +38,79 @@ public interface Trigger {
default void onStop() throws Exception {}
@SuppressWarnings("squid:S112")
- default Integer fire(long timestamp, Integer value) throws Exception {
+ default Integer fire(long timestamp, Integer value, PartialPath path) throws
Exception {
return value;
}
- default int[] fire(long[] timestamps, int[] values) throws Exception {
+ default int[] fire(long[] timestamps, int[] values, PartialPath path) throws
Exception {
int size = timestamps.length;
for (int i = 0; i < size; ++i) {
- fire(timestamps[i], values[i]);
+ fire(timestamps[i], values[i], path);
}
return values;
}
@SuppressWarnings("squid:S112")
- default Long fire(long timestamp, Long value) throws Exception {
+ default Long fire(long timestamp, Long value, PartialPath path) throws
Exception {
return value;
}
- default long[] fire(long[] timestamps, long[] values) throws Exception {
+ default long[] fire(long[] timestamps, long[] values, PartialPath path)
throws Exception {
int size = timestamps.length;
for (int i = 0; i < size; ++i) {
- fire(timestamps[i], values[i]);
+ fire(timestamps[i], values[i], path);
}
return values;
}
@SuppressWarnings("squid:S112")
- default Float fire(long timestamp, Float value) throws Exception {
+ default Float fire(long timestamp, Float value, PartialPath path) throws
Exception {
return value;
}
- default float[] fire(long[] timestamps, float[] values) throws Exception {
+ default float[] fire(long[] timestamps, float[] values, PartialPath path)
throws Exception {
int size = timestamps.length;
for (int i = 0; i < size; ++i) {
- fire(timestamps[i], values[i]);
+ fire(timestamps[i], values[i], path);
}
return values;
}
@SuppressWarnings("squid:S112")
- default Double fire(long timestamp, Double value) throws Exception {
+ default Double fire(long timestamp, Double value, PartialPath path) throws
Exception {
return value;
}
- default double[] fire(long[] timestamps, double[] values) throws Exception {
+ default double[] fire(long[] timestamps, double[] values, PartialPath path)
throws Exception {
int size = timestamps.length;
for (int i = 0; i < size; ++i) {
- fire(timestamps[i], values[i]);
+ fire(timestamps[i], values[i], path);
}
return values;
}
@SuppressWarnings("squid:S112")
- default Boolean fire(long timestamp, Boolean value) throws Exception {
+ default Boolean fire(long timestamp, Boolean value, PartialPath path) throws
Exception {
return value;
}
- default boolean[] fire(long[] timestamps, boolean[] values) throws Exception
{
+ default boolean[] fire(long[] timestamps, boolean[] values, PartialPath
path) throws Exception {
int size = timestamps.length;
for (int i = 0; i < size; ++i) {
- fire(timestamps[i], values[i]);
+ fire(timestamps[i], values[i], path);
}
return values;
}
@SuppressWarnings("squid:S112")
- default Binary fire(long timestamp, Binary value) throws Exception {
+ default Binary fire(long timestamp, Binary value, PartialPath path) throws
Exception {
return value;
}
- default Binary[] fire(long[] timestamps, Binary[] values) throws Exception {
+ default Binary[] fire(long[] timestamps, Binary[] values, PartialPath path)
throws Exception {
int size = timestamps.length;
for (int i = 0; i < size; ++i) {
- fire(timestamps[i], values[i]);
+ fire(timestamps[i], values[i], path);
}
return values;
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/builtin/ForwardTrigger.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/builtin/ForwardTrigger.java
new file mode 100644
index 0000000000..0a22cf4d7e
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/builtin/ForwardTrigger.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.builtin;
+
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.engine.trigger.api.Trigger;
+import org.apache.iotdb.db.engine.trigger.api.TriggerAttributes;
+import org.apache.iotdb.db.engine.trigger.sink.api.Configuration;
+import org.apache.iotdb.db.engine.trigger.sink.api.Event;
+import org.apache.iotdb.db.engine.trigger.sink.api.Handler;
+import org.apache.iotdb.db.engine.trigger.sink.exception.SinkException;
+import org.apache.iotdb.db.engine.trigger.sink.http.HTTPForwardConfiguration;
+import org.apache.iotdb.db.engine.trigger.sink.http.HTTPForwardEvent;
+import org.apache.iotdb.db.engine.trigger.sink.http.HTTPForwardHandler;
+import org.apache.iotdb.db.engine.trigger.sink.mqtt.MQTTForwardConfiguration;
+import org.apache.iotdb.db.engine.trigger.sink.mqtt.MQTTForwardEvent;
+import org.apache.iotdb.db.engine.trigger.sink.mqtt.MQTTForwardHandler;
+import org.apache.iotdb.db.engine.trigger.utils.BatchHandlerQueue;
+import org.apache.iotdb.db.exception.TriggerExecutionException;
+import org.apache.iotdb.tsfile.utils.Binary;
+
+import java.util.HashMap;
+
+public class ForwardTrigger implements Trigger {
+
+ private static final String PROTOCOL_HTTP = "http";
+ private static final String PROTOCOL_MQTT = "mqtt";
+
+ private Handler forwardHandler;
+ private Configuration forwardConfig;
+ private BatchHandlerQueue<Event> queue;
+ private final HashMap<String, String> labels = new HashMap<>();
+ private String protocol;
+
+ @Override
+ public void onCreate(TriggerAttributes attributes) throws Exception {
+ protocol = attributes.getStringOrDefault("protocol",
PROTOCOL_HTTP).toLowerCase();
+ int queueNumber = attributes.getIntOrDefault("queueNumber", 8);
+ int queueSize = attributes.getIntOrDefault("queueSize", 2000);
+ int batchSize = attributes.getIntOrDefault("batchSize", 50);
+
+ switch (protocol) {
+ case PROTOCOL_HTTP:
+ forwardConfig = createHTTPConfiguration(attributes);
+ forwardHandler = new HTTPForwardHandler();
+ break;
+ case PROTOCOL_MQTT:
+ forwardConfig = createMQTTConfiguration(attributes);
+ forwardHandler = new MQTTForwardHandler();
+ break;
+ default:
+ throw new TriggerExecutionException("Forward protocol doesn't
support.");
+ }
+ queue = new BatchHandlerQueue<>(queueNumber, queueSize, batchSize,
forwardHandler);
+ forwardHandler.open(forwardConfig);
+ }
+
+ private HTTPForwardConfiguration createHTTPConfiguration(TriggerAttributes
attributes)
+ throws SinkException {
+ String endpoint = attributes.getString("endpoint");
+ boolean stopIfException =
attributes.getBooleanOrDefault("stopIfException", false);
+ HTTPForwardConfiguration forwardConfig =
+ new HTTPForwardConfiguration(endpoint, stopIfException);
+ forwardConfig.checkConfig();
+ return forwardConfig;
+ }
+
+ private MQTTForwardConfiguration createMQTTConfiguration(TriggerAttributes
attributes)
+ throws SinkException {
+ String host = attributes.getString("host");
+ int port = attributes.getInt("port");
+ String username = attributes.getString("username");
+ String password = attributes.getString("password");
+ String topic = attributes.getString("topic");
+ long reconnectDelay = attributes.getLongOrDefault("reconnectDelay", 10L);
+ long connectAttemptsMax =
attributes.getLongOrDefault("connectAttemptsMax", 3L);
+ String qos = attributes.getStringOrDefault("qos", "exactly_once");
+ int poolSize = attributes.getIntOrDefault("poolSize", 4);
+ boolean retain = attributes.getBooleanOrDefault("retain", false);
+ boolean stopIfException =
attributes.getBooleanOrDefault("stopIfException", false);
+
+ MQTTForwardConfiguration forwardConfig =
+ new MQTTForwardConfiguration(
+ host,
+ port,
+ username,
+ password,
+ topic,
+ reconnectDelay,
+ connectAttemptsMax,
+ qos,
+ retain,
+ poolSize,
+ stopIfException);
+ forwardConfig.checkConfig();
+ return forwardConfig;
+ }
+
+ @Override
+ public void onDrop() throws Exception {
+ forwardHandler.close();
+ }
+
+ @Override
+ public void onStart() throws Exception {
+ forwardHandler.open(forwardConfig);
+ }
+
+ @Override
+ public void onStop() throws Exception {
+ forwardHandler.close();
+ }
+
+ private void offerEventToQueue(long timestamp, Object value, PartialPath
path) throws Exception {
+ Event event;
+ switch (protocol) {
+ case PROTOCOL_HTTP:
+ event = new HTTPForwardEvent(timestamp, value, path);
+ break;
+ case PROTOCOL_MQTT:
+ event = new MQTTForwardEvent(timestamp, value, path);
+ break;
+ default:
+ throw new TriggerExecutionException("Forward protocol doesn't
support.");
+ }
+ queue.offer(event);
+ }
+
+ @Override
+ public Integer fire(long timestamp, Integer value, PartialPath path) throws
Exception {
+ offerEventToQueue(timestamp, value, path);
+ return value;
+ }
+
+ @Override
+ public int[] fire(long[] timestamps, int[] values, PartialPath path) throws
Exception {
+ for (int i = 0; i < timestamps.length; i++) {
+ offerEventToQueue(timestamps[i], values[i], path);
+ }
+ return values;
+ }
+
+ @Override
+ public Long fire(long timestamp, Long value, PartialPath path) throws
Exception {
+ offerEventToQueue(timestamp, value, path);
+ return value;
+ }
+
+ @Override
+ public long[] fire(long[] timestamps, long[] values, PartialPath path)
throws Exception {
+ for (int i = 0; i < timestamps.length; i++) {
+ offerEventToQueue(timestamps[i], values[i], path);
+ }
+ return values;
+ }
+
+ @Override
+ public Float fire(long timestamp, Float value, PartialPath path) throws
Exception {
+ offerEventToQueue(timestamp, value, path);
+ return value;
+ }
+
+ @Override
+ public float[] fire(long[] timestamps, float[] values, PartialPath path)
throws Exception {
+ for (int i = 0; i < timestamps.length; i++) {
+ offerEventToQueue(timestamps[i], values[i], path);
+ }
+ return values;
+ }
+
+ @Override
+ public Double fire(long timestamp, Double value, PartialPath path) throws
Exception {
+ offerEventToQueue(timestamp, value, path);
+ return value;
+ }
+
+ @Override
+ public double[] fire(long[] timestamps, double[] values, PartialPath path)
throws Exception {
+ for (int i = 0; i < timestamps.length; i++) {
+ offerEventToQueue(timestamps[i], values[i], path);
+ }
+ return values;
+ }
+
+ @Override
+ public Boolean fire(long timestamp, Boolean value, PartialPath path) throws
Exception {
+ offerEventToQueue(timestamp, value, path);
+ return value;
+ }
+
+ @Override
+ public boolean[] fire(long[] timestamps, boolean[] values, PartialPath path)
throws Exception {
+ for (int i = 0; i < timestamps.length; i++) {
+ offerEventToQueue(timestamps[i], values[i], path);
+ }
+ return values;
+ }
+
+ @Override
+ public Binary fire(long timestamp, Binary value, PartialPath path) throws
Exception {
+ offerEventToQueue(timestamp, value, path);
+ return value;
+ }
+
+ @Override
+ public Binary[] fire(long[] timestamps, Binary[] values, PartialPath path)
throws Exception {
+ for (int i = 0; i < timestamps.length; i++) {
+ offerEventToQueue(timestamps[i], values[i], path);
+ }
+ return values;
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerEngine.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerEngine.java
index fcf17622ce..07605f6d07 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerEngine.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerEngine.java
@@ -56,7 +56,12 @@ public class TriggerEngine {
continue;
}
for (TriggerExecutor executor : mNode.getUpperTriggerExecutorList()) {
- executor.fireIfActivated(event, timestamp, values[i],
mNode.getSchema().getType());
+ executor.fireIfActivated(
+ event,
+ timestamp,
+ values[i],
+ mNode.getSchema().getType(),
+ insertRowPlan.getPaths().get(i));
}
}
}
@@ -83,7 +88,12 @@ public class TriggerEngine {
continue;
}
for (TriggerExecutor executor : mNode.getUpperTriggerExecutorList()) {
- executor.fireIfActivated(event, timestamps, columns[i],
mNode.getSchema().getType());
+ executor.fireIfActivated(
+ event,
+ timestamps,
+ columns[i],
+ mNode.getSchema().getType(),
+ insertTabletPlan.getPaths().get(i));
}
}
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerExecutor.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerExecutor.java
index ea26758559..732b4c0a62 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerExecutor.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/executor/TriggerExecutor.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.engine.trigger.executor;
+import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.utils.TestOnly;
import org.apache.iotdb.db.engine.trigger.api.Trigger;
import org.apache.iotdb.db.engine.trigger.api.TriggerAttributes;
@@ -136,36 +137,37 @@ public class TriggerExecutor {
}
public void fireIfActivated(
- TriggerEvent event, long timestamp, Object value, TSDataType
seriesDataType)
+ TriggerEvent event, long timestamp, Object value, TSDataType
seriesDataType, PartialPath path)
throws TriggerExecutionException {
if (!registrationInformation.isStopped() &&
event.equals(registrationInformation.getEvent())) {
- fire(timestamp, value, seriesDataType);
+ fire(timestamp, value, seriesDataType, path);
}
}
- private synchronized void fire(long timestamp, Object value, TSDataType
seriesDataType)
+ private synchronized void fire(
+ long timestamp, Object value, TSDataType seriesDataType, PartialPath
path)
throws TriggerExecutionException {
Thread.currentThread().setContextClassLoader(classLoader);
try {
switch (seriesDataType) {
case INT32:
- trigger.fire(timestamp, (Integer) value);
+ trigger.fire(timestamp, (Integer) value, path);
break;
case INT64:
- trigger.fire(timestamp, (Long) value);
+ trigger.fire(timestamp, (Long) value, path);
break;
case FLOAT:
- trigger.fire(timestamp, (Float) value);
+ trigger.fire(timestamp, (Float) value, path);
break;
case DOUBLE:
- trigger.fire(timestamp, (Double) value);
+ trigger.fire(timestamp, (Double) value, path);
break;
case BOOLEAN:
- trigger.fire(timestamp, (Boolean) value);
+ trigger.fire(timestamp, (Boolean) value, path);
break;
case TEXT:
- trigger.fire(timestamp, (Binary) value);
+ trigger.fire(timestamp, (Binary) value, path);
break;
default:
throw new TriggerExecutionException("Unsupported series data type.");
@@ -178,36 +180,41 @@ public class TriggerExecutor {
}
public void fireIfActivated(
- TriggerEvent event, long[] timestamps, Object values, TSDataType
seriesDataType)
+ TriggerEvent event,
+ long[] timestamps,
+ Object values,
+ TSDataType seriesDataType,
+ PartialPath path)
throws TriggerExecutionException {
if (!registrationInformation.isStopped() &&
event.equals(registrationInformation.getEvent())) {
- fire(timestamps, values, seriesDataType);
+ fire(timestamps, values, seriesDataType, path);
}
}
- private synchronized void fire(long[] timestamps, Object values, TSDataType
seriesDataType)
+ private synchronized void fire(
+ long[] timestamps, Object values, TSDataType seriesDataType, PartialPath
path)
throws TriggerExecutionException {
Thread.currentThread().setContextClassLoader(classLoader);
try {
switch (seriesDataType) {
case INT32:
- trigger.fire(timestamps, (int[]) values);
+ trigger.fire(timestamps, (int[]) values, path);
break;
case INT64:
- trigger.fire(timestamps, (long[]) values);
+ trigger.fire(timestamps, (long[]) values, path);
break;
case FLOAT:
- trigger.fire(timestamps, (float[]) values);
+ trigger.fire(timestamps, (float[]) values, path);
break;
case DOUBLE:
- trigger.fire(timestamps, (double[]) values);
+ trigger.fire(timestamps, (double[]) values, path);
break;
case BOOLEAN:
- trigger.fire(timestamps, (boolean[]) values);
+ trigger.fire(timestamps, (boolean[]) values, path);
break;
case TEXT:
- trigger.fire(timestamps, (Binary[]) values);
+ trigger.fire(timestamps, (Binary[]) values, path);
break;
default:
throw new TriggerExecutionException("Unsupported series data type.");
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
index 3b3b94acbc..c46e4dd187 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
@@ -19,4 +19,10 @@
package org.apache.iotdb.db.engine.trigger.sink.api;
-public interface Event {}
+import org.apache.iotdb.commons.path.PartialPath;
+
+public interface Event {
+ default PartialPath getFullPath() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
index dbdf62061e..5f98d2005a 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
@@ -19,6 +19,8 @@
package org.apache.iotdb.db.engine.trigger.sink.api;
+import java.util.List;
+
public interface Handler<C extends Configuration, E extends Event> {
@SuppressWarnings("squid:S112")
@@ -29,4 +31,7 @@ public interface Handler<C extends Configuration, E extends
Event> {
@SuppressWarnings("squid:S112")
void onEvent(E event) throws Exception;
+
+ @SuppressWarnings("squid:S112")
+ default void onEvent(List<E> events) throws Exception {}
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/forward/ForwardEvent.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/forward/ForwardEvent.java
new file mode 100644
index 0000000000..c38886ee40
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/forward/ForwardEvent.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.sink.forward;
+
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.engine.trigger.sink.api.Event;
+import org.apache.iotdb.tsfile.utils.Binary;
+
+public class ForwardEvent implements Event {
+ private final long timestamp;
+ private final Object value;
+ private final PartialPath fullPath;
+
+ private static final String PAYLOAD_FORMATTER =
+
"{\"device\":\"%s\",\"measurement\":\"%s\",\"timestamp\":%d,\"value\":%s}";
+
+ public static final String PAYLOADS_FORMATTER_REGEX =
+
"\\[(\\{\"device\":\".*\",\"measurement\":\".*\",\"timestamp\":\\d*,\"value\":.*},)*"
+ +
"(\\{\"device\":\".*\",\"measurement\":\".*\",\"timestamp\":\\d*,\"value\":.*})]";
+
+ public ForwardEvent(long timestamp, Object value, PartialPath fullPath) {
+ this.timestamp = timestamp;
+ this.value = value;
+ this.fullPath = fullPath;
+ }
+
+ public String toJsonString() {
+ return String.format(
+ PAYLOAD_FORMATTER,
+ fullPath.getDevice(),
+ fullPath.getMeasurement(),
+ timestamp,
+ objectToJson(value));
+ }
+
+ private static String objectToJson(Object object) {
+ return (object instanceof String || object instanceof Binary)
+ ? ('\"' + object.toString() + '\"')
+ : object.toString();
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public PartialPath getFullPath() {
+ return fullPath;
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardConfiguration.java
similarity index 50%
copy from
server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
copy to
server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardConfiguration.java
index dbdf62061e..530b784615 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Handler.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardConfiguration.java
@@ -17,16 +17,31 @@
* under the License.
*/
-package org.apache.iotdb.db.engine.trigger.sink.api;
+package org.apache.iotdb.db.engine.trigger.sink.http;
-public interface Handler<C extends Configuration, E extends Event> {
+import org.apache.iotdb.db.engine.trigger.sink.api.Configuration;
+import org.apache.iotdb.db.engine.trigger.sink.exception.SinkException;
- @SuppressWarnings("squid:S112")
- default void open(C configuration) throws Exception {}
+public class HTTPForwardConfiguration implements Configuration {
+ private final String endpoint;
+ private final boolean stopIfException;
- @SuppressWarnings("squid:S112")
- default void close() throws Exception {}
+ public HTTPForwardConfiguration(String endpoint, boolean stopIfException) {
+ this.endpoint = endpoint;
+ this.stopIfException = stopIfException;
+ }
- @SuppressWarnings("squid:S112")
- void onEvent(E event) throws Exception;
+ public void checkConfig() throws SinkException {
+ if (endpoint == null || endpoint.isEmpty()) {
+ throw new SinkException("HTTP config item error");
+ }
+ }
+
+ public boolean isStopIfException() {
+ return stopIfException;
+ }
+
+ public String getEndpoint() {
+ return endpoint;
+ }
}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardEvent.java
similarity index 69%
copy from
server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
copy to
server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardEvent.java
index 3b3b94acbc..bfb407a566 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardEvent.java
@@ -17,6 +17,14 @@
* under the License.
*/
-package org.apache.iotdb.db.engine.trigger.sink.api;
+package org.apache.iotdb.db.engine.trigger.sink.http;
-public interface Event {}
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.engine.trigger.sink.forward.ForwardEvent;
+
+public class HTTPForwardEvent extends ForwardEvent {
+
+ public HTTPForwardEvent(long timestamp, Object value, PartialPath fullPath) {
+ super(timestamp, value, fullPath);
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardHandler.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardHandler.java
new file mode 100644
index 0000000000..1c091653ad
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/http/HTTPForwardHandler.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.sink.http;
+
+import org.apache.iotdb.db.engine.trigger.sink.api.Handler;
+import org.apache.iotdb.db.engine.trigger.sink.exception.SinkException;
+import org.apache.iotdb.db.engine.trigger.utils.HTTPConnectionPool;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+
+public class HTTPForwardHandler implements Handler<HTTPForwardConfiguration,
HTTPForwardEvent> {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(HTTPForwardHandler.class);
+
+ private static CloseableHttpClient client;
+ private static int referenceCount;
+
+ private HttpPost request;
+ private HTTPForwardConfiguration config;
+
+ private static synchronized void closeClient() throws IOException {
+ if (--referenceCount == 0) {
+ client.close();
+ }
+ }
+
+ private static synchronized void openClient() {
+ if (referenceCount++ == 0) {
+ PoolingHttpClientConnectionManager connectionManager =
HTTPConnectionPool.getInstance();
+ client =
HttpClients.custom().setConnectionManager(connectionManager).build();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ closeClient();
+ }
+
+ @Override
+ public void open(HTTPForwardConfiguration config) {
+ this.config = config;
+ if (this.request == null) {
+ this.request = new HttpPost(config.getEndpoint());
+ request.setHeader("Accept", "application/json");
+ request.setHeader("Content-type", "application/json");
+ }
+
+ openClient();
+ }
+
+ @Override
+ public void onEvent(HTTPForwardEvent event) throws SinkException {
+ CloseableHttpResponse response = null;
+ try {
+ request.setEntity(new StringEntity("[" + event.toJsonString() + "]"));
+ response = client.execute(request);
+ if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+ throw new SinkException(response.getStatusLine().toString());
+ }
+ } catch (Exception e) {
+ if (config.isStopIfException()) {
+ throw new SinkException("HTTP Forward Exception", e);
+ }
+ LOGGER.error("HTTP Forward Exception", e);
+ } finally {
+ try {
+ if (null != response) {
+ response.close();
+ }
+ } catch (IOException e) {
+ LOGGER.error("Connection Close Exception", e);
+ }
+ }
+ }
+
+ @Override
+ public void onEvent(List<HTTPForwardEvent> events) throws SinkException {
+ CloseableHttpResponse response = null;
+ try {
+ StringBuilder sb = new StringBuilder().append("[");
+ for (HTTPForwardEvent event : events) {
+ sb.append(event.toJsonString()).append(", ");
+ }
+ sb.replace(sb.lastIndexOf(", "), sb.length(), "").append("]");
+ request.setEntity(new StringEntity(sb.toString()));
+ response = client.execute(request);
+ if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+ throw new SinkException(response.getStatusLine().toString());
+ }
+ } catch (Exception e) {
+ if (config.isStopIfException()) {
+ throw new SinkException("HTTP Forward Exception", e);
+ }
+ LOGGER.error("HTTP Forward Exception", e);
+ } finally {
+ try {
+ if (null != response) {
+ response.close();
+ }
+ } catch (IOException e) {
+ LOGGER.error("Connection Close Exception", e);
+ }
+ }
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardConfiguration.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardConfiguration.java
new file mode 100644
index 0000000000..820ac925fe
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardConfiguration.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.sink.mqtt;
+
+import org.apache.iotdb.db.engine.trigger.sink.api.Configuration;
+import org.apache.iotdb.db.engine.trigger.sink.exception.SinkException;
+
+import org.fusesource.mqtt.client.QoS;
+
+public class MQTTForwardConfiguration implements Configuration {
+ private final String host;
+ private final int port;
+ private final String username;
+ private final String password;
+ private final String topic;
+ private final long reconnectDelay;
+ private final long connectAttemptsMax;
+ private final QoS qos;
+ private final boolean retain;
+ private final int poolSize;
+ private final boolean stopIfException;
+
+ public MQTTForwardConfiguration(
+ String host,
+ int port,
+ String username,
+ String password,
+ String topic,
+ long reconnectDelay,
+ long connectAttemptsMax,
+ String qos,
+ boolean retain,
+ int poolSize,
+ boolean stopIfException)
+ throws SinkException {
+ this.host = host;
+ this.port = port;
+ this.username = username;
+ this.password = password;
+ this.topic = topic;
+ this.reconnectDelay = reconnectDelay;
+ this.connectAttemptsMax = connectAttemptsMax;
+ this.qos = parseQoS(qos);
+ this.retain = retain;
+ this.poolSize = poolSize;
+ this.stopIfException = stopIfException;
+ }
+
+ private static QoS parseQoS(String qos) throws SinkException {
+ switch (qos.toLowerCase()) {
+ case "exactly_once":
+ return QoS.EXACTLY_ONCE;
+ case "at_least_once":
+ return QoS.AT_LEAST_ONCE;
+ case "at_most_once":
+ return QoS.AT_MOST_ONCE;
+ default:
+ throw new SinkException("Unable to identify QoS config");
+ }
+ }
+
+ public void checkConfig() throws SinkException {
+ if (host == null
+ || host.isEmpty()
+ || port < 0
+ || port > 65535
+ || username == null
+ || username.isEmpty()
+ || password == null
+ || password.isEmpty()
+ || topic == null
+ || topic.isEmpty()) {
+ throw new SinkException("MQTT config item error");
+ }
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getTopic() {
+ return topic;
+ }
+
+ public long getReconnectDelay() {
+ return reconnectDelay;
+ }
+
+ public long getConnectAttemptsMax() {
+ return connectAttemptsMax;
+ }
+
+ public QoS getQos() {
+ return qos;
+ }
+
+ public boolean isRetain() {
+ return retain;
+ }
+
+ public int getPoolSize() {
+ return poolSize;
+ }
+
+ public boolean isStopIfException() {
+ return stopIfException;
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardEvent.java
similarity index 69%
copy from
server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
copy to
server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardEvent.java
index 3b3b94acbc..bd99824ab1 100644
---
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/api/Event.java
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardEvent.java
@@ -17,6 +17,14 @@
* under the License.
*/
-package org.apache.iotdb.db.engine.trigger.sink.api;
+package org.apache.iotdb.db.engine.trigger.sink.mqtt;
-public interface Event {}
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.engine.trigger.sink.forward.ForwardEvent;
+
+public class MQTTForwardEvent extends ForwardEvent {
+
+ public MQTTForwardEvent(long timestamp, Object value, PartialPath fullPath) {
+ super(timestamp, value, fullPath);
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardHandler.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardHandler.java
new file mode 100644
index 0000000000..175a9be5b9
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/sink/mqtt/MQTTForwardHandler.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.sink.mqtt;
+
+import org.apache.iotdb.db.engine.trigger.sink.api.Handler;
+import org.apache.iotdb.db.engine.trigger.sink.exception.SinkException;
+import org.apache.iotdb.db.engine.trigger.utils.MQTTConnectionFactory;
+import org.apache.iotdb.db.engine.trigger.utils.MQTTConnectionPool;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class MQTTForwardHandler implements Handler<MQTTForwardConfiguration,
MQTTForwardEvent> {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(MQTTForwardHandler.class);
+
+ private MQTTConnectionPool connectionPool;
+ private MQTTForwardConfiguration config;
+
+ @Override
+ public void open(MQTTForwardConfiguration config) throws Exception {
+ this.config = config;
+ MQTTConnectionFactory factory =
+ new MQTTConnectionFactory(
+ config.getHost(),
+ config.getPort(),
+ config.getUsername(),
+ config.getPassword(),
+ config.getConnectAttemptsMax(),
+ config.getReconnectDelay());
+ connectionPool =
+ MQTTConnectionPool.getInstance(
+ config.getHost(),
+ config.getPort(),
+ config.getUsername(),
+ factory,
+ config.getPoolSize());
+ }
+
+ @Override
+ public void close() throws Exception {
+ connectionPool.clearAndClose();
+ }
+
+ @Override
+ public void onEvent(MQTTForwardEvent event) throws SinkException {
+ try {
+ connectionPool.publish(
+ config.getTopic(),
+ ("[" + event.toJsonString() + "]").getBytes(),
+ config.getQos(),
+ config.isRetain());
+ } catch (Exception e) {
+ if (config.isStopIfException()) {
+ throw new SinkException("MQTT Forward Exception", e);
+ }
+ LOGGER.error("MQTT Forward Exception", e);
+ }
+ }
+
+ @Override
+ public void onEvent(List<MQTTForwardEvent> events) throws SinkException {
+ StringBuilder sb = new StringBuilder().append("[");
+ for (MQTTForwardEvent event : events) {
+ sb.append(event.toJsonString()).append(", ");
+ }
+ sb.replace(sb.lastIndexOf(", "), sb.length(), "").append("]");
+ try {
+ connectionPool.publish(
+ config.getTopic(), sb.toString().getBytes(), config.getQos(),
config.isRetain());
+ } catch (Exception e) {
+ if (config.isStopIfException()) {
+ throw new SinkException("MQTT Forward Exception", e);
+ }
+ LOGGER.error("MQTT Forward Exception", e);
+ }
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/BatchHandlerQueue.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/BatchHandlerQueue.java
new file mode 100644
index 0000000000..bf5d478e8a
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/BatchHandlerQueue.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.utils;
+
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.engine.trigger.sink.api.Event;
+import org.apache.iotdb.db.engine.trigger.sink.api.Handler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Each Trigger instantiate a ForwardQueue
+ *
+ * @param <T> Subclass of Event
+ */
+public class BatchHandlerQueue<T extends Event> {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(BatchHandlerQueue.class);
+
+ private final int queueNumber;
+ private final int queueSize;
+ private final int batchSize;
+ private final AtomicInteger atomicCount = new AtomicInteger();
+
+ private final ArrayBlockingQueue<T>[] queues;
+
+ private final Handler handler;
+
+ public BatchHandlerQueue(int queueNumber, int queueSize, int batchSize,
Handler handler) {
+ this.queueNumber =
+ Math.min(
+ queueNumber,
+
IoTDBDescriptor.getInstance().getConfig().getTriggerForwardMaxQueueNumber());
+ this.queueSize =
+ Math.min(
+ queueSize,
+
IoTDBDescriptor.getInstance().getConfig().getTriggerForwardMaxSizePerQueue());
+ this.batchSize =
+ Math.min(batchSize,
IoTDBDescriptor.getInstance().getConfig().getTriggerForwardBatchSize());
+ this.handler = handler;
+ queues = new ArrayBlockingQueue[this.queueNumber];
+ for (int i = 0; i < queueNumber; i++) {
+ queues[i] = new ArrayBlockingQueue<>(this.queueSize);
+ Thread t =
+ new ForwardQueueConsumer(
+ handler.getClass().getSimpleName()
+ + "-"
+ + BatchHandlerQueue.class.getSimpleName()
+ + "-"
+ + i,
+ queues[i]);
+ t.setDaemon(true);
+ t.start();
+ }
+ }
+
+ private int getQueueID(int hashCode) {
+ return (hashCode & 0x7FFFFFFF) % queues.length;
+ }
+
+ public boolean offer(T event) {
+ // Group by device or polling
+ if (event.getFullPath() != null) {
+ return
queues[getQueueID(event.getFullPath().getDevice().hashCode())].offer(event);
+ } else {
+ return queues[getQueueID(atomicCount.incrementAndGet())].offer(event);
+ }
+ }
+
+ public void put(T event) throws InterruptedException {
+ // Group by device or polling
+ if (event.getFullPath() != null) {
+
queues[getQueueID(event.getFullPath().getDevice().hashCode())].put(event);
+ } else {
+ queues[getQueueID(atomicCount.incrementAndGet())].put(event);
+ }
+ }
+
+ private void handle(ArrayList<T> events) throws Exception {
+ handler.onEvent(events);
+ }
+
+ class ForwardQueueConsumer extends Thread {
+
+ ArrayBlockingQueue<T> queue;
+
+ public ForwardQueueConsumer(String name, ArrayBlockingQueue<T> queue) {
+ super(name);
+ this.queue = queue;
+ }
+
+ public void run() {
+ final long maxWaitMillis = 500;
+ final ArrayList<T> list = new ArrayList<>();
+ long startMillis = System.currentTimeMillis();
+ long restMillis = maxWaitMillis;
+ while (true) {
+ try {
+ T obj;
+ if (list.isEmpty()) {
+ obj = queue.take();
+ } else {
+ obj = queue.poll(restMillis, TimeUnit.MILLISECONDS);
+ }
+ if (obj != null) {
+ list.add(obj);
+ queue.drainTo(list, batchSize - list.size());
+ if (list.size() < batchSize) {
+ long waitMillis = System.currentTimeMillis() - startMillis;
+ if (waitMillis < maxWaitMillis) {
+ restMillis = maxWaitMillis - waitMillis;
+ continue;
+ }
+ }
+ }
+ handle(list);
+ list.clear();
+ startMillis = System.currentTimeMillis();
+ } catch (InterruptedException e) {
+ break;
+ } catch (Throwable t) {
+ LOGGER.error("ForwardTaskQueue consumer error", t);
+ }
+ }
+ }
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/HTTPConnectionPool.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/HTTPConnectionPool.java
new file mode 100644
index 0000000000..0a6503287d
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/HTTPConnectionPool.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.utils;
+
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+
+public class HTTPConnectionPool {
+
+ private static volatile PoolingHttpClientConnectionManager
clientConnectionManager;
+
+ private HTTPConnectionPool() {}
+
+ public static PoolingHttpClientConnectionManager getInstance() {
+ if (clientConnectionManager == null) {
+ synchronized (HTTPConnectionPool.class) {
+ if (clientConnectionManager == null) {
+ clientConnectionManager = new PoolingHttpClientConnectionManager();
+ // Set the max number of connections
+ clientConnectionManager.setMaxTotal(
+
IoTDBDescriptor.getInstance().getConfig().getTriggerForwardHTTPPoolSize());
+ // Set the maximum number of connections per host and the specified
number of connections
+ // per website, which will not affect the access of other websites
+ clientConnectionManager.setDefaultMaxPerRoute(
+
IoTDBDescriptor.getInstance().getConfig().getTriggerForwardHTTPPOOLMaxPerRoute());
+ }
+ }
+ }
+ return clientConnectionManager;
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/MQTTConnectionFactory.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/MQTTConnectionFactory.java
new file mode 100644
index 0000000000..4de66a12fa
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/MQTTConnectionFactory.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.utils;
+
+import org.apache.iotdb.db.engine.trigger.sink.exception.SinkException;
+
+import org.apache.commons.pool2.BasePooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.fusesource.mqtt.client.BlockingConnection;
+import org.fusesource.mqtt.client.MQTT;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class MQTTConnectionFactory extends
BasePooledObjectFactory<BlockingConnection> {
+ private final String host;
+ private final int port;
+ private final String username;
+ private final String password;
+ private final long connectAttemptsMax;
+ private final long reconnectDelay;
+
+ private static final AtomicInteger atomicCount = new AtomicInteger();
+ private static final String CLIENT_NAME = "MQTTClient";
+
+ public MQTTConnectionFactory(
+ String host,
+ int port,
+ String username,
+ String password,
+ long connectAttemptsMax,
+ long reconnectDelay) {
+ this.host = host;
+ this.port = port;
+ this.username = username;
+ this.password = password;
+ this.connectAttemptsMax = connectAttemptsMax;
+ this.reconnectDelay = reconnectDelay;
+ }
+
+ @Override
+ public BlockingConnection create() throws Exception {
+ MQTT mqtt = new MQTT();
+ mqtt.setClientId(CLIENT_NAME + atomicCount.incrementAndGet());
+ mqtt.setHost(host, port);
+ mqtt.setUserName(username);
+ mqtt.setPassword(password);
+ mqtt.setConnectAttemptsMax(connectAttemptsMax);
+ mqtt.setReconnectDelay(reconnectDelay);
+
+ BlockingConnection connection = mqtt.blockingConnection();
+ try {
+ connection.connect();
+ } catch (Exception e) {
+ if (connection != null) {
+ if (connection.isConnected()) {
+ connection.disconnect();
+ }
+ connection.kill();
+ }
+ throw new SinkException("MQTT Connection activate error", e);
+ }
+ return connection;
+ }
+
+ @Override
+ public PooledObject<BlockingConnection> wrap(BlockingConnection
blockingConnection) {
+ return new DefaultPooledObject<>(blockingConnection);
+ }
+
+ @Override
+ public boolean validateObject(PooledObject<BlockingConnection> p) {
+ if (p == null) {
+ return false;
+ }
+ BlockingConnection connection = p.getObject();
+ return connection != null && connection.isConnected();
+ }
+
+ @Override
+ public void destroyObject(PooledObject<BlockingConnection> p) throws
Exception {
+ if (p == null) {
+ return;
+ }
+ BlockingConnection connection = p.getObject();
+ try {
+ if (connection != null) {
+ if (connection.isConnected()) {
+ connection.disconnect();
+ }
+ connection.kill();
+ }
+ } catch (Exception e) {
+ throw new SinkException("MQTT connection destroy error", e);
+ }
+ super.destroyObject(p);
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/MQTTConnectionPool.java
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/MQTTConnectionPool.java
new file mode 100644
index 0000000000..fbc775a1a3
--- /dev/null
+++
b/server/src/main/java/org/apache/iotdb/db/engine/trigger/utils/MQTTConnectionPool.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.iotdb.db.engine.trigger.utils;
+
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+
+import org.apache.commons.pool2.impl.GenericObjectPool;
+import org.fusesource.mqtt.client.BlockingConnection;
+import org.fusesource.mqtt.client.QoS;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class MQTTConnectionPool extends GenericObjectPool<BlockingConnection> {
+
+ // Each host:port,username corresponds to a singleton instance
+ private static final Map<String, MQTTConnectionPool>
MQTT_CONNECTION_POOL_MAP =
+ new ConcurrentHashMap<>();
+ private final AtomicInteger referenceCount = new AtomicInteger(0);
+
+ public static MQTTConnectionPool getInstance(
+ String host, int port, String username, MQTTConnectionFactory factory,
int size)
+ throws Exception {
+ String key = host + ":" + port + "," + username;
+ MQTTConnectionPool connectionPool =
+ MQTT_CONNECTION_POOL_MAP.computeIfAbsent(key, k -> new
MQTTConnectionPool(factory, size));
+ if (connectionPool.referenceCount.getAndIncrement() == 0) {
+ connectionPool.preparePool();
+ }
+ return connectionPool;
+ }
+
+ private MQTTConnectionPool(MQTTConnectionFactory factory, int size) {
+ super(factory);
+ setMaxTotal(
+ Math.min(size,
IoTDBDescriptor.getInstance().getConfig().getTriggerForwardMQTTPoolSize()));
+ setMinIdle(1);
+ }
+
+ public void connect() throws Exception {
+ BlockingConnection connection = borrowObject();
+ if (!connection.isConnected()) {
+ connection.connect();
+ }
+ returnObject(connection);
+ }
+
+ public void clearAndClose() {
+ clear();
+ if (referenceCount.decrementAndGet() == 0) {
+ close();
+ }
+ }
+
+ public void publish(final String topic, final byte[] payload, final QoS qos,
final boolean retain)
+ throws Exception {
+ BlockingConnection connection = this.borrowObject();
+ connection.publish(topic, payload, qos, retain);
+ returnObject(connection);
+ }
+}
diff --git
a/server/src/main/java/org/apache/iotdb/db/protocol/mqtt/JSONPayloadFormatter.java
b/server/src/main/java/org/apache/iotdb/db/protocol/mqtt/JSONPayloadFormatter.java
index 2436fcac0d..3493252501 100644
---
a/server/src/main/java/org/apache/iotdb/db/protocol/mqtt/JSONPayloadFormatter.java
+++
b/server/src/main/java/org/apache/iotdb/db/protocol/mqtt/JSONPayloadFormatter.java
@@ -20,8 +20,11 @@ package org.apache.iotdb.db.protocol.mqtt;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import io.netty.buffer.ByteBuf;
@@ -50,13 +53,28 @@ public class JSONPayloadFormatter implements
PayloadFormatter {
return null;
}
String txt = payload.toString(StandardCharsets.UTF_8);
- JsonObject jsonObject = GSON.fromJson(txt, JsonObject.class);
+ try {
+ JsonObject jsonObject = GSON.fromJson(txt, JsonObject.class);
- if (jsonObject.get(JSON_KEY_TIMESTAMP) != null) {
- return formatJson(jsonObject);
- }
- if (jsonObject.get(JSON_KEY_TIMESTAMPS) != null) {
- return formatBatchJson(jsonObject);
+ if (jsonObject.get(JSON_KEY_TIMESTAMP) != null) {
+ return formatJson(jsonObject);
+ }
+ if (jsonObject.get(JSON_KEY_TIMESTAMPS) != null) {
+ return formatBatchJson(jsonObject);
+ }
+ } catch (JsonSyntaxException e) {
+ JsonArray jsonArray = GSON.fromJson(txt, JsonArray.class);
+ List<Message> messages = new ArrayList<>();
+ for (JsonElement jsonElement : jsonArray) {
+ JsonObject jsonObject = jsonElement.getAsJsonObject();
+ if (jsonObject.get(JSON_KEY_TIMESTAMP) != null) {
+ messages.addAll(formatJson(jsonObject));
+ }
+ if (jsonObject.get(JSON_KEY_TIMESTAMPS) != null) {
+ messages.addAll(formatBatchJson(jsonObject));
+ }
+ }
+ return messages;
}
throw new JsonParseException("payload is invalidate");
}
diff --git
a/server/src/test/java/org/apache/iotdb/db/metadata/idtable/trigger_example/Counter.java
b/server/src/test/java/org/apache/iotdb/db/metadata/idtable/trigger_example/Counter.java
index 4db46caa94..3c86592de5 100644
---
a/server/src/test/java/org/apache/iotdb/db/metadata/idtable/trigger_example/Counter.java
+++
b/server/src/test/java/org/apache/iotdb/db/metadata/idtable/trigger_example/Counter.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.metadata.idtable.trigger_example;
+import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.engine.trigger.api.Trigger;
import org.apache.iotdb.db.engine.trigger.api.TriggerAttributes;
import org.apache.iotdb.tsfile.utils.Binary;
@@ -47,37 +48,37 @@ public class Counter implements Trigger {
}
@Override
- public Integer fire(long timestamp, Integer value) {
+ public Integer fire(long timestamp, Integer value, PartialPath path) {
++counter;
return value;
}
@Override
- public Long fire(long timestamp, Long value) {
+ public Long fire(long timestamp, Long value, PartialPath path) {
++counter;
return value;
}
@Override
- public Float fire(long timestamp, Float value) {
+ public Float fire(long timestamp, Float value, PartialPath path) {
++counter;
return value;
}
@Override
- public Double fire(long timestamp, Double value) {
+ public Double fire(long timestamp, Double value, PartialPath path) {
++counter;
return value;
}
@Override
- public Boolean fire(long timestamp, Boolean value) {
+ public Boolean fire(long timestamp, Boolean value, PartialPath path) {
++counter;
return value;
}
@Override
- public Binary fire(long timestamp, Binary value) {
+ public Binary fire(long timestamp, Binary value, PartialPath path) {
++counter;
return value;
}