diff --git a/pom.xml b/pom.xml index cd686d3c1..3b77c86a1 100644 --- a/pom.xml +++ b/pom.xml @@ -64,7 +64,7 @@ unknown - 4.0.19.Final + 4.0.20.Final UTF-8 diff --git a/proxy/pom.xml b/proxy/pom.xml index f5e40c474..7fe39226b 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,6 +29,12 @@ ${netty.version} compile + + io.netty + netty-transport-native-epoll + ${netty.version} + compile + jline jline diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 2f49735ae..484671f91 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -21,9 +21,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; -import io.netty.channel.MultithreadEventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.EventLoopGroup; import io.netty.util.ResourceLeakDetector; import net.md_5.bungee.conf.Configuration; import java.io.File; @@ -33,7 +31,6 @@ import java.net.InetSocketAddress; import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; @@ -97,7 +94,7 @@ public class BungeeCord extends ProxyServer * Localization bundle. */ public ResourceBundle bundle; - public MultithreadEventLoopGroup eventLoops; + public EventLoopGroup eventLoops; /** * locations.yml save thread. */ @@ -206,7 +203,7 @@ public class BungeeCord extends ProxyServer System.setProperty( "io.netty.selectorAutoRebuildThreshold", "0" ); // Seems to cause Bungee to stop accepting connections ResourceLeakDetector.setEnabled( false ); // Eats performance - eventLoops = new NioEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() ); + eventLoops = PipelineUtils.newEventLoopGroup(0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() ); File moduleDirectory = new File( "modules" ); moduleManager.load( this, moduleDirectory ); @@ -259,7 +256,7 @@ public class BungeeCord extends ProxyServer } }; new ServerBootstrap() - .channel( NioServerSocketChannel.class ) + .channel( PipelineUtils.getServerChannel() ) .childAttr( PipelineUtils.LISTENER, info ) .childHandler( PipelineUtils.SERVER_CHILD ) .group( eventLoops ) diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java index 6613016cd..2c3bb9187 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java @@ -5,7 +5,6 @@ import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelOption; -import io.netty.channel.socket.nio.NioSocketChannel; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; @@ -131,7 +130,7 @@ public class BungeeServerInfo implements ServerInfo } }; new Bootstrap() - .channel( NioSocketChannel.class ) + .channel( PipelineUtils.getChannel() ) .group( BungeeCord.getInstance().eventLoops ) .handler( PipelineUtils.BASE ) .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index 5b06bf88d..10e732e66 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -7,7 +7,6 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; -import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.internal.PlatformDependent; import java.net.InetSocketAddress; import java.util.Collection; @@ -261,7 +260,7 @@ public final class UserConnection implements ProxiedPlayer } }; Bootstrap b = new Bootstrap() - .channel( NioSocketChannel.class ) + .channel( PipelineUtils.getChannel() ) .group( ch.getHandle().eventLoop() ) .handler( initializer ) .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable diff --git a/proxy/src/main/java/net/md_5/bungee/http/HttpClient.java b/proxy/src/main/java/net/md_5/bungee/http/HttpClient.java index e1f2c1b60..c8e4af867 100644 --- a/proxy/src/main/java/net/md_5/bungee/http/HttpClient.java +++ b/proxy/src/main/java/net/md_5/bungee/http/HttpClient.java @@ -8,7 +8,6 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoop; -import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; @@ -21,6 +20,7 @@ import java.util.concurrent.TimeUnit; import lombok.AccessLevel; import lombok.NoArgsConstructor; import net.md_5.bungee.api.Callback; +import net.md_5.bungee.netty.PipelineUtils; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class HttpClient @@ -90,7 +90,7 @@ public class HttpClient } }; - new Bootstrap().channel( NioSocketChannel.class ).group( eventLoop ).handler( new HttpInitializer( callback, ssl ) ). + new Bootstrap().channel( PipelineUtils.getChannel() ).group( eventLoop ).handler( new HttpInitializer( callback, ssl ) ). option( ChannelOption.CONNECT_TIMEOUT_MILLIS, TIMEOUT ).remoteAddress( inetHost, port ).connect().addListener( future ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java index 24261072b..8239799fa 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -5,13 +5,25 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.ServerChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollServerSocketChannel; +import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.AttributeKey; +import io.netty.util.internal.PlatformDependent; import java.net.InetSocketAddress; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.BungeeServerInfo; import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ListenerInfo; @@ -62,6 +74,47 @@ public class PipelineUtils public static String LEGACY_DECODER = "legacy-decoder"; public static String LEGACY_KICKER = "legacy-kick"; + private static boolean epoll; + + static + { + if ( !PlatformDependent.isWindows() ) + { + ProxyServer.getInstance().getLogger().info( "Not on Windows, attempting to use enhanced EpollEventLoop" ); + EpollEventLoopGroup testGroup = null; + try + { + testGroup = new EpollEventLoopGroup( 1 ); + epoll = true; + ProxyServer.getInstance().getLogger().info( "Epoll is working, utilising it!" ); + } catch ( Throwable t ) + { + ProxyServer.getInstance().getLogger().log( Level.WARNING, "Event though Epoll should be supported, it is not working, falling back to NIO: {0}", Util.exception( t ) ); + } finally + { + if ( testGroup != null ) + { + testGroup.shutdownGracefully(); + } + } + } + } + + public static EventLoopGroup newEventLoopGroup(int threads, ThreadFactory factory) + { + return epoll ? new EpollEventLoopGroup( threads, factory ) : new NioEventLoopGroup( threads, factory ); + } + + public static Class getServerChannel() + { + return epoll ? EpollServerSocketChannel.class : NioServerSocketChannel.class; + } + + public static Class getChannel() + { + return epoll ? EpollSocketChannel.class : NioSocketChannel.class; + } + public final static class Base extends ChannelInitializer {