简介

我们知道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/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

netty系列之:好马配好鞍,为channel选择配套的selector的更多相关文章

  1. 5. 彤哥说netty系列之Java NIO核心组件之Channel

    你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...

  2. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  3. Netty 系列之 Netty 高性能之道 高性能的三个主题 Netty使得开发者能够轻松地接受大量打开的套接字 Java 序列化

    Netty系列之Netty高性能之道 https://www.infoq.cn/article/netty-high-performance 李林锋 2014 年 5 月 29 日 话题:性能调优语言 ...

  4. Netty系列之源码解析(一)

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...

  5. netty系列之:自定义编码解码器

    目录 简介 自定义编码器 自定义解码器 添加编码解码器到pipeline 计算2的N次方 总结 简介 在之前的netty系列文章中,我们讲到了如何将对象或者String转换成为ByteBuf,通过使用 ...

  6. Netty 系列之 Netty 高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用 Netty4 + Thrift 压缩二进制编解码技术,他们实现了 10 W TPS(1 K 的复杂 POJO 对象)的跨 ...

  7. Netty系列之Netty高性能之道

    转载自http://www.infoq.com/cn/articles/netty-high-performance 1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Ne ...

  8. 转:Netty系列之Netty高性能之道

    1. 背景 1.1. 惊人的性能数据 最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用 ...

  9. Netty 系列目录

    Netty 系列目录 二 Netty 源码分析(4.1.20) 1.1 Netty 源码(一)Netty 组件简介 2.1 Netty 源码(一)服务端启动 2.2 Netty 源码(二)客户端启动 ...

随机推荐

  1. Jaeger的客户端采样配置(Java版)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. ELK 使用filebeat替代Logstash收集日志

    使用beats采集日志 之前也介绍过beats是ELK体系中新增的一个工具,它属于一个轻量的日志采集器,以上我们使用的日志采集工具是logstash,但是logstash占用的资源比较大,没有beat ...

  3. 关于Marshal 类的整理

    在两个不同的实体(两个线程或者进程甚至机器.在Managed和Unmanaged之间)进行方法调用和参数传递的时候,具体的调用方法和参数的内存格式可能需要一定的转换,这个转换的过程叫做Marshal. ...

  4. lldb调试C++总结(1)

    Note 好记性不如烂笔头.时间一长,lldb的基本功快忘本了. 本文将介绍使用 lldb 调试 C++程序的基本用法. 演示基于 Ubuntu + lldb lldb + clang(++) 版本 ...

  5. 【LeetCode】973. K Closest Points to Origin 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 小根堆 日期 题目地址:https://leetco ...

  6. Myeclipse查看jdk源代码

    过程如下: 1.点 "window"-> "Preferences" -> "Java" -> "Install ...

  7. 4 种主流的 API 架构风格对比

    1RPC:调用另一个系统的函数 RPC 的工作机制 客户端调用一个远程的过程,将参数和附加信息序列化为消息,然后将消息发送到服务端.服务端在接受到消息后,将信息的内容反序列化,执行所请求的操作,然后将 ...

  8. IT6563替代芯片|DP转HDMI 2.0音视频讯号转换芯片|CS5263Capstpne

    IT6563替代芯片|CS 5263|4LAN DP转HDMI 2.0音视频讯号转换芯片IT6563是一款4LAN EDP转HDMI2.0 HDMI2.0转换器芯片.IT6563FN结合Display ...

  9. Vue的安装及使用(Vue的三种安装使用方式)

    vue是一个JavaMVVM库,是一套用于构建用户界面的渐进式框架,是初创项目的首选前端框架.它是以数据驱动和组件化的思想构建的,采用自底向上增量开发的设计.它是轻量级的,它有很多独立的功能或库,我们 ...

  10. MyBatis 一级缓存实现详解及使用注意事项

    一级缓存介绍 在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对 ...