Netty(二)—从Java NIO到Netty

从一段代码开始说起

  • Java NIO实现
public class demo{
    public void socket() {
        //  使用 socket 提供的  ServerSocketChannel建立一个 通信通道 channel
        ServerSocketChannel server = ServerSocketChannel.open();
        //  进行 IP 端口绑定,默认本地IP
        server.bind(new InetSocketAddress("port"));
        //  手动设置非阻塞
        server.configurationBlocking(false);
        //  打开选择器  可以开始接收请求了
        Selector selector = Selector.open();
        // ON_ACCEPT :设置为可接收请求状态
        server.register(this.selector, SelectionKey.OP_ACCEPT);
        //  使用轮询的方法 处理请求
        while (true) {
            /*   多路复用体现
             *       多路:select();方法可以接收多个TCP连接请求
             *       复用:复用一个主线程去处理多个请求
             */  
            //  接收所有请求  select()方法会阻塞,直到拿到一个新的key
            selector.select();
            //  拿到请求
            Set<SelectionKey> selectionKeys = this.selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            //  处理请求.....
            while (iterator.hasNext()) {
                //  每一个key代表一种状态
                SelectionKey key = iterator.next();
                //  每一个key都有一个通道去处理
                key.channel();
                //  to  do
            }
        }
    }
}

Netty的做法

  • 通过复习java NIO的实现,对NIO有了更深一层的记忆
  • Netty是基于Java的一个高性能的RPC框架
  • Netty提供了如 零拷贝、内存池、无锁串行化设计理念、Reactor线程模型 等高性能操作
  • 回归正题:Netty既然是基于Java实现的,那么利用Netty编程和使用Java实现NIO必然有异曲同工之处

Netty代码示例

在这里插入图片描述

  • 说实话,属实不知道怎么去搞懂它
  • 不过记住一件事,这里做的事情和java NIO做的事情是一样的,只不过性能更优
  • 其实从 serverBootstrap.bind() 方法也可以看出,是绑定端口的操作

从Channel()开始学起

  • Java NIO中有 ServerSocketChannel.open() 建立一个channel对象
  • 那么Netty中必然也有Channel对象,从代码中也可以窥见一二
  • Netty的 Channel.java

在这里插入图片描述

  • 部分 API
    • 连接到socket网络或者组件进行IO操作、读、写、绑定或连接
    • A channel providers a user : 用来提供Channel对象的

在这里插入图片描述

  • 通过其很多的实现类可以了解到,Netty.Channel针对不同的连接协议提供了支持
Channel的创建
  • 从代码入手,本例 完成了 对 NioServerSocketChannel的初始化
ServerBootStrap 提供了 channel()  方法  其本身是由 Netty 提供用来完成Netty客户端/服务端初始化操作的

在这里插入图片描述

channel方法  返回了  一个工厂对象

在这里插入图片描述

  • 通过跟踪代码,channel初始化操作是由 serverBootstrap.bind(port) 完成
    在这里插入图片描述
  • 交给 channelFactory 工厂来创建
    在这里插入图片描述
  • 最终通过反射进行 Channel() 对象的创建
    在这里插入图片描述
  • 随后调用 NioServerSocketChannel 的构造方法 NioServerSocketChannel 在 调用Channel()方法阶段设置
    在这里插入图片描述
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
  • 接下来就是通过父类的层层调用—直到 AbstractChannel 的构造器

在这里插入图片描述在这里插入图片描述

  • 至此,以 NioServerSocketChannel 为例的 Channel初始化就完成了
总结
  • NioServerSocketChannel 构造方法中,使用 newSocket 方法新建 一个 ServerSocketChannel
  • 需要初始化的属性在 AbstractChannel 中完成,并 为每一个channel设置了一个唯一的id
    • 在执行AcstractChannel构造器的时候同时新建了一个unsafe对象并完成了pipeline对象的实例化
  • 在代码编程方面
    • 我们可以理解到,ServerBootstrap 完成了初始化预设参数
    • 在调用 bind 方法的时候完成注册和端口绑定

Unsafe的初始化

  • 在 Channel 的创建过程中,在父类 AbstractChannel 的构造方法中调用 newUnsafe() 方法来获取一个unsafe实例
  • 了解一下 Unsafe 接口所提供的方法
