From dc5f64777ebe2d58cd3f7b82bb494b8cf89f7b85 Mon Sep 17 00:00:00 2001 From: Ivan Gorbachev Date: Tue, 26 Nov 2024 10:56:43 +0000 Subject: [PATCH] finagle-netty4: Add epollEventLoopGroupClassName flag Problem We need to be able to use custom implementation of event loop, e.g. with additional logging or metrics. Solution Add com.twitter.finagle.netty4.epollEventLoopGroupClassName flag. It specifies the full classname that should be used instead of default netty implementation. JIRA Issues: STOR-8478 Differential Revision: https://phabricator.twitter.biz/D1185136 --- CHANGELOG.rst | 1 + .../finagle/netty4/WorkerEventLoop.scala | 29 ++++++++++++++----- .../netty4/epollEventLoopGroupClassName.scala | 12 ++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 finagle-netty4/src/main/scala/com/twitter/finagle/netty4/epollEventLoopGroupClassName.scala diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 784202a51ef..9a8ca0b09b8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -28,6 +28,7 @@ New Features * finagle-mysql: Added support for LONG_BLOB data type. ``PHAB_ID=D1152247`` * finagle-mux: Added a stat, /mux/request_context_bytes, that shows the size of the received context per request. +* finagle-netty4: Added support for custom event loop implementations. ``PHAB_ID=D1185136`` Bug Fixes diff --git a/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/WorkerEventLoop.scala b/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/WorkerEventLoop.scala index c42c08cbf74..98e5027c8d4 100644 --- a/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/WorkerEventLoop.scala +++ b/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/WorkerEventLoop.scala @@ -2,6 +2,7 @@ package com.twitter.finagle.netty4 import com.twitter.finagle.stats.FinagleStatsReceiver import com.twitter.finagle.util.BlockingTimeTrackingThreadFactory +import com.twitter.util.logging.Logger import io.netty.channel.EventLoopGroup import io.netty.channel.epoll.Epoll import io.netty.channel.epoll.EpollEventLoopGroup @@ -19,6 +20,8 @@ import java.util.concurrent.ThreadFactory */ private object WorkerEventLoop { + private[this] val log = Logger.getLogger(getClass.getName) + private[this] val workerPoolSize = new AtomicInteger(0) private[this] val eventLoopGroups = ConcurrentHashMap.newKeySet[EventLoopGroup]() @@ -63,15 +66,27 @@ private object WorkerEventLoop { def make(executor: Executor, numWorkers: Int, ioRatio: Int = 50): EventLoopGroup = { workerPoolSize.addAndGet(numWorkers) - val result = - if (useNativeEpoll() && Epoll.isAvailable) { - val group = new EpollEventLoopGroup(numWorkers, executor) - group.setIoRatio(ioRatio) - group - } else { - new NioEventLoopGroup(numWorkers, executor) + val result = if (epollEventLoopGroupClassName().nonEmpty) { + val clz = Class.forName(epollEventLoopGroupClassName()) + val eventLoopGroup = clz + .getConstructor(classOf[Int], classOf[Executor]) + .newInstance(java.lang.Integer.valueOf(numWorkers), executor) + try { + val setIoRatioMethod = clz.getMethod("setIoRatio", classOf[Int]) + setIoRatioMethod.invoke(eventLoopGroup, java.lang.Integer.valueOf(ioRatio)) + } catch { + case _: NoSuchMethodException | _: SecurityException => // no-op } + eventLoopGroup.asInstanceOf[EventLoopGroup] + } else if (useNativeEpoll() && Epoll.isAvailable) { + val group = new EpollEventLoopGroup(numWorkers, executor) + group.setIoRatio(ioRatio) + group + } else { + new NioEventLoopGroup(numWorkers, executor) + } + log.info(s"Created event loop group: ${result.getClass.getName}") eventLoopGroups.add(result) result diff --git a/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/epollEventLoopGroupClassName.scala b/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/epollEventLoopGroupClassName.scala new file mode 100644 index 00000000000..0e3702ab61b --- /dev/null +++ b/finagle-netty4/src/main/scala/com/twitter/finagle/netty4/epollEventLoopGroupClassName.scala @@ -0,0 +1,12 @@ +package com.twitter.finagle.netty4 + +import com.twitter.app.GlobalFlag + +/** + * Create custom EventLoopGroup instead of default EpollEventLoopGroup or NioEventLoopGroup. + * This can be useful to run alternative implementations, e.g. with additional instrumentation. + * The provided value must be full class name, e.g. io.netty.channel.epoll.EpollEventLoopGroup, + * which implements io.netty.channel.MultithreadEventLoopGroup + */ +private object epollEventLoopGroupClassName + extends GlobalFlag[String]("", "Create custom EventLoopGroup")