[Performance] Attempt to use Netty's Epoll implementation on Linux.

This will attempt to make use of Netty's EpollEventLoopGroup and Epoll(Server)SocketChannel when on Linux and the native libraries load correctly. It should bring a large increase in performance and hopefully reliability. Big thanks to the @netty team for implementing this and @normanmaurer for some tips on the support detection.

Feedback is appreciated.
This commit is contained in:
md_5 2014-06-25 18:14:50 +10:00
parent 2b304ecebc
commit 500b0af782
7 changed files with 68 additions and 14 deletions

View File

@ -64,7 +64,7 @@
<properties>
<build.number>unknown</build.number>
<netty.version>4.0.19.Final</netty.version>
<netty.version>4.0.20.Final</netty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@ -29,6 +29,12 @@
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jline</groupId>
<artifactId>jline</artifactId>

View File

@ -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 )

View File

@ -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

View File

@ -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

View File

@ -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 );
}
}

View File

@ -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<? extends ServerChannel> getServerChannel()
{
return epoll ? EpollServerSocketChannel.class : NioServerSocketChannel.class;
}
public static Class<? extends Channel> getChannel()
{
return epoll ? EpollSocketChannel.class : NioSocketChannel.class;
}
public final static class Base extends ChannelInitializer<Channel>
{