From 3a52cc410aac560dbc56b32034606879125d7f90 Mon Sep 17 00:00:00 2001 From: Courtney Robinson Date: Fri, 30 Nov 2012 06:07:26 +0000 Subject: [PATCH] Add some of the metrics mentioned in #718 use single static initialization of available metrics monitor registries * This changes the original implementation to work in a similar way to how slf4j selects and loads an implementation. * Uses a single static instance so intialization is done only once. * Doesn't throw IllegalStateException if multiple implementations are found on the classpath. It instead selects and uses the first implementation returned by iterator() * Class left as an iterable to keep the API the same add yammer metrics to examples to allow them to publish metrics publish the number of threads used in an EventLoopGroup see issue #718 * seems like the better place to put this because it sets the default thread count if the MultithreadEventLoopGroup uses super(0,...) * It also happens to be the common parent class amongst all the MultiThreadedEventLoopGroup implementations * Count is reported for io.netty.channel.{*,.local,.socket.aio,.socket.nio} fix cosmetic issues pointed out in pull request and updated notice.txt see https://github.com/netty/netty/pull/780 count # of channels registered in single threaded event loop measure how many times Selector.select return before SELECT_TIME --- NOTICE.txt | 6 +- .../io/netty/monitor/MonitorRegistries.java | 74 +++++++++++++------ .../netty/monitor/MonitorRegistriesTest.java | 2 +- example/pom.xml | 5 ++ .../MultithreadEventExecutorGroup.java | 6 ++ .../netty/channel/SingleThreadEventLoop.java | 8 ++ .../channel/socket/nio/NioEventLoop.java | 15 ++++ 7 files changed, 90 insertions(+), 26 deletions(-) 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;