diff --git a/NOTICE.txt b/NOTICE.txt
index 6fbd3e3cca..15f070e5bd 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -90,8 +90,10 @@ serialization API, which can be obtained at:
* HOMEPAGE:
* http://www.jboss.org/jbossmarshalling
-This product optionally depends on 'SLF4J', a simple logging facade for Java,
-which can be obtained at:
+This product optionally depends on 'SLF4J', a simple logging facade for Java.
+The monitor registries implementation is based on the approach used by SLF4J.
+
+Slf4j can be obtained at:
* LICENSE:
* license/LICENSE.slf4j.txt (MIT License)
diff --git a/common/src/main/java/io/netty/monitor/MonitorRegistries.java b/common/src/main/java/io/netty/monitor/MonitorRegistries.java
index 289ee19bf1..f1c8735b74 100644
--- a/common/src/main/java/io/netty/monitor/MonitorRegistries.java
+++ b/common/src/main/java/io/netty/monitor/MonitorRegistries.java
@@ -15,6 +15,8 @@
*/
package io.netty.monitor;
+import io.netty.logging.InternalLogger;
+import io.netty.logging.InternalLoggerFactory;
import io.netty.monitor.spi.MonitorProvider;
import io.netty.monitor.spi.MonitorRegistryFactory;
@@ -38,9 +40,19 @@ import java.util.ServiceLoader;
*
*/
public final class MonitorRegistries implements Iterable {
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(MonitorRegistries.class);
+ //set of initialization states
+ private static final int UNINITIALIZED = 0;
+ private static final int ONGOING_INITIALIZATION = 1;
+ private static final int SUCCESSFUL_INITIALIZATION = 2;
+ private static final int NOP_FALLBACK_INITIALIZATION = 3;
+
+ private static int INITIALIZATION_STATE = UNINITIALIZED;
+ private static MonitorRegistry selectedRegistry;
/**
* Return the singleton {@code MonitorRegistries} instance.
+ *
* @return The singleton {@code MonitorRegistries} instance
*/
public static MonitorRegistries instance() {
@@ -57,14 +69,15 @@ public final class MonitorRegistries implements Iterable {
/**
* Create a new {@link MonitorRegistry} that supports the supplied
* {@link MonitorProvider provider}.
+ *
* @param provider The {@link MonitorProvider provider} we are interested in
* @return A {@link MonitorRegistry} implemented by the supplied
* {@link MonitorProvider provider}
- * @throws NullPointerException If {@code provider} is {@code null}
+ * @throws NullPointerException If {@code provider} is {@code null}
* @throws IllegalArgumentException If no {@code MonitorRegistry} matching
- * the given {@link MonitorProvider provider} could be found
+ * the given {@link MonitorProvider provider} could be found
*/
- public MonitorRegistry forProvider(final MonitorProvider provider) {
+ public static MonitorRegistry forProvider(final MonitorProvider provider) {
if (provider == null) {
throw new NullPointerException("provider");
}
@@ -79,34 +92,49 @@ public final class MonitorRegistries implements Iterable {
/**
*
- * Look up and return the uniquely determined
- * {@link MonitorRegistry} implementation. This method will work in the
- * standard situation where exactly one {@link MonitorRegistryFactory} is
- * registered in
+ * Look up and return the a uniquely determined
+ * {@link MonitorRegistry} implementation. This method will select
+ * exactly one {@link MonitorRegistryFactory} from those registered in
* {@code META-INF/services/io.netty.monitor.spi.MonitorRegistryFactory}.
- * Otherwise, if either none or more than one such provider is found on the
- * classpath, it will throw an {@code IllegalStateException}.
+ * if no implementation is found then a NOOP registry is returned.
+ * If multiple implementations are found then the first one returned by
+ * {@link #iterator()} is used and a message is logged to say which one is
+ * selected.
*
- * @return The uniquely determined {@link MonitorRegistry}
+ *
+ * @return the uniquely determined {@link MonitorRegistry}
* implementation
- * @throws IllegalStateException If either none or more that one
- * {@link MonitorRegistries} provider was found on the
- * classpath
*/
public MonitorRegistry unique() {
+ //Implementation based on SLF4J's
+ if (INITIALIZATION_STATE == UNINITIALIZED) {
+ INITIALIZATION_STATE = ONGOING_INITIALIZATION;
+ performInitialization();
+ }
+ switch (INITIALIZATION_STATE) {
+ case SUCCESSFUL_INITIALIZATION:
+ return selectedRegistry;
+ case NOP_FALLBACK_INITIALIZATION:
+ case ONGOING_INITIALIZATION:
+ default:
+ return MonitorRegistry.NOOP;
+ }
+ }
+
+ private void performInitialization() {
final Iterator registries = iterator();
- if (!registries.hasNext()) {
- throw new IllegalStateException("Could not find any MonitorRegistries the classpath - "
- + "implementations need to be registered in META-INF/services/"
- + MonitorRegistryFactory.class.getName());
- }
- final MonitorRegistry candidate = registries.next();
if (registries.hasNext()) {
- throw new IllegalStateException("Found more than one MonitorRegistryFactory on the classpath - "
- + "check if there is more than one implementation registered in META-INF/services/"
- + MonitorRegistryFactory.class.getName());
+ selectedRegistry = registries.next();
+ INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
+ }
+ if (selectedRegistry != null && registries.hasNext()) {
+ logger.warn(String.format("Multiple metrics implementations found. " +
+ "Selected %s, ignoring other implementations", selectedRegistry.getClass().getName()));
+ }
+ if (selectedRegistry == null) {
+ INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
+ logger.debug("No metrics implementation found on the classpath.");
}
- return candidate;
}
/**
diff --git a/common/src/test/java/io/netty/monitor/MonitorRegistriesTest.java b/common/src/test/java/io/netty/monitor/MonitorRegistriesTest.java
index 388100e157..bf86771903 100644
--- a/common/src/test/java/io/netty/monitor/MonitorRegistriesTest.java
+++ b/common/src/test/java/io/netty/monitor/MonitorRegistriesTest.java
@@ -35,7 +35,7 @@ public class MonitorRegistriesTest {
registry.getClass());
}
- @Test(expected = IllegalStateException.class)
+ @Test
public final void uniqueShouldThrowIllegalStateExceptionIfMoreThanOneProviderIsRegistered() {
final MonitorRegistries objectUnderTest = MonitorRegistries.instance();
diff --git a/example/pom.xml b/example/pom.xml
index e19518b7b2..7755f2afdb 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -59,6 +59,11 @@
com.google.protobuf
protobuf-java
+
+ ${project.groupId}
+ netty-metrics-yammer
+ ${project.version}
+
diff --git a/transport/src/main/java/io/netty/channel/MultithreadEventExecutorGroup.java b/transport/src/main/java/io/netty/channel/MultithreadEventExecutorGroup.java
index f48d2ef827..917b5d4311 100644
--- a/transport/src/main/java/io/netty/channel/MultithreadEventExecutorGroup.java
+++ b/transport/src/main/java/io/netty/channel/MultithreadEventExecutorGroup.java
@@ -22,6 +22,9 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import io.netty.monitor.CounterMonitor;
+import io.netty.monitor.MonitorName;
+import io.netty.monitor.MonitorRegistries;
/**
* Abstract base class for {@link EventExecutorGroup} implementations that handles their tasks with multiple threads at
* the same time.
@@ -30,6 +33,8 @@ public abstract class MultithreadEventExecutorGroup implements EventExecutorGrou
public static final int DEFAULT_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
private static final AtomicInteger poolId = new AtomicInteger();
+ private final CounterMonitor threadCounter = MonitorRegistries.instance()
+ .unique().newCounterMonitor(new MonitorName(getClass(), "total-threads"));
final ChannelTaskScheduler scheduler;
private final EventExecutor[] children;
@@ -57,6 +62,7 @@ public abstract class MultithreadEventExecutorGroup implements EventExecutorGrou
if (threadFactory == null) {
threadFactory = new DefaultThreadFactory();
}
+ threadCounter.increment(nThreads);
scheduler = new ChannelTaskScheduler(threadFactory);
diff --git a/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java b/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java
index ed79380f81..4ca188705d 100644
--- a/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java
+++ b/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java
@@ -15,6 +15,10 @@
*/
package io.netty.channel;
+import io.netty.monitor.CounterMonitor;
+import io.netty.monitor.MonitorName;
+import io.netty.monitor.MonitorRegistries;
+
import java.util.concurrent.ThreadFactory;
/**
@@ -22,6 +26,8 @@ import java.util.concurrent.ThreadFactory;
*
*/
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
+ protected CounterMonitor channelCounter = MonitorRegistries.instance()
+ .unique().newCounterMonitor(new MonitorName(getClass(), "total-channels-registered"));
/**
*
@@ -68,6 +74,8 @@ public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor im
}
});
}
+
+ channelCounter.increment();
return promise;
}
}
diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java
index 2021a8da49..e9b39de083 100644
--- a/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java
+++ b/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java
@@ -15,6 +15,7 @@
*/
package io.netty.channel.socket.nio;
+
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelTaskScheduler;
@@ -23,6 +24,9 @@ import io.netty.channel.SingleThreadEventLoop;
import io.netty.channel.socket.nio.AbstractNioChannel.NioUnsafe;
import io.netty.logging.InternalLogger;
import io.netty.logging.InternalLoggerFactory;
+import io.netty.monitor.CounterMonitor;
+import io.netty.monitor.MonitorName;
+import io.netty.monitor.MonitorRegistries;
import java.io.IOException;
import java.nio.channels.CancelledKeyException;
@@ -38,6 +42,7 @@ import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -46,6 +51,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
*
*/
public final class NioEventLoop extends SingleThreadEventLoop {
+ private final CounterMonitor selectorWokeUpBeforeTime = MonitorRegistries.instance()
+ .unique().newCounterMonitor(new MonitorName(getClass(), "Selector.select-early-wake-up"));
/**
* Internal Netty logger.
@@ -232,6 +239,14 @@ public final class NioEventLoop extends SingleThreadEventLoop {
try {
long beforeSelect = System.nanoTime();
int selected = SelectorUtil.select(selector);
+
+ //measure how long select took & convert nano time to milliseconds
+ long totalSelectTime = System.nanoTime() - beforeSelect;
+ long selectTime = TimeUnit.MILLISECONDS.convert(totalSelectTime, TimeUnit.NANOSECONDS);
+ if (selectTime < SelectorUtil.SELECT_TIMEOUT) {
+ selectorWokeUpBeforeTime.increment();
+ }
+
if (SelectorUtil.EPOLL_BUG_WORKAROUND) {
if (selected == 0) {
long timeBlocked = System.nanoTime() - beforeSelect;