今天在翻看netty的源碼的時候發現netty對EventLoopGroup的實現有不止常用的NIOEventLoopGroup ,一共有以下幾種。
- EpollEventLoopGroup
- NioEventLoopGroup
- KQueueEventLoopGroup
其中NioEventLoopGroup則是我們比較常用的,這個使用了java NIO中的SelectorProvider.provider()來選擇系統默認的selectorProvider(即多路復用IO的支持)。
public NioEventLoopGroup(ThreadFactory threadFactory) { this(0, threadFactory, SelectorProvider.provider()); }
而JDK則會根據自己的版本來返回默認的。
public static SelectorProvider provider() { synchronized (lock) { if (provider != null) return provider; return AccessController.doPrivileged( new PrivilegedAction<SelectorProvider>() { public SelectorProvider run() { if (loadProviderFromProperty()) return provider; if (loadProviderAsService()) return provider; provider = sun.nio.ch.DefaultSelectorProvider.create(); return provider; } }); } }
如果我們沒有在參數中指定,那么provider = sun.nio.ch.DefaultSelectorProvider.create();這句代碼則會找到當前jdk版本默認的selectorProvider。 不同的系統對應的默認selectorProvider不一樣。
oracle jdk也會根據發行的不同操作系統的版本來提供不同的selectorProvider實現 例如windows的
則是WindowsSelectorProvider (注意本文是以oracle jdk為例,open jdk這兒的實現是不一樣的)
public class DefaultSelectorProvider { private DefaultSelectorProvider() { } public static SelectorProvider create() { return new WindowsSelectorProvider(); } }
而BSD/MAC OS則是KQueue->KQueueSelectorProvider ,linux則是Epoll->EPollSelectorProvider(注意linux 2.5.44以后才提供,之前均為select/poll) 。而java NIO會根據jvm的版本來選擇合適的selectorProvider,使用這個是確保不會出現系統
切換的問題,即java跨平台的特性還是得到保證的。那netty為何還要多此一舉的為EPollSelectorProvider和KQueueSelectorProvider單獨提供對應的EventLoopGroup即EpollEventLoopGroup和KQueueEventLoopGroup呢。要知道手動指定會有操作系統不匹配的風險,例如如下代碼
public class Server { public static void main(String[] args) throws InterruptedException { ServerBootstrap serverBootstrap = new ServerBootstrap(); ChannelFuture channelFuture = serverBootstrap.group(new EpollEventLoopGroup(1) , new EpollEventLoopGroup(10)) .channel(EpollServerSocketChannel.class) .handler(new LoggingHandler()) .childHandler(new InitialierHandler()) .bind(8080) .sync(); channelFuture.channel().closeFuture().sync(); } }
如果這段代碼在windows或者mac os上運行則會報本文標題上的錯 (將其中EpollEnventLoopGroup換為NIOEventLoopGroup,EpollServerSocketChannel 換為NIOsERVERSocketChannel則可以平台通用)
Exception in thread "main" java.lang.UnsatisfiedLinkError: failed to load the required native library at io.netty.channel.epoll.Epoll.ensureAvailability(Epoll.java:80) at io.netty.channel.epoll.EpollEventLoop.<clinit>(EpollEventLoop.java:51) at io.netty.channel.epoll.EpollEventLoopGroup.newChild(EpollEventLoopGroup.java:150) at io.netty.channel.epoll.EpollEventLoopGroup.newChild(EpollEventLoopGroup.java:35) at io.netty.util.concurrent.MultithreadEventExecutorGroup.<init>(MultithreadEventExecutorGroup.java:84) at io.netty.util.concurrent.MultithreadEventExecutorGroup.<init>(MultithreadEventExecutorGroup.java:58) at io.netty.util.concurrent.MultithreadEventExecutorGroup.<init>(MultithreadEventExecutorGroup.java:47) at io.netty.channel.MultithreadEventLoopGroup.<init>(MultithreadEventLoopGroup.java:59) at io.netty.channel.epoll.EpollEventLoopGroup.<init>(EpollEventLoopGroup.java:112) at io.netty.channel.epoll.EpollEventLoopGroup.<init>(EpollEventLoopGroup.java:99) at io.netty.channel.epoll.EpollEventLoopGroup.<init>(EpollEventLoopGroup.java:76) at io.netty.channel.epoll.EpollEventLoopGroup.<init>(EpollEventLoopGroup.java:52) at netty.Server.main(Server.java:22) Caused by: java.lang.ExceptionInInitializerError at io.netty.channel.epoll.Epoll.<clinit>(Epoll.java:39) ... 12 more Caused by: java.lang.IllegalStateException: Only supported on Linux at io.netty.channel.epoll.Native.loadNativeLibrary(Native.java:225) at io.netty.channel.epoll.Native.<clinit>(Native.java:58) ... 13 more
俗話說,有風險,肯定就有收益。netty單獨提供的epoll/KQueue實現肯定是有性能上的優化的。這兒可以引用一下官方文檔,地址:https://netty.io/wiki/native-transports.html
其中大概的解釋了下原因。
即相比於java NIO,提供了更多的特定平台的功能,產生更少的垃圾,總體上提高了性能。java JDK因為要保證跨平台所以在功能上必須做出妥協
所以我們在構建項目的時候可以根據項目指定環境的不同來指定不同的EnventLoopGroup提升性能。例如開發環境 windows可以使用java的NIO ,mac可以使用netty定制的KqueueSelector.
生產環境一般為linux則可以使用netty定制的EpollSelector來提高負載性能