目錄
- 簡介
- netty服務的基本建構方式
- EventLoopGroup
- channel
- 多種建構方式
- 其他的channel
- 總結
我們知道netty的基礎是channel和在channel之上的selector,當然作為一個nio架構,channel和selector不僅僅是netty的基礎,也是所有nio實作的基礎。
同樣的,我們知道netty很多種不同的協定,這些協定都是在channel上進行通訊的,那麼對于不同的協定來說,使用的channel和selector會有所不同嗎?
帶着這個疑問,我們一起來深入探究一下吧。
netty可以分為用戶端和伺服器端,實際上用戶端和伺服器端的構造方式差别不大,這裡為了簡單起見,以netty中伺服器端的建構為例子進行研究。
回顧一下我們最開始搭建的netty伺服器,其對應的代碼如下:
//建立兩個EventloopGroup用來處理連接配接和消息
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new FirstServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 綁定端口并開始接收連接配接
ChannelFuture f = b.bind(port).sync();
我們要注意的是兩個地方,一個是ServerBootstrap的group方法,一個是它的channel方法。
group有兩種實作方式,可以帶一個參數,也可以帶兩個參數。參數都是EventLoopGroup,EventLoopGroup主要用來注冊channel, 供後續的Selector進行選擇。
如果使用一個參數的形式,則一個EventLoopGroup同時處理acceptor和client的事件,如果使用兩個參數,則會将兩者分開。
當然,這都不是今天要講的重點,今天要講的是EventLoopGroup的建構在不同的協定中有什麼不同。
EventLoopGroup本身是一個接口,他有很多種實作,但是本質上還是兩種EventLoop:SingleThreadEventLoop和MultithreadEventLoopGroup.
也就是用單線程進行EventLoop處理和多線程進行EventLoop處理。
比如上面我們常用的NioEventLoopGroup,就是一個單線程的EventLoop。
NioEventLoopGroup通常我們使用的是無參的構造函數,實際上NioEventLoopGroup可以傳入ThreadFactory,thread的個數,SelectorProvider和SelectStrategyFactory.
netty隻提供了一個SelectStrategyFactory的實作:DefaultSelectStrategyFactory。
而對應SelectorProvider來說,預設的實作是SelectorProvider.provider(), 我們看下這個方法的具體實作:
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;
}
});
}
}
可以看到預設情況下,SelectorProvider有三種建立方式。
第一種就是從系統屬性中查找:java.nio.channels.spi.SelectorProvider:
String cn = System.getProperty("java.nio.channels.spi.SelectorProvider");
Class<?> c = Class.forName(cn, true,
ClassLoader.getSystemClassLoader());
provider = (SelectorProvider)c.newInstance();
如果有定義,則建立一個執行個體傳回。
如果沒有的話,則會從"META-INF/services/"中加載service Loader :
private static boolean loadProviderAsService() {
ServiceLoader<SelectorProvider> sl =
ServiceLoader.load(SelectorProvider.class,
ClassLoader.getSystemClassLoader());
Iterator<SelectorProvider> i = sl.iterator();
如果servie也沒有找到的話,則會使用最後預設的sun.nio.ch.DefaultSelectorProvider.
預設情況下,我們使用的是NioServerSocketChannel。他實際是從上面提到的預設的SelectorProvider來建立的。
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
return DEFAULT_SELECTOR_PROVIDER.openServerSocketChannel();
是以使用的channel需要跟selector相比對。
我們可以直接使用channel,也可以使用ChannelFactory,通過這些Factory來生成channel。
如果要使用ChannelFactory,則可以調用ServerBootstrap的channelFactory方法。
上面提到了最基本的netty server建構方式。對應的是socket協定。
如果是要進行UDP連接配接,對應的channel應該換成NioDatagramChannel,如下:
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new UDPServerHandler());
b.bind(PORT).sync().channel().closeFuture().await();
EventLoopGroup可以保持不變。
因為netty底層是基于Socket進行通訊的,socket底層又是基于TCP或者UDP協定,是以在netty中實作的http或者http2或者SOCKS協定都是在socket連接配接基礎上進行的。
是以對http或者http2來說,channel還是NioServerSocketChannel。
可以看到隻有UDP協定有所不同。同樣的基于UDP協定之上的UDT協定也是不同的,其使用如下:
final NioEventLoopGroup acceptGroup = new NioEventLoopGroup(1, acceptFactory, NioUdtProvider.BYTE_PROVIDER);
final NioEventLoopGroup connectGroup = new NioEventLoopGroup(1, connectFactory, NioUdtProvider.BYTE_PROVIDER);
final ServerBootstrap boot = new ServerBootstrap();
boot.group(acceptGroup, connectGroup)
.channelFactory(NioUdtProvider.BYTE_ACCEPTOR)
.option(ChannelOption.SO_BACKLOG, 10)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<UdtChannel>() {
@Override
public void initChannel(final UdtChannel ch) {
ch.pipeline().addLast(
new LoggingHandler(LogLevel.INFO),
new UDTEchoServerHandler());
}
});
UDT使用的是NioUdtProvider中提供的BYTE_PROVIDER和BYTE_ACCEPTOR分别作為selector和channelFactory。
除了NioSocketChannel之外,還有EpollChannel、KQueueChannel、SctpChannel,這些channel都是針對不同協定來使用的。我們會在後續的文章中詳細進行介紹。
channel和selector是netty的基礎,在這基礎之上,netty可以擴充适配所有基于tcp和udp的協定,可以說非常的強大。
本文已收錄于 http://www.flydean.com/39-netty-selecto…r-channelfactory/
最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注我的公衆号:「程式那些事」,懂技術,更懂你!