You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sk...@apache.org on 2022/09/09 16:43:00 UTC

[ignite-3] branch main updated: IGNITE-17444 Introduced MetricExporter interface and service loading. Fixes #1027

This is an automated email from the ASF dual-hosted git repository.

sk0x50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new d9d230453b IGNITE-17444 Introduced MetricExporter interface and service loading. Fixes #1027
d9d230453b is described below

commit d9d230453b4c410937a021cdfad3d86199b99961
Author: Kirill Gusakov <kg...@gmail.com>
AuthorDate: Fri Sep 9 19:42:46 2022 +0300

    IGNITE-17444 Introduced MetricExporter interface and service loading. Fixes #1027
    
    Signed-off-by: Slava Koptilin <sl...@gmail.com>
---
 .../exporters/MetricExportersLoadingTest.java      |  74 ++++++++++++++
 .../metrics/exporters/TestMetricsSource.java       |  61 ++++++++++++
 .../metrics/exporters/TestPullMetricExporter.java  |  98 +++++++++++++++++++
 .../metrics/exporters/TestPushMetricExporter.java  |  69 +++++++++++++
 ...gnite.internal.metrics.exporters.MetricExporter |  17 ++++
 .../ignite/internal/metrics/MetricManager.java     |  40 +++++++-
 .../ignite/internal/metrics/MetricProvider.java    |  47 +++++++++
 .../metrics/exporters/BasicMetricExporter.java     |  52 ++++++++++
 .../internal/metrics/exporters/MetricExporter.java |  56 +++++++++++
 .../metrics/exporters/PushMetricExporter.java      | 107 +++++++++++++++++++++
 10 files changed, 619 insertions(+), 2 deletions(-)