interface Unsafe {
    RecvByteBufAllocator.Handle recvBufAllocHandle();
    SocketAddress localAddress();
    SocketAddress remoteAddress();
    void register(EventLoop eventLoop, ChannelPromise promise);
    void bind(SocketAddress localAddress, ChannelPromise promise);
    void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
    void disconnect(ChannelPromise promise);
    void close(ChannelPromise promise);
    void closeForcibly();
    void deregister(ChannelPromise promise);
    void beginRead();
    void write(Object msg, ChannelPromise promise);
    void flush();
    ChannelPromise voidPromise();
    ChannelOutboundBuffer outboundBuffer();
}
  • Netty提供的Unsafe类对Java底层Socket的操作进行了封装,是Netty和Java底层**”沟通“**的重要桥梁
  • Unsafe类以内部类的方式实现,同时避免了上层业务的调用

Pipeline初始化

  • 在ChannelPipline的注释中有这样一句话

在这里插入图片描述

  • 在上文Channel初始化过程中,AbstractChannel构造器中完成了对Pipline的初始化
    • 传入 Channel 对象作为初始化参数,进行绑定

在这里插入图片描述

  • 对Pipline暂时先了解到这里

关于EventLoop

  • 最开始的时候我们创建了两个NioEventLoopGroup对象

    • 通过其构造方法可以发现其构造器中可传入线程数量、线程工厂对象、线程池对象进行初始化
      在这里插入图片描述
  • 通过跟踪代码了解其构造器参数的意义

    • 首先Neyyt通过io.netty.eventLoopThreads从系统属性中获取值
    • 默认值为处理器核心数*2
      在这里插入图片描述
  • 最终会调用到 MultithreadEventExecutorGroup 的构造方法

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    ......
    children = new EventExecutor[nThreads];
    ......
    chooser = chooserFactory.newChooser(children);
    ......
}
  • 这里的判断意思是:如果nThreads是2的幂,则使用PowerOfTwoEventExecutorChooser否则GenericEventExecutorChooser
    在这里插入图片描述

  • 两种 Chooser 都重写了 next() 方法
    在这里插入图片描述

  • next()方法中的运算逻辑是数组索引循环位移

  • 当索引移动到最后要给位置,再次调用next()方法将会使索引位置重新指向0

    • 运算逻辑:每次索引自增后和数组长度取模 idx.getAndIncrement() % executors.length
    • Netty优化算法:idx.getAndIncrement() & executors.length-1
    • 注:在计算机底层&比%运算效率更高
总结
  • EventLoop其实就是创建了一个大小为nThreads类型为EventExecutor的 childern 数组,构成了一个线程池
  • 当我们实例化NioEventLoopGroup的时候,如果指定线程池大小,则 nThreads 就是指定的值,反之为处理器核心数*2

Channel注册到Seclector

  • 当Channel在 initAndRegister() 方法中注册的时候还会将初始化完成的Channel注册到NioEventloop的Selector中
//  initAndRegister()方法
ChannelFuture regFuture = config().group().register(channel);
  • 其调用关系如下
//  MultithreadEventLoopGroup.java
public ChannelFuture register(Channel channel) {
    return next().register(channel);
}
//  SingleThreadEventLoop.java
public ChannelFuture register(Channel channel) {
    return register(new DefaultChannelPromise(channel, this));
}
//   AbstractChannel.java
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    AbstractChannel.this.eventLoop = eventLoop;      
    register0(promise);
}
private void register0(ChannelPromise promise) {
    doRegister();
}
  • 到这里,实现了SocketChannel注册到EventLoop对象的selector上

在这里插入图片描述

  • 注册流程总结

在这里插入图片描述

  • 本质上所做的事情就是建channel与对应的EventLoop关联。关联完成,把Channel注册到EcentLoop对象中的selector中

如何添加Handler

  • 在开始时有这样的代码,这里体现了Netty的Handler机制
.group().channel().childHandler
  • 从官方偷来一段注释瞧瞧
    • 处理IO事件或拦截IO操作,然后交给其ChannelPipline去处理
Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in its 
{@link ChannelPipeline}.
  • 简单理解就是很强大还支持扩展。针对不同场景使用不同的Handler,先了解到这里

通过这次代码分析流程,有助于后续学习Netty

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值