共计 4403 个字符,预计需要花费 12 分钟才能阅读完成。
简介
咱们晓得 netty 的根底是 channel 和在 channel 之上的 selector, 当然作为一个 nio 框架,channel 和 selector 不仅仅是 netty 的根底,也是所有 nio 实现的根底。
同样的,咱们晓得 netty 很多种不同的协定,这些协定都是在 channel 上进行通信的,那么对于不同的协定来说,应用的 channel 和 selector 会有所不同吗?
带着这个疑难,咱们一起来深刻探索一下吧。
netty 服务的根本构建形式
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 办法。
EventLoopGroup
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.
channel
默认状况下,咱们应用的是 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。
其余的 channel
除了 NioSocketChannel 之外,还有 EpollChannel、KQueueChannel、SctpChannel, 这些 channel 都是针对不同协定来应用的。咱们会在后续的文章中具体进行介绍。
总结
channel 和 selector 是 netty 的根底,在这根底之上,netty 能够扩大适配所有基于 tcp 和 udp 的协定,能够说十分的弱小。
本文已收录于 http://www.flydean.com/39-netty-selecto…r-channelfactory/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!