diff --git a/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/MetricExportersLoadingTest.java b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/MetricExportersLoadingTest.java
new file mode 100644
index 0000000000..5fb4593da4
--- /dev/null
+++ b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/MetricExportersLoadingTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.concurrent.locks.LockSupport;
+import org.apache.ignite.internal.metrics.MetricManager;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Integration test for metrics' exporters loading.
+ */
+public class MetricExportersLoadingTest {
+    @Test
+    public void test() throws Exception {
+        MetricManager metricManager = new MetricManager();
+
+        TestMetricsSource src = new TestMetricsSource("TestMetricsSource");
+
+        metricManager.registerSource(src);
+
+        metricManager.enable(src.name());
+
+        try (OutputStream pullOutputStream = new ByteArrayOutputStream();
+                OutputStream pushOutputStream = new ByteArrayOutputStream()) {
+            TestPullMetricExporter.setOutputStream(pullOutputStream);
+
+            TestPushMetricExporter.setOutputStream(pushOutputStream);
+
+            assertEquals(0, pullOutputStream.toString().length());
+
+            assertEquals(0, pushOutputStream.toString().length());
+
+            metricManager.start();
+
+            src.inc();
+
+            waitForOutput(pushOutputStream, "TestMetricsSource:\nmetric:1");
+            assertTrue(pushOutputStream.toString().contains("TestMetricsSource:\nmetric:1"));
+
+            TestPullMetricExporter.requestMetrics();
+
+            waitForOutput(pullOutputStream, "TestMetricsSource:\nmetric:1");
+            assertTrue(pullOutputStream.toString().contains("TestMetricsSource:\nmetric:1"));
+
+            metricManager.stop();
+        }
+    }
+
+    private void waitForOutput(OutputStream outputStream, String content) {
+        while (!outputStream.toString().contains(content)) {
+            LockSupport.parkNanos(100_000_000);
+        }
+    }
+}
diff --git a/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestMetricsSource.java b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestMetricsSource.java
new file mode 100644
index 0000000000..1c6d4e1737
--- /dev/null
+++ b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestMetricsSource.java
@@ -0,0 +1,61 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import org.apache.ignite.internal.metrics.AbstractMetricSource;
+import org.apache.ignite.internal.metrics.AtomicIntMetric;
+import org.apache.ignite.internal.metrics.MetricSetBuilder;
+import org.apache.ignite.internal.metrics.ThreadPoolMetricTest;
+import org.apache.ignite.internal.metrics.exporters.TestMetricsSource.Holder;
+
+/**
+ * Metric source for {@link ThreadPoolMetricTest}.
+ */
+public class TestMetricsSource extends AbstractMetricSource<Holder> {
+    private AtomicIntMetric atomicIntMetric;
+
+    /**
+     * Constructor.
+     *
+     * @param name Name.
+     */
+    public TestMetricsSource(String name) {
+        super(name);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected Holder createHolder() {
+        return new Holder();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void init(MetricSetBuilder bldr, Holder holder) {
+        atomicIntMetric = bldr.atomicInt("metric", "Metric");
+    }
+
+    public void inc() {
+        atomicIntMetric.increment();
+    }
+
+    /**
+     * Holder class.
+     */
+    protected static class Holder implements AbstractMetricSource.Holder<Holder> {
+        // No-op.
+    }
+}
diff --git a/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestPullMetricExporter.java b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestPullMetricExporter.java
new file mode 100644
index 0000000000..0029fe06ed
--- /dev/null
+++ b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestPullMetricExporter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.ignite.internal.metrics.Metric;
+import org.apache.ignite.internal.metrics.MetricSet;
+
+/**
+ * Simple pull exporter, which simulate the pull principe throw primitive wait/notify API
+ * instead of the complex TCP/IP etc. endpoints.
+ */
+public class TestPullMetricExporter extends BasicMetricExporter {
+    private static OutputStream outputStream;
+
+    private static final Object obj = new Object();
+
+    ExecutorService executorService = Executors.newSingleThreadExecutor();
+
+    public static void setOutputStream(OutputStream outputStream) {
+        TestPullMetricExporter.outputStream = outputStream;
+    }
+
+    /**
+     * Simulate metric request.
+     */
+    public static void requestMetrics() {
+        synchronized (obj) {
+            obj.notify();
+        }
+    }
+
+    @Override
+    public void start() {
+        executorService.execute(() -> {
+            while (true) {
+                waitForRequest();
+
+                var report = new StringBuilder();
+
+                for (MetricSet metricSet : metrics().get1().values()) {
+                    report.append(metricSet.name()).append(":\n");
+
+                    for (Metric metric : metricSet) {
+                        report.append(metric.name())
+                                .append(":")
+                                .append(metric.getValueAsString())
+                                .append("\n");
+                    }
+
+                    report.append("\n");
+                }
+
+                try {
+                    outputStream.write(report.toString().getBytes(StandardCharsets.UTF_8));
+
+                    outputStream.flush();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void stop() {
+        executorService.shutdown();
+    }
+
+    private void waitForRequest() {
+        synchronized (obj) {
+            try {
+                obj.wait();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestPushMetricExporter.java b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestPushMetricExporter.java
new file mode 100644
index 0000000000..ac4d7675d0
--- /dev/null
+++ b/modules/metrics/src/integrationTest/java/org/apache/ignite/internal/metrics/exporters/TestPushMetricExporter.java
@@ -0,0 +1,69 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import org.apache.ignite.internal.metrics.Metric;
+import org.apache.ignite.internal.metrics.MetricSet;
+
+/**
+ * Test push metrics exporter.
+ */
+public class TestPushMetricExporter extends PushMetricExporter {
+    private static OutputStream outputStream;
+
+    public TestPushMetricExporter() {
+        setPeriod(100);
+    }
+
+    public static void setOutputStream(OutputStream outputStream) {
+        TestPushMetricExporter.outputStream = outputStream;
+    }
+
+    @Override
+    public void report() {
+        var report = new StringBuilder();
+
+        for (MetricSet metricSet : metrics().get1().values()) {
+            report.append(metricSet.name()).append(":\n");
+
+            for (Metric metric : metricSet) {
+                report.append(metric.name())
+                        .append(":")
+                        .append(metric.getValueAsString())
+                        .append("\n");
+            }
+
+            report.append("\n");
+        }
+
+        write(report.toString());
+    }
+
+    private void write(String report) {
+        try {
+            outputStream.write(report.getBytes(StandardCharsets.UTF_8));
+
+            outputStream.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/modules/metrics/src/integrationTest/resources/META-INF/services/org.apache.ignite.internal.metrics.exporters.MetricExporter b/modules/metrics/src/integrationTest/resources/META-INF/services/org.apache.ignite.internal.metrics.exporters.MetricExporter
new file mode 100644
index 0000000000..165247eaaa
--- /dev/null
+++ b/modules/metrics/src/integrationTest/resources/META-INF/services/org.apache.ignite.internal.metrics.exporters.MetricExporter
@@ -0,0 +1,17 @@
+# 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.
+#
+org.apache.ignite.internal.metrics.exporters.TestPullMetricExporter
+org.apache.ignite.internal.metrics.exporters.TestPushMetricExporter
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
index 91cc669441..b4da6e8aa9 100644
--- a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
@@ -17,11 +17,18 @@
 
 package org.apache.ignite.internal.metrics;
 
+import static java.util.stream.Collectors.toUnmodifiableList;
+
+import java.util.List;
 import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.ServiceLoader.Provider;
 import org.apache.ignite.internal.manager.IgniteComponent;
+import org.apache.ignite.internal.metrics.exporters.MetricExporter;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.jetbrains.annotations.NotNull;
 
+
 /**
  * Metric manager.
  */
@@ -31,6 +38,9 @@ public class MetricManager implements IgniteComponent {
      */
     private final MetricRegistry registry;
 
+    /** Metrics' exporters. */
+    private List<MetricExporter> metricExporters;
+
     /**
      * Constructor.
      */
@@ -40,12 +50,23 @@ public class MetricManager implements IgniteComponent {
 
     /** {@inheritDoc} */
     @Override public void start() {
-        // No-op.
+        // TODO: IGNITE-17358 not all exporters should be started, it must be defined by configuration
+        metricExporters = loadExporters();
+
+        MetricProvider metricsProvider = new MetricProvider(registry);
+
+        for (MetricExporter metricExporter : metricExporters) {
+            metricExporter.init(metricsProvider);
+
+            metricExporter.start();
+        }
     }
 
     /** {@inheritDoc} */
     @Override public void stop() throws Exception {
-        // No-op.
+        for (MetricExporter metricExporter : metricExporters) {
+            metricExporter.stop();
+        }
     }
 
     /**
@@ -95,6 +116,21 @@ public class MetricManager implements IgniteComponent {
         return registry.enable(srcName);
     }
 
+    /**
+     * Load exporters by {@link ServiceLoader} mechanism.
+     *
+     * @return list of loaded exporters.
+     */
+    private List<MetricExporter> loadExporters() {
+        var clsLdr = Thread.currentThread().getContextClassLoader();
+
+        return ServiceLoader
+                .load(MetricExporter.class, clsLdr)
+                .stream()
+                .map(Provider::get)
+                .collect(toUnmodifiableList());
+    }
+
     /**
      * Disable metric source. See {@link MetricRegistry#disable(MetricSource)}.
      *
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricProvider.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricProvider.java
new file mode 100644
index 0000000000..ca4ae3f901
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricProvider.java
@@ -0,0 +1,47 @@
+/*
+ * 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.ignite.internal.metrics;
+
+import java.util.Map;
+import org.apache.ignite.lang.IgniteBiTuple;
+
+/**
+ * Read-only metrics registry.
+ */
+public class MetricProvider {
+    /** Metrics registry. */
+    private MetricRegistry metricRegistry;
+
+    /**
+     * Constructor.
+     *
+     * @param metricRegistry Metrics registry.
+     */
+    public MetricProvider(MetricRegistry metricRegistry) {
+        this.metricRegistry = metricRegistry;
+    }
+
+    /**
+     * Returns a map of (metricSetName -> metricSet) pairs with available metrics from {@link MetricRegistry}.
+     *
+     * @return map of metrics
+     */
+    public IgniteBiTuple<Map<String, MetricSet>, Long> metrics() {
+        return metricRegistry.metricSnapshot();
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/BasicMetricExporter.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/BasicMetricExporter.java
new file mode 100644
index 0000000000..10aa93d311
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/BasicMetricExporter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import java.util.Map;
+import org.apache.ignite.internal.metrics.MetricProvider;
+import org.apache.ignite.internal.metrics.MetricSet;
+import org.apache.ignite.lang.IgniteBiTuple;
+
+/**
+ * Base class for new metrics exporters implementations.
+ */
+public abstract class BasicMetricExporter implements MetricExporter {
+    /** Metrics provider. */
+    private MetricProvider metricsProvider;
+
+    /** {@inheritDoc} */
+    @Override
+    public final void init(MetricProvider metricProvider) {
+        this.metricsProvider = metricProvider;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String name() {
+        return getClass().getCanonicalName();
+    }
+
+    /**
+     * Returns a map of (metricSetName -> metricSet) pairs with available metrics.
+     *
+     * @return map of metrics
+     */
+    protected final IgniteBiTuple<Map<String, MetricSet>, Long> metrics() {
+        return metricsProvider.metrics();
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/MetricExporter.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/MetricExporter.java
new file mode 100644
index 0000000000..cb7c38dd3d
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/MetricExporter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import org.apache.ignite.internal.metrics.MetricProvider;
+
+/**
+ * Interface for metric exporters to external recipients.
+ * Exporters can be one of the two type: push and pull exporters.
+ *
+ * <p>Push exporters push metrics to the external endpoint periodically.
+ * Push exporters should implement {@link PushMetricExporter} according to its documentation.
+ *
+ * <p>Pull exporters is the endpoint by itself (HTTP, JMX and etc.), which response with the metric data for request.
+ * Pull exporters should extend {@link BasicMetricExporter}.
+ */
+public interface MetricExporter {
+    /**
+     * Initialize metric exporter with the provider of available metrics.
+     *
+     * @param metricProvider Metrics provider
+     */
+    void init(MetricProvider metricProvider);
+
+    /**
+     * Start metrics exporter. Here all needed listeners, schedulers etc. should be started.
+     */
+    void start();
+
+    /**
+     * Stop and cleanup work for current exporter must be implemented here.
+     */
+    void stop();
+
+    /**
+     * Returns the name of exporter. Name must be unique.
+     *
+     * @return Name of the exporter.
+     */
+    String name();
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/PushMetricExporter.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/PushMetricExporter.java
new file mode 100644
index 0000000000..6f8cdc649b
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/PushMetricExporter.java
@@ -0,0 +1,107 @@
+/*
+ * 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.ignite.internal.metrics.exporters;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.thread.NamedThreadFactory;
+import org.apache.ignite.internal.util.IgniteUtils;
+
+/**
+ * Base class for push metrics exporters, according to terminology from {@link MetricExporter} docs.
+ * Every {@code period} of time {@link PushMetricExporter#report()} will be called
+ * to push metrics to the external system.
+ */
+public abstract class PushMetricExporter extends BasicMetricExporter {
+    /** Logger. */
+    protected final IgniteLogger log = Loggers.forClass(getClass());
+
+    /** Default export period in milliseconds. */
+    public static final long DFLT_EXPORT_PERIOD = 60_000;
+
+    /** Export period. */
+    private long period = DFLT_EXPORT_PERIOD;
+
+    /** Export task future. */
+    private ScheduledFuture<?> fut;
+
+    /** Export scheduler. */
+    private ScheduledExecutorService scheduler;
+
+    /** {@inheritDoc} */
+    @Override
+    public void start() {
+        scheduler =
+                Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("metrics-exporter", log));
+
+        fut = scheduler.scheduleWithFixedDelay(() -> {
+            try {
+                report();
+            } catch (Throwable th) {
+                log.error("Metrics export error. "
+                        + "This exporter will be stopped [class=" + getClass() + ",name=" + name() + ']', th);
+
+                throw th;
+            }
+        }, period, period, TimeUnit.MILLISECONDS);
+
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void stop() {
+        fut.cancel(false);
+
+        IgniteUtils.shutdownAndAwaitTermination(scheduler, 10, TimeUnit.SECONDS);
+    }
+
+    // TODO: after IGNITE-17358 this period maybe shouldn't be a part of protected API
+    // TODO: and should be configured throw configuration API
+    /**
+     * Sets period in milliseconds after {@link #report()} method should be called.
+     *
+     * @param period Period in milliseconds.
+     */
+    protected void setPeriod(long period) {
+        this.period = period;
+    }
+
+    /**
+     * Returns export period.
+     *
+     * @return Period in milliseconds after {@link #report()} method should be called.
+     */
+    public long getPeriod() {
+        return period;
+    }
+
+    /**
+     * A heart of the push exporter.
+     * Inside this method all needed operations to send the metrics outside must be implemented.
+     *
+     * <p>This method will be executed periodically by internal exporter's scheduler.
+     *
+     * <p>In case of any exceptions exporter's internal scheduler will be stopped
+     * and no new {@link #report()} will be executed.
+     */
+    public abstract void report();
+}