Cleaning
diff --git a/modules/metrics/pom.xml b/modules/metrics/pom.xml
new file mode 100644
index 0000000..76ea09b
--- /dev/null
+++ b/modules/metrics/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.ignite</groupId>
+        <artifactId>ignite-parent</artifactId>
+        <version>1</version>
+        <relativePath>../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>ignite-metrics</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+        </dependency>
+
+        <!-- 3rd party dependencies -->
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetric.java
new file mode 100644
index 0000000..edaac8c
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetric.java
@@ -0,0 +1,54 @@
+package org.apache.ignite.internal.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+public abstract class AbstractMetric implements Metric {
+    /** Metric name. It is local for a particular {@link MetricsSet}. */
+    private final String name;
+
+    /** Metric description. */
+    private final String desc;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     */
+    public AbstractMetric(String name, String desc) {
+        assert name != null;
+        assert !name.isEmpty();
+
+        this.name = name;
+        this.desc = desc;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return name;
+    }
+
+    /** {@inheritDoc} */
+    @Override @Nullable public String description() {
+        return desc;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        AbstractMetric metric = (AbstractMetric)o;
+
+        if (!name.equals(metric.name))
+            return false;
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return name.hashCode();
+    }
+}
\ No newline at end of file
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricsSource.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricsSource.java
new file mode 100644
index 0000000..964b2aa
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AbstractMetricsSource.java
@@ -0,0 +1,119 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater;
+
+/**
+ * Base class for all metric sources.
+ *
+ * @param <T> Holder type.
+ */
+public abstract class AbstractMetricsSource<T extends AbstractMetricsSource.Holder<T>> implements MetricsSource {
+    /** Holder field updater. */
+    @SuppressWarnings("rawtypes")
+    private static final AtomicReferenceFieldUpdater<AbstractMetricsSource, Holder> HOLDER_FIELD_UPD =
+            newUpdater(AbstractMetricsSource.class, AbstractMetricsSource.Holder.class, "holder");
+
+    /** Metric source name. */
+    private final String name;
+
+    /** Metric instances holder. */
+    private volatile T holder;
+
+    /**
+     * Base constructor for all metric source implemnetations.
+     *
+     * @param name Metric source name.
+     */
+    protected AbstractMetricsSource(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns metric source name.
+     *
+     * @return Metric source name.
+     */
+    @Override public final String name() {
+        return name;
+    }
+
+    /**
+     * Checks whether metrics is enabled (switched on) or not (switched off) for metric source.
+     *
+     * @return {@code True} if metrics are enabled, otherwise - {@code false}.
+     */
+    @Override public final boolean enabled() {
+        return holder != null;
+    }
+
+    /**
+     * Returns metric instances holder. Use this on oreder to avoid metric lookup from map-like data structures.
+     * Returned value is {@code null} if metrics are disabled.
+     *
+     * @return Metrics holder instance if metrics are enabled, otherwise - {@code null}.
+     */
+    protected final T holder() {
+        return holder;
+    }
+
+    /**
+     * Method is responsible for creation of appropriate holder instance in underlying implementations.
+     *
+     * @return New instance of metrics holder that must implements {@link Holder} interface.
+     */
+    protected abstract T createHolder();
+
+    /** {@inheritDoc} */
+    @Override public final MetricsSet enable() {
+        MetricsSetBuilder bldr = new MetricsSetBuilder(name);
+
+        T hldr = createHolder();
+
+        init(bldr, hldr);
+
+        MetricsSet reg = bldr.build();
+
+        return HOLDER_FIELD_UPD.compareAndSet(this, null, hldr) ? reg : null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public final void disable() {
+        T holder0 = holder;
+
+        if (HOLDER_FIELD_UPD.compareAndSet(this, holder0, null))
+            cleanup(holder0);
+    }
+
+    /**
+     * Method is responsible for:
+     * <ol>
+     *     <li>Creation of {@link MetricsSet} instance using provided {@link MetricsSetBuilder}.</li>
+     *     <li>Creation of metric instances in given holder.</li>
+     *     <li>Other initialization if needed.</li>
+     * <ol/>
+     *
+     * @param bldr Metric registry builder.
+     * @param holder Metric instances holder.
+     */
+    protected abstract void init(MetricsSetBuilder bldr, T holder);
+
+    /**
+     * Method is responsible for cleanup and release of all resources initialized or created during {@link #init} method
+     * execution. Note that {@link MetricsSet} and {@link Holder} instances will be released automatically.
+     *
+     * @param holder Metric instances holder.
+     */
+    protected void cleanup(T holder) {
+        // No-op.
+    }
+
+    /**
+     * Marker interface for metric instances holder.
+     *
+     * @param <T> Holder type subclass.
+     */
+    protected interface Holder<T extends Holder<T>> {
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
new file mode 100644
index 0000000..281d3c0
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicDoubleMetric.java
@@ -0,0 +1,50 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+public class AtomicDoubleMetric extends AbstractMetric implements DoubleMetric {
+    /** Field updater. */
+    private static final AtomicLongFieldUpdater<AtomicDoubleMetric> updater =
+            AtomicLongFieldUpdater.newUpdater(AtomicDoubleMetric.class, "val");
+
+    /** Value. */
+    private volatile long val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     */
+    public AtomicDoubleMetric(String name, String desc) {
+        super(name, desc);
+    }
+
+    /**
+     * Adds given value to the metric value.
+     *
+     * @param v Value to be added.
+     */
+    public void add(double v) {
+        for (;;) {
+            long exp = val;
+            long upd = Double.doubleToLongBits(Double.longBitsToDouble(exp) + v);
+            if (updater.compareAndSet(this, exp, upd)) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Sets value.
+     *
+     * @param val Value.
+     */
+    public void value(double val) {
+        this.val = Double.doubleToLongBits(val);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public double value() {
+        return Double.longBitsToDouble(val);
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicIntMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicIntMetric.java
new file mode 100644
index 0000000..a534402
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicIntMetric.java
@@ -0,0 +1,53 @@
+package org.apache.ignite.internal.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+/**
+ * Integer metric is implemented as a volatile {@code int} value.
+ */
+public class AtomicIntMetric extends AbstractMetric implements IntMetric {
+    /** Field updater. */
+    private static final AtomicIntegerFieldUpdater<AtomicIntMetric> updater =
+            AtomicIntegerFieldUpdater.newUpdater(AtomicIntMetric.class, "val");
+
+    /** Value. */
+    private volatile int val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     */
+    public AtomicIntMetric(String name, @Nullable String desc) {
+        super(name, desc);
+    }
+
+    /**
+     * Adds x to the metric.
+     *
+     * @param x Value to be added.
+     */
+    public void add(int x) {
+        updater.addAndGet(this, x);
+    }
+
+    /** Adds 1 to the metric. */
+    public void increment() {
+        add(1);
+    }
+
+    /**
+     * Sets value.
+     *
+     * @param val Value.
+     */
+    public void value(int val) {
+        this.val = val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int value() {
+        return val;
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicLongMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicLongMetric.java
new file mode 100644
index 0000000..9926dee
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/AtomicLongMetric.java
@@ -0,0 +1,58 @@
+package org.apache.ignite.internal.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+/**
+ * Long metric implementation.
+ */
+public class AtomicLongMetric extends AbstractMetric implements LongMetric {
+    /** Field updater. */
+    static final AtomicLongFieldUpdater<AtomicLongMetric> updater =
+            AtomicLongFieldUpdater.newUpdater(AtomicLongMetric.class, "val");
+
+    /** Field value. */
+    private volatile long val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     */
+    public AtomicLongMetric(String name, @Nullable String desc) {
+        super(name, desc);
+    }
+
+    /**
+     * Adds x to the metric.
+     *
+     * @param x Value to be added.
+     */
+    public void add(long x) {
+        updater.getAndAdd(this, x);
+    }
+
+    /** Adds 1 to the metric. */
+    public void increment() {
+        add(1);
+    }
+
+    /** Adds -1 to the metric. */
+    public void decrement() {
+        add(-1);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long value() {
+        return val;
+    }
+
+    /**
+     * Sets value.
+     *
+     * @param val Value.
+     */
+    public void value(long val) {
+        this.val = val;
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleAdderMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleAdderMetric.java
new file mode 100644
index 0000000..02805b3
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleAdderMetric.java
@@ -0,0 +1,49 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.concurrent.atomic.DoubleAdder;
+import org.apache.ignite.internal.metrics.AbstractMetric;
+import org.apache.ignite.internal.metrics.DoubleMetric;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Double metric.
+ */
+public class DoubleAdderMetric extends AbstractMetric implements DoubleMetric {
+    /** Value. */
+    private volatile DoubleAdder val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     */
+    public DoubleAdderMetric(String name, @Nullable String desc) {
+        super(name, desc);
+
+        this.val = new DoubleAdder();
+    }
+
+    /**
+     * Adds x to the metric.
+     *
+     * @param x Value to be added.
+     */
+    public void add(double x) {
+        val.add(x);
+    }
+
+    /**
+     * Sets value.
+     *
+     * @param val Value.
+     */
+    public void value(double val) {
+        DoubleAdder adder = new DoubleAdder();
+        adder.add(val);
+        this.val = adder;
+    }
+
+    /** {@inheritDoc} */
+    @Override public double value() {
+        return val.sum();
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleGauge.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleGauge.java
new file mode 100644
index 0000000..98dde98
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleGauge.java
@@ -0,0 +1,28 @@
+package org.apache.ignite.internal.metrics;
+
+import org.apache.ignite.internal.metrics.AbstractMetric;
+import org.apache.ignite.internal.metrics.DoubleMetric;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.DoubleSupplier;
+
+public class DoubleGauge extends AbstractMetric implements DoubleMetric {
+    /** Value supplier. */
+    private final DoubleSupplier val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     * @param val Supplier.
+     */
+    public DoubleGauge(String name, @Nullable String desc, DoubleSupplier val) {
+        super(name, desc);
+
+        this.val = val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public double value() {
+        return val.getAsDouble();
+    }
+}
\ No newline at end of file
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleMetric.java
new file mode 100644
index 0000000..e149112
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/DoubleMetric.java
@@ -0,0 +1,11 @@
+package org.apache.ignite.internal.metrics;
+
+public interface DoubleMetric extends Metric {
+    /** @return Value of the metric. */
+    public double value();
+
+    /** {@inheritDoc} */
+    @Override public default String getAsString() {
+        return Double.toString(value());
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntGauge.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntGauge.java
new file mode 100644
index 0000000..af47db6
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntGauge.java
@@ -0,0 +1,31 @@
+package org.apache.ignite.internal.metrics;
+
+import org.apache.ignite.internal.metrics.AbstractMetric;
+import org.apache.ignite.internal.metrics.IntMetric;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.IntSupplier;
+
+/**
+ * Implementation based on primitive supplier.
+ */
+public class IntGauge extends AbstractMetric implements IntMetric {
+    /** Value supplier. */
+    private final IntSupplier val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     * @param val Supplier.
+     */
+    public IntGauge(String name, @Nullable String desc, IntSupplier val) {
+        super(name, desc);
+
+        this.val = val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int value() {
+        return val.getAsInt();
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntMetric.java
new file mode 100644
index 0000000..1c42d01
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/IntMetric.java
@@ -0,0 +1,14 @@
+package org.apache.ignite.internal.metrics;
+
+/**
+ * Interface for the metrics that holds int primitive.
+ */
+public interface IntMetric extends Metric {
+    /** @return Value of the metric. */
+    int value();
+
+    /** {@inheritDoc} */
+    default String getAsString() {
+        return Integer.toString(value());
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongAdderMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongAdderMetric.java
new file mode 100644
index 0000000..0f9f863
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongAdderMetric.java
@@ -0,0 +1,47 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.concurrent.atomic.LongAdder;
+import org.apache.ignite.internal.metrics.AbstractMetric;
+import org.apache.ignite.internal.metrics.LongMetric;
+import org.jetbrains.annotations.Nullable;
+
+public class LongAdderMetric extends AbstractMetric implements LongMetric {
+    /** Value. */
+    private volatile LongAdder val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     */
+    public LongAdderMetric(String name, @Nullable String desc) {
+        super(name, desc);
+
+        this.val = new LongAdder();
+    }
+
+    /**
+     * Adds x to the metric.
+     *
+     * @param x Value to be added.
+     */
+    public void add(long x) {
+        val.add(x);
+    }
+
+    /**
+     * Sets value.
+     *
+     * @param val Value.
+     */
+    public void value(long val) {
+        LongAdder adder = new LongAdder();
+        adder.add(val);
+        this.val = adder;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long value() {
+        return val.sum();
+    }
+
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongGauge.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongGauge.java
new file mode 100644
index 0000000..e2e21dc
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongGauge.java
@@ -0,0 +1,31 @@
+package org.apache.ignite.internal.metrics;
+
+import org.apache.ignite.internal.metrics.AbstractMetric;
+import org.apache.ignite.internal.metrics.LongMetric;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.LongSupplier;
+
+/**
+ * Implementation based on primitive supplier.
+ */
+public class LongGauge extends AbstractMetric implements LongMetric {
+    /** Value supplier. */
+    private final LongSupplier val;
+
+    /**
+     * @param name Name.
+     * @param desc Description.
+     * @param val Supplier.
+     */
+    public LongGauge(String name, @Nullable String desc, LongSupplier val) {
+        super(name, desc);
+
+        this.val = val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long value() {
+        return val.getAsLong();
+    }
+}
\ No newline at end of file
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongMetric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongMetric.java
new file mode 100644
index 0000000..61dbcf1
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/LongMetric.java
@@ -0,0 +1,14 @@
+package org.apache.ignite.internal.metrics;
+
+/**
+ * Interface for the metrics that holds long primitive.
+ */
+public interface LongMetric extends Metric {
+    /** @return Value of the metric. */
+    public long value();
+
+    /** {@inheritDoc} */
+    @Override public default String getAsString() {
+        return Long.toString(value());
+    }
+}
\ No newline at end of file
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/Metric.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/Metric.java
new file mode 100644
index 0000000..fc76e45
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/Metric.java
@@ -0,0 +1,21 @@
+package org.apache.ignite.internal.metrics;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface Metric {
+    /**
+     * @return Name of the metric.
+     */
+    String name();
+
+    /**
+     * @return Description of the metric.
+     */
+    String description();
+
+    /**
+     * @return String representation of metric value.
+     */
+    @Nullable
+    String getAsString();
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsRegistry.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsRegistry.java
new file mode 100644
index 0000000..c78bb33
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsRegistry.java
@@ -0,0 +1,73 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class MetricsRegistry {
+    private final Lock lock = new ReentrantLock();
+
+    /** Version always should be changed on metrics enabled/disabled action. */
+    private long ver;
+    private final Map<String, MetricsSource> sources = new HashMap<>();
+
+    private final Map<String, MetricsSet> sets = new TreeMap<>();
+
+    public void registerSource(MetricsSource src) {
+        lock.lock();
+
+        try {
+            MetricsSource old = sources.putIfAbsent(src.name(), src);
+
+            if (old != null)
+                throw new IllegalStateException("Metrics source with given name is already exists: " + src.name());
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void unregisterSource(MetricsSource src) {
+        lock.lock();
+
+        try {
+            disable(src.name());
+
+            sources.remove(src.name());
+        } finally {
+            lock.unlock();
+        }
+    }
+
+
+    public void enable(final String srcName) {
+        lock.lock();
+
+        try {
+            MetricsSource src = sources.get(srcName);
+
+            if (src == null)
+                throw new IllegalStateException("Metrics source with given name doesn't exists: " + srcName);
+
+            sets.put(srcName, src.enable());
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    public void disable(final String srcName) {
+        lock.lock();
+
+        try {
+            MetricsSource src = sources.get(srcName);
+
+            src.disable();
+
+            sets.remove(srcName);
+        } finally {
+            lock.unlock();
+        }
+    }
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSet.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSet.java
new file mode 100644
index 0000000..092316c
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSet.java
@@ -0,0 +1,48 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.TestOnly;
+
+/**
+ * The Metric set that consists of set of metrics identified by a metric name.
+ * The metrics set is immutable.
+ */
+public class MetricsSet implements Iterable<Metric> {
+    /** Metrics set name. */
+    private final String name;
+
+    /** Registered metrics. */
+    private final Map<String, Metric> metrics;
+
+    /**
+     * Creates an instance of a metrics set with given name and metrics.
+     *
+     * @param name Metrics set name.
+     * @param metrics Metrics.
+     */
+    public MetricsSet(String name, Map<String, Metric> metrics) {
+        this.name = name;
+        this.metrics = Collections.unmodifiableMap(metrics);
+    }
+
+    /** {@inheritDoc} */
+    @Nullable
+    @TestOnly
+    public <M extends Metric> M get(String name) {
+        return (M)metrics.get(name);
+    }
+
+    /** {@inheritDoc} */
+    public Iterator<Metric> iterator() {
+        return metrics.values().iterator();
+    }
+
+    /** @return Name of the metrics set. */
+    public String name() {
+        return name;
+    }
+
+}
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSetBuilder.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSetBuilder.java
new file mode 100644
index 0000000..8717251
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSetBuilder.java
@@ -0,0 +1,98 @@
+package org.apache.ignite.internal.metrics;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+
+public class MetricsSetBuilder {
+    /** Metrics set name. */
+    private final String name;
+
+    /** Registered metrics. */
+    private Map<String, Metric> metrics = new LinkedHashMap<>();
+
+    /**
+     *  Creates a new instance of metrics set builder with given name.
+     *
+     * @param name Name of metrics set. Can't be null.
+     */
+    public MetricsSetBuilder(String name) {
+        Objects.requireNonNull(name, "Metrics set name can't be null");
+        this.name = name;
+    }
+
+    public MetricsSet build() {
+        if (metrics == null) {
+            throw new IllegalStateException("Builder can't be used twice.");
+        }
+
+        MetricsSet reg = new MetricsSet(name, metrics);
+
+        metrics = null;
+
+        return reg;
+    }
+
+    /**
+     * Returns metrics set name.
+     *
+     * @return Metrics set name.
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Adds existing metric with the specified name.
+     *
+     * @param metric Metric.
+     * @throws IllegalStateException If metric with given name is already added.
+     * @deprecated Wrong method. Breaks contract.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends Metric> T register(T metric) {
+        T old = (T)metrics.putIfAbsent(metric.name(), metric);
+
+        if (old != null) {
+            throw new IllegalStateException("Metric with given name is already registered [name=" + name +
+                    ", metric=" + metric + ']');
+        }
+
+        return metric;
+    }
+
+    public AtomicIntMetric atomicInt(String name, String description) {
+        return register(new AtomicIntMetric(name, description));
+    }
+
+    public IntGauge intGauge(String name, String description, IntSupplier supplier) {
+        return register(new IntGauge(name, description, supplier));
+    }
+
+    public AtomicLongMetric atomicLong(String name, String description) {
+        return register(new AtomicLongMetric(name, description));
+    }
+
+    public LongAdderMetric longAdder(String name, String description) {
+        return register(new LongAdderMetric(name, description));
+    }
+
+    public LongGauge longGauge(String name, String description, LongSupplier supplier) {
+        return register(new LongGauge(name, description, supplier));
+    }
+
+    public AtomicDoubleMetric atomicDouble(String name, String description) {
+        return register(new AtomicDoubleMetric(name, description));
+    }
+
+    public DoubleAdderMetric doubleAdder(String name, String description) {
+        return register(new DoubleAdderMetric(name, description));
+    }
+
+    public DoubleGauge doubleGauge(String name, String description, DoubleSupplier supplier) {
+        return register(new DoubleGauge(name, description, supplier));
+    }
+}
\ No newline at end of file
diff --git a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSource.java b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSource.java
new file mode 100644
index 0000000..e729d58
--- /dev/null
+++ b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricsSource.java
@@ -0,0 +1,32 @@
+package org.apache.ignite.internal.metrics;
+
+/**
+ * Interface for all metrics source.
+ */
+public interface MetricsSource {
+    /**
+     * Returns metric source name.
+     *
+     * @return Metric source name.
+     */
+    String name();
+
+    /**
+     * Enables metrics for metric source. Creates and returns {@link MetricsSet} built during enabling.
+     *
+     * @return Newly created {@link MetricsSet} instance or {@code null} if metrics are already enabled.
+     */
+    MetricsSet enable();
+
+    /**
+     * Disables metrics for metric source.
+     */
+    void disable();
+
+    /**
+     * Checks whether metrics is enabled (switched on) or not (switched off) for metric source.
+     *
+     * @return {@code True} if metrics are enabled, otherwise - {@code false}.
+     */
+    boolean enabled();
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/examples/threadpool/ThreadPoolExample.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/examples/threadpool/ThreadPoolExample.java
new file mode 100644
index 0000000..b6a87ed
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/examples/threadpool/ThreadPoolExample.java
@@ -0,0 +1,41 @@
+package org.apache.ignite.internal.metrics.examples.threadpool;
+
+import org.apache.ignite.internal.metrics.MetricsRegistry;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class ThreadPoolExample {
+    public static void main(String[] args) {
+        // Should be one per node.
+        MetricsRegistry registry = new MetricsRegistry();
+
+        // ------------------------------------------------------------------------
+
+        // System component, e.g. thread pool executor
+        ThreadPoolExecutor exec = new ThreadPoolExecutor(4, 4,
+                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
+
+        // Metrics source for thread pool
+        ThreadPoolMetricsSource src = new ThreadPoolMetricsSource("example.thread_pool.ExamplePool", exec);
+
+        // Register source after the component created.
+        registry.registerSource(src);
+
+        // ------------------------------------------------------------------------
+
+        // Enable metrics by signal (or because configuration)
+        registry.enable(src.name());
+
+        // ------------------------------------------------------------------------
+
+        // Disable metrics by signal
+        registry.disable(src.name());
+
+        // ------------------------------------------------------------------------
+
+        // Component is stopped\destroyed
+        registry.unregisterSource(src);
+    }
+}
diff --git a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/examples/threadpool/ThreadPoolMetricsSource.java b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/examples/threadpool/ThreadPoolMetricsSource.java
new file mode 100644
index 0000000..ae6c42a
--- /dev/null
+++ b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/examples/threadpool/ThreadPoolMetricsSource.java
@@ -0,0 +1,74 @@
+package org.apache.ignite.internal.metrics.examples.threadpool;
+
+import org.apache.ignite.internal.metrics.AbstractMetricsSource;
+import org.apache.ignite.internal.metrics.MetricsSetBuilder;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+public class ThreadPoolMetricsSource extends AbstractMetricsSource<ThreadPoolMetricsSource.Holder> {
+    private final ThreadPoolExecutor exec;
+
+    public ThreadPoolMetricsSource(String name, ThreadPoolExecutor exec) {
+        super(name);
+
+        this.exec = exec;
+    }
+
+
+    /** {@inheritDoc} */
+    @Override protected Holder createHolder() {
+        return new Holder();
+    }
+
+    @Override
+    protected void init(MetricsSetBuilder bldr, Holder holder) {
+        bldr.intGauge(
+                "ActiveCount",
+                "Approximate number of threads that are actively executing tasks.",
+                exec::getActiveCount
+        );
+
+        bldr.longGauge(
+                "CompletedTaskCount",
+                "Approximate total number of tasks that have completed execution.",
+                exec::getCompletedTaskCount
+        );
+
+        bldr.intGauge("CorePoolSize", "The core number of threads.", exec::getCorePoolSize);
+
+        bldr.intGauge(
+                "LargestPoolSize",
+                "Largest number of threads that have ever simultaneously been in the pool.",
+                exec::getLargestPoolSize
+        );
+
+        bldr.intGauge(
+                "MaximumPoolSize",
+                "The maximum allowed number of threads.",
+                exec::getMaximumPoolSize
+        );
+
+        bldr.intGauge("PoolSize", "Current number of threads in the pool.", exec::getPoolSize);
+
+        bldr.longGauge(
+                "TaskCount",
+                "Approximate total number of tasks that have been scheduled for execution.",
+                exec::getTaskCount
+        );
+
+        bldr.intGauge("QueueSize", "Current size of the execution queue.", () -> exec.getQueue().size());
+
+        bldr.longGauge(
+                "KeepAliveTime",
+                "Thread keep-alive time, which is the amount of time which threads in excess of " +
+                "the core pool size may remain idle before being terminated.",
+                () -> exec.getKeepAliveTime(MILLISECONDS)
+        );
+    }
+
+    protected static class Holder implements AbstractMetricsSource.Holder<Holder> {
+
+    }
+}
diff --git a/pom.xml b/pom.xml
index 3893628..0248e2f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,7 @@
         <module>modules/metastorage-client</module>
         <module>modules/metastorage-common</module>
         <module>modules/metastorage-server</module>
+        <module>modules/metrics</module>
         <module>modules/network</module>
         <module>modules/network-annotation-processor</module>
         <module>modules/network-api</module>