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;